Skip to main content

lib/npm/dev_deps.ex

defmodule NPM.DevDeps do
  @moduledoc """
  Manages devDependencies from package.json.

  DevDependencies are only needed during development (testing, building,
  linting) and should be excluded in production installs.
  """

  @doc """
  Extracts devDependencies from package.json data.
  """
  @spec extract(map()) :: map()
  def extract(%{"devDependencies" => deps}) when is_map(deps), do: deps
  def extract(_), do: %{}

  @doc """
  Extracts production dependencies only.
  """
  @spec production_deps(map()) :: map()
  def production_deps(pkg_data) do
    pkg_data["dependencies"] || %{}
  end

  @doc """
  Returns all dependencies (production + dev).
  """
  @spec all_deps(map()) :: map()
  def all_deps(pkg_data) do
    prod = production_deps(pkg_data)
    dev = extract(pkg_data)
    Map.merge(prod, dev)
  end

  @doc """
  Checks if a package is a dev dependency.
  """
  @spec dev_dep?(String.t(), map()) :: boolean()
  def dev_dep?(name, pkg_data) do
    Map.has_key?(extract(pkg_data), name)
  end

  @doc """
  Categorizes dependencies into production and development.
  """
  @spec categorize(map()) :: %{production: map(), development: map()}
  def categorize(pkg_data) do
    %{
      production: production_deps(pkg_data),
      development: extract(pkg_data)
    }
  end

  @doc """
  Finds dev deps that are also in production deps (potential misplacement).
  """
  @spec overlapping(map()) :: [String.t()]
  def overlapping(pkg_data) do
    prod_names = MapSet.new(Map.keys(production_deps(pkg_data)))
    dev_names = Map.keys(extract(pkg_data))
    Enum.filter(dev_names, &MapSet.member?(prod_names, &1)) |> Enum.sort()
  end

  @doc """
  Returns a summary of dependency distribution.
  """
  @spec summary(map()) :: %{
          production: non_neg_integer(),
          development: non_neg_integer(),
          total: non_neg_integer()
        }
  def summary(pkg_data) do
    prod = production_deps(pkg_data) |> map_size()
    dev = extract(pkg_data) |> map_size()
    %{production: prod, development: dev, total: prod + dev}
  end
end