Generate module and optimize LiveView diffing

This commit is contained in:
Chris McCord
2022-09-15 11:58:46 -04:00
parent 2ff8bca42e
commit 46c0c7237d
857 changed files with 8823 additions and 501 deletions

View File

@ -1,31 +0,0 @@
defmodule Mix.Tasks.Heroicons do
@moduledoc """
Invokes heroicons mix utilities.
Usage:
$ mix heroicons
"""
@shortdoc "Invokes heroicons mix utilities"
use Mix.Task
@impl true
def run(args) do
{_opts, args} = OptionParser.parse!(args, strict: [])
case args do
[] -> help()
_ -> Mix.raise("Invalid arguments, expected: mix heroicons")
end
end
defp help() do
Mix.Task.run("app.start")
Mix.shell().info("Heroicons v#{Application.spec(:heroicons, :vsn)}")
Mix.shell().info("Include Heroicons as SVG-strings in your Elixir/Phoenix project!")
Mix.shell().info("\nAvailable tasks:\n")
Mix.Tasks.Help.run(["--search", "heroicons."])
end
end

View File

@ -0,0 +1,35 @@
defmodule Mix.Tasks.Heroicons.Build do
# Builds a new lib/heroicons.ex with bundled icon set.
@moduledoc false
@shortdoc false
use Mix.Task
@target_file "lib/heroicons.ex"
def run(_args) do
vsn = Mix.Tasks.Heroicons.Update.vsn()
svgs_path = Mix.Tasks.Heroicons.Update.svgs_path()
outlined = Path.wildcard(Path.join(svgs_path, "outline/**/*.svg"))
solid = Path.wildcard(Path.join(svgs_path, "solid/**/*.svg"))
mini = Path.wildcard(Path.join(svgs_path, "mini/**/*.svg"))
ordered_icons = outlined ++ solid ++ mini
icons =
Enum.group_by(ordered_icons, &function_name(&1), fn file ->
for path <- file |> File.read!() |> String.split("\n"),
path = String.trim(path),
String.starts_with?(path, "<path"),
do: path
end)
Mix.Generator.copy_template("assets/heroicons.exs", @target_file, %{icons: icons, vsn: vsn},
force: true
)
Mix.Task.run("format")
end
defp function_name(file) do
file |> Path.basename() |> Path.rootname() |> String.split("-") |> Enum.join("_")
end
end

View File

@ -1,33 +1,119 @@
defmodule Mix.Tasks.Heroicons.Update do
@moduledoc """
Update heroicons.
# Updates the icon set via download from github.
@moduledoc false
@shortdoc false
By default, it downloads the latest version but you can configure it
in your config files.
@vsn "2.0.10"
@tmp_dir_name "heroicons-elixir"
Example:
config :heroicons, :version, "#{Heroicons.latest_version()}"
Then update with
$ mix heroicons.update
"""
@shortdoc "Update heroicons assets"
# Updates the icons in the assets/icons directory
use Mix.Task
require Logger
def vsn, do: @vsn
def svgs_path, do: Path.join("assets", "icons")
@impl true
def run(args) do
{_opts, args} = OptionParser.parse!(args, strict: [])
def run(_args) do
version = @vsn
tmp_dir = Path.join(System.tmp_dir!(), @tmp_dir_name)
svgs_dir = Path.join([tmp_dir, "heroicons-#{version}", "optimized"])
case args do
[] ->
Heroicons.update()
File.rm_rf!(tmp_dir)
File.mkdir_p!(tmp_dir)
_ ->
Mix.raise("Invalid arguments, expected: mix heroicons.update")
url = "https://github.com/tailwindlabs/heroicons/archive/refs/tags/v#{version}.zip"
archive = fetch_body!(url)
case unpack_archive(".zip", archive, tmp_dir) do
:ok -> :ok
other -> raise "couldn't unpack archive: #{inspect(other)}"
end
# Copy icon styles, mini, outline and solid, to priv folder
svgs_dir
|> File.ls!()
|> Enum.each(fn size ->
case size do
"20" ->
copy_svg_files(Path.join([svgs_dir, size, "solid"]), "mini")
"24" ->
Path.join(svgs_dir, size)
|> File.ls!()
|> Enum.each(fn style -> copy_svg_files(Path.join([svgs_dir, size, style]), style) end)
_ ->
true
end
end)
end
defp copy_svg_files(src_dir, style) do
dest_dir = Path.join(svgs_path(), style)
File.rm_rf!(dest_dir)
File.mkdir_p!(dest_dir)
File.cp_r!(src_dir, dest_dir)
end
defp fetch_body!(url) do
url = String.to_charlist(url)
Logger.debug("Downloading heroicons from #{url}")
{:ok, _} = Application.ensure_all_started(:inets)
{:ok, _} = Application.ensure_all_started(:ssl)
if proxy = System.get_env("HTTP_PROXY") || System.get_env("http_proxy") do
Logger.debug("Using HTTP_PROXY: #{proxy}")
%{host: host, port: port} = URI.parse(proxy)
:httpc.set_options([{:proxy, {{String.to_charlist(host), port}, []}}])
end
if proxy = System.get_env("HTTPS_PROXY") || System.get_env("https_proxy") do
Logger.debug("Using HTTPS_PROXY: #{proxy}")
%{host: host, port: port} = URI.parse(proxy)
:httpc.set_options([{:https_proxy, {{String.to_charlist(host), port}, []}}])
end
# https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/inets
cacertfile = CAStore.file_path() |> String.to_charlist()
http_options = [
ssl: [
verify: :verify_peer,
cacertfile: cacertfile,
depth: 2,
customize_hostname_check: [
match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
],
versions: protocol_versions()
]
]
options = [body_format: :binary]
case :httpc.request(:get, {url, []}, http_options, options) do
{:ok, {{_, 200, _}, _headers, body}} ->
body
other ->
raise "couldn't fetch #{url}: #{inspect(other)}"
end
end
defp protocol_versions do
if otp_version() < 25, do: [:"tlsv1.2"], else: [:"tlsv1.2", :"tlsv1.3"]
end
defp otp_version, do: :erlang.system_info(:otp_release) |> List.to_integer()
defp unpack_archive(".zip", zip, cwd) do
with {:ok, _} <- :zip.unzip(zip, cwd: to_charlist(cwd)), do: :ok
end
defp unpack_archive(_, tar, cwd) do
:erl_tar.extract({:binary, tar}, [:compressed, cwd: to_charlist(cwd)])
end
end