mix.exs

# credo:disable-for-this-file Credo.Check.Refactor.ModuleDependencies
defmodule Enumex.Mixfile do
  use Mix.Project

  alias Enumex.Dynamic
  alias Enumex.Static

  @name :enumex
  @gitlab_url "https://gitlab.com/ramurix-software/#{@name}"
  @version "1.0.0"

  @deps [
    # Code checking
    {:credo, "~> 1.7", only: :test, runtime: false},
    {:dialyxir, "~> 1.4", only: :test, runtime: false},
    # Documentation
    {:ex_doc, "~> 0.38", only: :dev, runtime: false},
    # Tests
    {:excoveralls, "~> 0.18", only: :test},
    {:ecto_sql, "~> 3.13"},
    {:meck, "~> 0.9", only: :test},
    {:postgrex, "~> 0.20"},
    # optional support
    {:absinthe, "~> 1.7", optional: true},
    # for tasks:
    {:owl, "~> 0.12"}
  ]

  @description "A comprehensive library for working with both static and dynamic enum structures in Elixir."

  @groups_for_modules [
    Main: [Enumex, Enumex.Component, Enumex.Value],
    Dynamic: [Dynamic, Dynamic.Migration],
    "Dynamic Components": [
      Dynamic.Components.Context,
      Dynamic.Components.Convert,
      Dynamic.Components.EctoChangeset,
      Dynamic.Components.EctoSchema,
      Dynamic.Components.Typespecs
    ],
    Static: [Static, Static.Adapter, Static.Adapters.Postgres, Static.Migration],
    "Static Components": [
      Static.Components.AbsinthePhase,
      Static.Components.Constant,
      Static.Components.EctoQuery,
      Static.Components.EctoType,
      Static.Components.Guards,
      Static.Components.Index,
      Static.Components.List,
      Static.Components.Sort,
      Static.Components.Typespecs
    ]
  ]

  @package [
    files: ~w(.formatter.exs lib LICENSE mix.exs),
    licenses: ["Apache-2.0"],
    links: %{"GitLab" => @gitlab_url},
    maintainers: ["Tomasz Marek Sulima"]
  ]

  @spec application :: [{:extra_applications, [Application.app()]}]
  def application, do: [extra_applications: [:logger]]

  @typep dep ::
           {Application.app(), Version.requirement()}
           | {Application.app(), Version.requirement(), keyword(atom() | boolean())}

  @typep doc ::
           {:api_reference, boolean()}
           | {:authors, [String.t()]}
           | {:extra_section, String.t()}
           | {:extras, keyword([{:filename, String.t()} | {:title, String.t()}])}
           | {:groups_for_docs, []}
           | {:groups_for_modules, []}
           | {:main, String.t()}
           | {:source_ref, String.t()}
           | {:source_url, String.t()}

  @typep package_item ::
           {:files, [Path.t()]}
           | {:licenses, [String.t()]}
           | {:links, %{String.t() => String.t()}}
           | {:maintainers, [String.t()]}

  @spec project :: [
          {:app, unquote(@name)}
          | {:deps, [dep()]}
          | {:description, String.t()}
          | {:dialyzer, [{:plt_add_apps, [Application.app()]}]}
          | {:docs, [doc()]}
          | {:elixir, Version.requirement()}
          | {:elixirc_paths, [Path.t()]}
          | {:name, String.t()}
          | {:package, [package_item()]}
          | {:start_permanent, boolean()}
          | {:test_coverage, [{:tool, module()}]}
          | {:test_paths, [Path.t()]}
          | {:version, Version.version()}
        ]

  @spec cli :: [{:preferred_envs, [{Application.app(), :test}]}]
  def cli, do: [preferred_envs: ["coveralls.html": :test, credo: :test, dialyzer: :test]]

  def project do
    [
      app: @name,
      deps: @deps,
      description: @description,
      dialyzer: [plt_add_apps: ~w[absinthe ecto ecto_sql eex ex_unit mix postgrex]a],
      docs: [
        api_reference: false,
        authors: ["Tomasz Marek Sulima"],
        before_closing_body_tag: &before_closing_body_tag/1,
        extra_section: "Guides",
        extras: [
          "README.md": [filename: "introduction", title: "Introduction"],
          "guides/http-status-codes.md": [filename: "http-status-codes", title: "HTTP Status Codes"],
          "guides/user-roles.md": [filename: "user-roles", title: "User Roles"]
        ],
        groups_for_docs: [
          "Struct fields": &(&1[:section] == :field),
          Guards: &(&1[:section] == :guard),
          Main: &(&1[:section] == :main)
        ],
        groups_for_modules: @groups_for_modules,
        main: "introduction",
        source_ref: "v#{@version}",
        source_url: @gitlab_url
      ],
      elixir: "~> 1.18",
      elixirc_paths: ["lib"],
      name: "Enumex",
      package: [{:exclude_patterns, [~r[.*_test.exs], ~r[lib/test_helper.exs]]} | @package],
      start_permanent: Mix.env() == :prod,
      test_coverage: [tool: ExCoveralls],
      test_paths: ["lib"],
      version: @version
    ]
  end

  @spec before_closing_body_tag(:epub | :html) :: String.t()
  defp before_closing_body_tag(:epub), do: ""

  defp before_closing_body_tag(:html) do
    """
    <script defer src="https://cdn.jsdelivr.net/npm/mermaid@10.2.3/dist/mermaid.min.js"></script>
    <script>
      let initialized = false;

      window.addEventListener("exdoc:loaded", () => {
        if (!initialized) {
          mermaid.initialize({
            securityLevel: 'antiscript',
            startOnLoad: false,
            theme: document.body.className.includes("dark") ? "dark" : "default"
          });
          initialized = true;
        }

        let id = 0;
        for (const codeEl of document.querySelectorAll("pre code.mermaid")) {
          const preEl = codeEl.parentElement;
          const graphDefinition = codeEl.textContent;
          const graphEl = document.createElement("div");
          const graphId = "mermaid-graph-" + id++;
          mermaid.render(graphId, graphDefinition).then(({svg, bindFunctions}) => {
            graphEl.innerHTML = svg;
            bindFunctions?.(graphEl);
            preEl.insertAdjacentElement("afterend", graphEl);
            preEl.remove();
          });
        }
      });
    </script>
    """
  end
end