defmodule NPM.GitInfo do @moduledoc """ Extracts git metadata for npm packages. Reads repository, bugs, and homepage URLs from package.json and generates common URLs (issues, PRs, compare, etc.). """ @doc """ Extracts git repository URL from package data. """ @spec repo_url(map()) :: String.t() | nil def repo_url(%{"repository" => %{"url" => url}}), do: clean_url(url) def repo_url(%{"repository" => url}) when is_binary(url), do: resolve_shorthand(url) def repo_url(_), do: nil @doc """ Returns the issues URL for a package. """ @spec issues_url(map()) :: String.t() | nil def issues_url(%{"bugs" => %{"url" => url}}), do: url def issues_url(%{"bugs" => url}) when is_binary(url), do: url def issues_url(data) do case github_base(data) do nil -> nil base -> "#{base}/issues" end end @doc """ Returns the homepage URL. """ @spec homepage(map()) :: String.t() | nil def homepage(%{"homepage" => url}) when is_binary(url), do: url def homepage(data), do: github_base(data) @doc """ Generates a compare URL for two versions. """ @spec compare_url(map(), String.t(), String.t()) :: String.t() | nil def compare_url(data, from_version, to_version) do case github_base(data) do nil -> nil base -> "#{base}/compare/v#{from_version}...v#{to_version}" end end @doc """ Extracts the GitHub user/repo from package data. """ @spec github_repo(map()) :: String.t() | nil def github_repo(data) do case repo_url(data) do nil -> nil url -> extract_github_path(url) end end @doc """ Checks if the package is hosted on GitHub. """ @spec github?(map()) :: boolean() def github?(data) do case repo_url(data) do nil -> false url -> String.contains?(url, "github.com") end end defp github_base(data) do case github_repo(data) do nil -> nil path -> "https://github.com/#{path}" end end defp clean_url(url) do url |> String.replace(~r/^git\+/, "") |> String.replace(~r/\.git$/, "") |> String.replace("git://", "https://") |> String.replace("ssh://git@", "https://") end defp resolve_shorthand(str) do cond do String.starts_with?(str, "github:") -> "https://github.com/#{String.trim_leading(str, "github:")}" String.contains?(str, "://") -> clean_url(str) String.contains?(str, "/") -> "https://github.com/#{str}" true -> str end end defp extract_github_path(url) do case Regex.run(~r{github\.com[/:]([^/]+/[^/.\s]+)}, url) do [_, path] -> path _ -> nil end end end