├── test
├── test_helper.exs
└── heroicons_test.exs
├── .formatter.exs
├── .gitignore
├── .github
└── workflows
│ └── ci.yml
├── LICENSE
├── lib
├── heroicons
│ └── icon.ex
└── heroicons.ex
├── README.md
├── mix.exs
├── CHANGELOG.md
└── mix.lock
/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
--------------------------------------------------------------------------------
/.formatter.exs:
--------------------------------------------------------------------------------
1 | # Used by "mix format"
2 | [
3 | import_deps: [:phoenix],
4 | plugins: [Phoenix.LiveView.HTMLFormatter],
5 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{heex,ex,exs}"]
6 | ]
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where 3rd-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Temporary files, for example, from tests.
23 | /tmp/
24 |
25 | # Ignore package tarball (built via "mix hex.build").
26 | ex_heroicons-*.tar
27 |
28 | # Ignore assets that are produced by build tools.
29 | /priv/static/assets/
30 |
31 | # Ignore digested assets cache.
32 | /priv/static/cache_manifest.json
33 |
34 | # In case you use Node.js/npm, you want to ignore these.
35 | npm-debug.log
36 | /assets/node_modules/
37 |
38 | # Ignore elixir-ls cache
39 | /.elixir_ls/
40 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 |
9 | jobs:
10 | mix_test:
11 | name: mix test (Elixir ${{matrix.elixir}} | Erlang/OTP ${{matrix.otp}})
12 | runs-on: ubuntu-latest
13 | strategy:
14 | fail-fast: false
15 | matrix:
16 | include:
17 | - elixir: 1.14.0
18 | otp: 25.0
19 | lint: true
20 | env:
21 | MIX_ENV: test
22 | steps:
23 | - uses: actions/checkout@v2.3.2
24 | - uses: erlef/setup-beam@v1
25 | with:
26 | otp-version: ${{matrix.otp}}
27 | elixir-version: ${{matrix.elixir}}
28 | - name: Install packages
29 | run: |
30 | sudo apt-get install -y -qq inotify-tools
31 | - name: Install Dependencies
32 | run: |
33 | mix local.hex --force
34 | mix local.rebar --force
35 | mix deps.get --only test
36 | - run: mix format --check-formatted
37 | if: matrix.lint
38 | - run: mix compile --warnings-as-errors
39 | if: matrix.lint
40 | - run: mix test
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Miguel Serrano
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/heroicons/icon.ex:
--------------------------------------------------------------------------------
1 | defmodule Heroicons.Icon do
2 | @moduledoc """
3 | This module defines the data structure and functions for working with icons stored as SVG files.
4 | """
5 |
6 | alias __MODULE__
7 |
8 | @doc """
9 | Defines the Heroicons.Icon struct.
10 |
11 | Its fields are:
12 |
13 | * `:type` - the type of the icon
14 |
15 | * `:name` - the name of the icon
16 |
17 | * `:file` - the binary content of the file
18 |
19 | """
20 | defstruct [:type, :name, :file]
21 |
22 | @type t :: %Icon{type: String.t(), name: String.t(), file: binary}
23 |
24 | @doc "Parses a SVG file and returns structured data"
25 | @spec parse!(String.t()) :: Icon.t()
26 | def parse!(filename) do
27 | [type, name] =
28 | filename
29 | |> Path.split()
30 | |> Enum.take(-3)
31 | |> case do
32 | ["16", "solid", name] -> ["micro", name]
33 | ["20", "solid", name] -> ["mini", name]
34 | ["24", "solid", name] -> ["solid", name]
35 | ["24", "outline", name] -> ["outline", name]
36 | end
37 |
38 | name = Path.rootname(name)
39 |
40 | file =
41 | filename
42 | |> File.read!()
43 | |> String.split("\n")
44 | |> Enum.drop(1)
45 | |> Enum.drop(-2)
46 | |> Enum.map(&String.trim/1)
47 |
48 | %__MODULE__{type: type, name: name, file: file}
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Heroicons
2 |
3 | 
4 |
5 | This package adds a convenient way of using [Heroicons](https://heroicons.com) with your Phoenix, Phoenix LiveView and Surface applications.
6 |
7 | Heroicons is "A set of 450+ free MIT-licensed high-quality SVG icons for you to use in your web projects."
8 | Created by the amazing folks at [Tailwind Labs](https://github.com/tailwindlabs).
9 |
10 | You can find the original docs [here](https://heroicons.com) and repo [here](https://github.com/tailwindlabs/heroicons).
11 |
12 | ## Installation
13 |
14 | Add `ex_heroicons` and `heroicons` to the list of dependencies in `mix.exs`:
15 |
16 | ```elixir
17 | def deps do
18 | [
19 | {:ex_heroicons, "~> 3.1.0"},
20 | {:heroicons,
21 | github: "tailwindlabs/heroicons",
22 | tag: "v2.1.5",
23 | sparse: "optimized",
24 | app: false,
25 | compile: false,
26 | depth: 1}
27 | ]
28 | end
29 | ```
30 |
31 | Then run `mix deps.get`.
32 |
33 | ## Usage
34 |
35 | ```elixir
36 |
37 | ```
38 |
39 | ## Config
40 |
41 | Defaults can be set in the `Heroicons` application configuration.
42 |
43 | ```elixir
44 | config :ex_heroicons, type: "outline"
45 | ```
46 |
47 | ## License
48 |
49 | MIT. See [LICENSE](https://github.com/miguel-s/ex_heroicons/blob/master/LICENSE) for more details.
50 |
--------------------------------------------------------------------------------
/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Heroicons.MixProject do
2 | use Mix.Project
3 |
4 | @version "3.1.0"
5 |
6 | def project do
7 | [
8 | app: :ex_heroicons,
9 | version: @version,
10 | elixir: "~> 1.14",
11 | elixirc_paths: elixirc_paths(Mix.env()),
12 | start_permanent: Mix.env() == :prod,
13 | deps: deps(),
14 | docs: docs(),
15 | description: description(),
16 | package: package(),
17 | source_url: "https://github.com/miguel-s/ex_heroicons"
18 | ]
19 | end
20 |
21 | def application do
22 | [
23 | extra_applications: [:logger]
24 | ]
25 | end
26 |
27 | defp elixirc_paths(:test), do: ["lib", "test/support"]
28 | defp elixirc_paths(_), do: ["lib"]
29 |
30 | defp deps do
31 | [
32 | {:phoenix_live_view, "~> 0.20 or ~> 1.0"},
33 | {:ex_doc, "~> 0.35", only: :dev, runtime: false},
34 | {:floki, ">= 0.36.0", only: :test},
35 | {:heroicons,
36 | github: "tailwindlabs/heroicons",
37 | tag: "v2.1.5",
38 | sparse: "optimized",
39 | app: false,
40 | compile: false,
41 | depth: 1,
42 | only: :test}
43 | ]
44 | end
45 |
46 | defp docs do
47 | [
48 | main: "Heroicons",
49 | source_ref: "v#{@version}",
50 | source_url: "https://github.com/miguel-s/ex_heroicons",
51 | extras: ["README.md"]
52 | ]
53 | end
54 |
55 | defp description() do
56 | """
57 | This package adds a convenient way of using Heroicons with your Phoenix, Phoenix LiveView and Surface applications.
58 |
59 | Heroicons is "A set of 450+ free MIT-licensed high-quality SVG icons for you to use in your web projects."
60 | Created by the amazing folks at Tailwind Labs.
61 | """
62 | end
63 |
64 | defp package do
65 | %{
66 | files: ~w(lib .formatter.exs mix.exs README* LICENSE* CHANGELOG*),
67 | licenses: ["MIT"],
68 | links: %{"GitHub" => "https://github.com/miguel-s/ex_heroicons"}
69 | }
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/test/heroicons_test.exs:
--------------------------------------------------------------------------------
1 | defmodule HeroiconsTest do
2 | use ExUnit.Case, async: true
3 | import Phoenix.Component
4 | import Phoenix.LiveViewTest
5 | doctest Heroicons
6 |
7 | test "renders icon" do
8 | assigns = %{}
9 | html = rendered_to_string(~H[])
10 |
11 | assert html =~
12 | ~s(\n)
13 | end
14 |
15 | test "renders icon with default type" do
16 | assigns = %{}
17 | html = rendered_to_string(~H[])
18 | assert html =~ ~s(heroicons-outline-academic-cap)
19 | end
20 |
21 | test "renders icon with explicit type" do
22 | assigns = %{}
23 | html = rendered_to_string(~H[])
24 | assert html =~ ~s(heroicons-solid-academic-cap)
25 | end
26 |
27 | test "renders icon with class" do
28 | assigns = %{}
29 | html = rendered_to_string(~H[])
30 | assert html =~ ~s(class="h-4 w-4")
31 | end
32 |
33 | test "renders icon with global attributes" do
34 | assigns = %{}
35 | html = rendered_to_string(~H[])
36 | assert html =~ ~s(aria-hidden="true")
37 | end
38 |
39 | test "raises when icon name is not found" do
40 | assigns = %{}
41 |
42 | assert_raise ArgumentError,
43 | "expected icon name to be one of #{inspect(Heroicons.names())}, got: nil",
44 | fn ->
45 | rendered_to_string(~H[])
46 | end
47 | end
48 |
49 | test "raises when icon name is invalid" do
50 | assigns = %{}
51 |
52 | assert_raise ArgumentError,
53 | "expected icon name to be one of #{inspect(Heroicons.names())}, got: \"invalid\"",
54 | fn ->
55 | rendered_to_string(~H[])
56 | end
57 | end
58 |
59 | test "raises when icon type is not found" do
60 | assigns = %{}
61 |
62 | assert_raise ArgumentError,
63 | "expected icon type to be one of #{inspect(Heroicons.types())}, got: nil",
64 | fn ->
65 | rendered_to_string(~H[])
66 | end
67 | end
68 |
69 | test "raises when icon type is invalid" do
70 | assigns = %{}
71 |
72 | assert_raise ArgumentError,
73 | "expected icon type to be one of #{inspect(Heroicons.types())}, got: \"invalid\"",
74 | fn ->
75 | rendered_to_string(~H[])
76 | end
77 | end
78 | end
79 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## v3.1.0 (2024-12-12)
2 |
3 | - Added support for Phoenix LiveView v1.0
4 |
5 | ## v3.0.0 (2024-10-18)
6 |
7 | - Upgraded to Heroicons v2.1.5 and include new micro icons
8 | - Upgraded to Surface v0.11
9 | - Removed Phoenix HTML dependency
10 | - Removed dependency on npm and node_modules
11 |
12 | ### Breaking changes
13 |
14 | - Removed `Heroicons.LiveView.icon` and `Heroicons.Surface.Icon` in favor of `Heroicons.icon`
15 |
16 | ### Migrating to v3
17 |
18 | Update the `ex_heroicons` dependency
19 |
20 | ```elixir
21 | {:ex_heroicons, "~> 3.1.0"},
22 | ```
23 |
24 | Add the `heroicons` dependency
25 |
26 | ```elixir
27 | {:heroicons,
28 | github: "tailwindlabs/heroicons",
29 | tag: "v2.1.5",
30 | sparse: "optimized",
31 | app: false,
32 | compile: false,
33 | depth: 1}
34 | ```
35 |
36 | Replace the LiveView and Surface components with the new functional component
37 |
38 | ```diff
39 | -
40 | +
41 | ```
42 |
43 | ## v2.0.0 (2022-09-13)
44 |
45 | - Upgraded to Heroicons v2.0.11
46 | - You can find the official Heroicons upgrade guide [here](https://github.com/tailwindlabs/heroicons/releases/tag/v2.0.0)
47 |
48 | ## v1.0.0 (2022-09-01)
49 |
50 | No changes
51 |
52 | ## v0.6.1 (2022-08-31)
53 |
54 | - Upgraded to Heroicons v1.0.6
55 |
56 | ## v0.6.0 (2022-01-13)
57 |
58 | - Upgraded to Phoenix HTML v3.2
59 | - Upgraded to Phoenix LiveView v0.17
60 | - Upgraded to Surface v0.7
61 |
62 | ## v0.5.1 (2021-11-16)
63 |
64 | - Added helper function `types/0` for listing available icon types
65 | - Added helper function `names/0` for listing available icon names
66 |
67 | ## v0.5.0 (2021-10-21)
68 |
69 | - Upgraded to Surface v0.6.0
70 | - Removed support for Phoenix HTML v2
71 |
72 | ## v0.4.2 (2021-09-25)
73 |
74 | - Upgraded to Heroicons v1.0.4
75 |
76 | ## v0.4.1 (2021-09-07)
77 |
78 | - Removed `IO.inspect`
79 |
80 | ## v0.4.0 (2021-09-05)
81 |
82 | - Upgraded to Phoenix HTML v3.0
83 | - Upgraded to Phoenix LiveView v0.16
84 | - Upgraded to Surface v0.5.2
85 |
86 | #### Breaking Changes
87 |
88 | - Renamed `Heroicons.Components.Icon` to `Heroicons.Surface.Icon`
89 |
90 | #### Enhancements
91 |
92 | - Added support for heex templates with a new Phoenix LiveView component `Heroicons.LiveView.icon`
93 |
94 | ## v0.3.1 (2021-09-03)
95 |
96 | - Added support for Phoenix HTML v0.3
97 |
98 | ## v0.3.0 (2021-06-18)
99 |
100 | #### Breaking Changes
101 |
102 | - Upgraded to Surface v0.5.0
103 |
104 | ## v0.2.0 (2021-06-07)
105 |
106 | #### Breaking Changes
107 |
108 | - Replaced `icon/3` with `icon/2`, the `type` argument should be passed as an option instead
109 |
110 | #### Enhancements
111 |
112 | - Added `:type` to config for setting the default icon type
113 |
114 | ## v0.1.4 (2021-05-29)
115 |
116 | - All options passed to `icon/3` will be added to the SVG tag as HTML attributes
117 | - Added `opts` prop to the Surface Icon component, which will be added to the SVG tag as HTML attributes
118 |
119 | ## v0.1.3 (2021-05-24)
120 |
121 | - Improved documentation
122 |
123 | ## v0.1.2 (2021-05-22)
124 |
125 | - Fixed Surface Icon component error when not passing the class prop
126 |
127 | ## v0.1.1 (2021-05-19)
128 |
129 | - Added Surface Icon component
130 |
131 | ## v0.1.0 (2021-05-19)
132 |
133 | - Initial release
134 |
--------------------------------------------------------------------------------
/lib/heroicons.ex:
--------------------------------------------------------------------------------
1 | defmodule Heroicons do
2 | @moduledoc """
3 | This package adds a convenient way of using [Heroicons](https://heroicons.com) with your Phoenix, Phoenix LiveView and Surface applications.
4 |
5 | Heroicons is "A set of 450+ free MIT-licensed high-quality SVG icons for you to use in your web projects."
6 | Created by the amazing folks at [Tailwind Labs](https://github.com/tailwindlabs).
7 |
8 | You can find the original docs [here](https://heroicons.com) and repo [here](https://github.com/tailwindlabs/heroicons).
9 |
10 | ## Installation
11 |
12 | Add `ex_heroicons` and `heroicons` to the list of dependencies in `mix.exs`:
13 |
14 | def deps do
15 | [
16 | {:ex_heroicons, "~> 3.1.0"},
17 | {:heroicons,
18 | github: "tailwindlabs/heroicons",
19 | tag: "v2.1.5",
20 | sparse: "optimized",
21 | app: false,
22 | compile: false,
23 | depth: 1}
24 | ]
25 | end
26 |
27 | Then run `mix deps.get`.
28 |
29 | ## Usage
30 |
31 |
32 |
33 | ## Config
34 |
35 | Defaults can be set in the `Heroicons` application configuration.
36 |
37 | config :ex_heroicons, type: "outline"
38 | """
39 |
40 | use Phoenix.Component
41 | alias Heroicons.Icon
42 |
43 | heroicons_path =
44 | if File.exists?("deps/heroicons") do
45 | "deps/heroicons"
46 | else
47 | "../heroicons"
48 | end
49 |
50 | unless File.exists?(heroicons_path) do
51 | raise """
52 | Heroicons not found, please add the `heroicons` dependency to your project.
53 |
54 | Add `heroicons` to the list of dependencies in `mix.exs`:
55 |
56 | def deps do
57 | [
58 | ...,
59 | {:heroicons,
60 | github: "tailwindlabs/heroicons",
61 | tag: "v2.1.5",
62 | sparse: "optimized",
63 | app: false,
64 | compile: false,
65 | depth: 1}
66 | ]
67 | end
68 | """
69 | end
70 |
71 | icon_paths =
72 | heroicons_path
73 | |> Path.join("optimized/**/*.svg")
74 | |> Path.wildcard()
75 |
76 | icons =
77 | for icon_path <- icon_paths do
78 | @external_resource Path.relative_to_cwd(icon_path)
79 | Icon.parse!(icon_path)
80 | end
81 |
82 | types = icons |> Enum.map(& &1.type) |> Enum.uniq()
83 | names = icons |> Enum.map(& &1.name) |> Enum.uniq()
84 |
85 | default_type =
86 | case Application.compile_env(:ex_heroicons, :type) do
87 | nil ->
88 | "outline"
89 |
90 | type when is_binary(type) ->
91 | if type in types do
92 | type
93 | else
94 | raise ArgumentError,
95 | "expected default type to be one of #{inspect(types)}, got: #{inspect(type)}"
96 | end
97 |
98 | type ->
99 | raise ArgumentError,
100 | "expected default type to be one of #{inspect(types)}, got: #{inspect(type)}"
101 | end
102 |
103 | @names names
104 | def names, do: @names
105 |
106 | @types types
107 | def types, do: @types
108 |
109 | attr :name, :string, values: @names, required: true, doc: "the name of the icon"
110 | attr :type, :string, values: @types, default: default_type, doc: "the type of the icon"
111 | attr :class, :string, default: nil, doc: "the css classes to add to the svg container"
112 | attr :rest, :global, doc: "the arbitrary HTML attributes to add to the svg container"
113 |
114 | def icon(assigns) do
115 | name = assigns[:name]
116 |
117 | if name == nil or name not in @names do
118 | raise ArgumentError,
119 | "expected icon name to be one of #{inspect(unquote(@names))}, got: #{inspect(name)}"
120 | end
121 |
122 | type = assigns[:type]
123 |
124 | if type == nil or type not in @types do
125 | raise ArgumentError,
126 | "expected icon type to be one of #{inspect(unquote(@types))}, got: #{inspect(type)}"
127 | end
128 |
129 | ~H"""
130 |
131 | <.svg_container type={@type} class={@class} {@rest}>
132 | <%= {:safe, svg_body(@name, @type)} %>
133 |
134 | """
135 | end
136 |
137 | attr :type, :string, values: @types, default: default_type, doc: "the type of the icon"
138 | attr :class, :string, default: nil, doc: "the css classes to add to the svg container"
139 | attr :rest, :global, doc: "the arbitrary HTML attributes to add to the svg container"
140 |
141 | slot :inner_block, required: true, doc: "the svg paths to render inside the svg container"
142 |
143 | defp svg_container(assigns) do
144 | ~H"""
145 |
158 | """
159 | end
160 |
161 | defp svg_viewbox(type) do
162 | case type do
163 | "micro" -> "0 0 16 16"
164 | "mini" -> "0 0 20 20"
165 | "solid" -> "0 0 24 24"
166 | "outline" -> "0 0 24 24"
167 | end
168 | end
169 |
170 | for %Icon{name: name, type: type, file: file} <- icons do
171 | defp svg_body(unquote(name), unquote(type)) do
172 | unquote(file)
173 | end
174 | end
175 | end
176 |
--------------------------------------------------------------------------------
/mix.lock:
--------------------------------------------------------------------------------
1 | %{
2 | "castore": {:hex, :castore, "1.0.10", "43bbeeac820f16c89f79721af1b3e092399b3a1ecc8df1a472738fd853574911", [:mix], [], "hexpm", "1b0b7ea14d889d9ea21202c43a4fa015eb913021cb535e8ed91946f4b77a8848"},
3 | "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
4 | "ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"},
5 | "floki": {:hex, :floki, "0.37.0", "b83e0280bbc6372f2a403b2848013650b16640cd2470aea6701f0632223d719e", [:mix], [], "hexpm", "516a0c15a69f78c47dc8e0b9b3724b29608aa6619379f91b1ffa47109b5d0dd3"},
6 | "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "ad0ad1f6d51bd64dcd67e363d2b2833a8de25154", [tag: "v2.1.5", sparse: "optimized", depth: 1]},
7 | "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
8 | "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
9 | "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
10 | "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
11 | "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"},
12 | "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
13 | "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
14 | "phoenix": {:hex, :phoenix, "1.7.18", "5310c21443514be44ed93c422e15870aef254cf1b3619e4f91538e7529d2b2e4", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "1797fcc82108442a66f2c77a643a62980f342bfeb63d6c9a515ab8294870004e"},
15 | "phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"},
16 | "phoenix_live_view": {:hex, :phoenix_live_view, "1.0.0", "3a10dfce8f87b2ad4dc65de0732fc2a11e670b2779a19e8d3281f4619a85bce4", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "254caef0028765965ca6bd104cc7d68dcc7d57cc42912bef92f6b03047251d99"},
17 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
18 | "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
19 | "phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"},
20 | "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
21 | "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"},
22 | "sourceror": {:hex, :sourceror, "1.0.3", "111711c147f4f1414c07a67b45ad0064a7a41569037355407eda635649507f1d", [:mix], [], "hexpm", "56c21ef146c00b51bc3bb78d1f047cb732d193256a7c4ba91eaf828d3ae826af"},
23 | "surface": {:hex, :surface, "0.11.5", "b5d7a289f78429162f63de0ab925b047a9b85e56f84e1e6edc1b8e2376a00bd1", [:mix], [{:phoenix_html, "~> 3.3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19.0 or ~> 0.20.10", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "288c640791c91dbd4568fc362d1ff425bfc6cc2af4cac44d1e7bcf4cea20edd0"},
24 | "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
25 | "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
26 | "websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"},
27 | }
28 |
--------------------------------------------------------------------------------