Skip to main content

lib/npm/package/people.ex

defmodule NPM.Package.People do
  @moduledoc """
  Extracts and analyzes author/contributor information from packages.
  """

  @doc """
  Extracts the author from package.json data.
  """
  @spec author(map()) :: map() | nil
  def author(%{"author" => author}) when is_binary(author), do: NPM.Normalize.parse_person(author)
  def author(%{"author" => author}) when is_map(author), do: author
  def author(_), do: nil

  @doc """
  Extracts contributors list.
  """
  @spec contributors(map()) :: [map()]
  def contributors(%{"contributors" => list}) when is_list(list) do
    Enum.map(list, fn
      person when is_binary(person) -> NPM.Normalize.parse_person(person)
      person when is_map(person) -> person
    end)
  end

  def contributors(%{"maintainers" => list}) when is_list(list) do
    Enum.map(list, fn
      person when is_binary(person) -> NPM.Normalize.parse_person(person)
      person when is_map(person) -> person
    end)
  end

  def contributors(_), do: []

  @doc """
  Returns all people (author + contributors).
  """
  @spec all(map()) :: [map()]
  def all(data) do
    author_list =
      case author(data) do
        nil -> []
        a -> [a]
      end

    author_list ++ contributors(data)
  end

  @doc """
  Counts unique contributors across multiple packages.
  """
  @spec unique_authors([map()]) :: [String.t()]
  def unique_authors(packages) do
    packages
    |> Enum.flat_map(fn data ->
      case author(data) do
        %{"name" => name} -> [name]
        _ -> []
      end
    end)
    |> Enum.uniq()
    |> Enum.sort()
  end

  @doc """
  Checks if author info is present.
  """
  @spec has_author?(map()) :: boolean()
  def has_author?(data), do: author(data) != nil
end