Skip to main content

lib/npm/corepack.ex

defmodule NPM.Corepack do
  @moduledoc """
  Parses corepack/packageManager field from package.json.

  The `packageManager` field specifies which package manager and version
  to use (e.g., `"npm@10.2.0"`, `"pnpm@8.10.0"`).
  """

  @doc """
  Extracts the packageManager field.
  """
  @spec get(map()) :: String.t() | nil
  def get(%{"packageManager" => pm}) when is_binary(pm), do: pm
  def get(_), do: nil

  @doc """
  Parses the package manager name from the field.
  """
  @spec manager_name(map()) :: String.t() | nil
  def manager_name(data) do
    case get(data) do
      nil -> nil
      pm -> pm |> String.split("@", parts: 2) |> hd()
    end
  end

  @doc """
  Parses the package manager version.
  """
  @spec manager_version(map()) :: String.t() | nil
  def manager_version(data) do
    case get(data) do
      nil ->
        nil

      pm ->
        case String.split(pm, "@", parts: 2) do
          [_, version] -> strip_hash(version)
          _ -> nil
        end
    end
  end

  @doc """
  Checks if the project uses npm.
  """
  @spec npm?(map()) :: boolean()
  def npm?(data), do: manager_name(data) == "npm"

  @doc """
  Checks if the project uses pnpm.
  """
  @spec pnpm?(map()) :: boolean()
  def pnpm?(data), do: manager_name(data) == "pnpm"

  @doc """
  Checks if the project uses yarn.
  """
  @spec yarn?(map()) :: boolean()
  def yarn?(data), do: manager_name(data) == "yarn"

  @doc """
  Checks if corepack is configured.
  """
  @spec configured?(map()) :: boolean()
  def configured?(data), do: get(data) != nil

  @doc """
  Formats for display.
  """
  @spec format(map()) :: String.t()
  def format(data) do
    case get(data) do
      nil -> "No package manager configured"
      pm -> "Package manager: #{pm}"
    end
  end

  defp strip_hash(version) do
    case String.split(version, "+", parts: 2) do
      [ver | _] -> ver
      _ -> version
    end
  end
end