defmodule NPM.Package.Repository do
@moduledoc """
Repository field parsing and URL generation from package.json.
"""
@doc """
Extracts repository info from package.json data.
"""
@spec extract(map()) :: map() | nil
def extract(%{"repository" => %{"type" => type, "url" => url} = repo}) do
%{
type: type,
url: clean_url(url),
directory: repo["directory"]
}
end
def extract(%{"repository" => url}) when is_binary(url) do
%{type: "git", url: resolve_shorthand(url), directory: nil}
end
def extract(_), do: nil
@doc """
Returns the browse URL (human-readable web URL).
"""
@spec browse_url(map()) :: String.t() | nil
def browse_url(data) do
case extract(data) do
nil -> nil
%{url: url, directory: nil} -> url
%{url: url, directory: dir} -> "#{url}/tree/HEAD/#{dir}"
end
end
@doc """
Returns the HTTPS clone URL.
"""
@spec clone_url(map()) :: String.t() | nil
def clone_url(data) do
case extract(data) do
nil -> nil
%{url: url} -> ensure_git_suffix(url)
end
end
@doc """
Detects the hosting provider.
"""
@spec provider(map()) :: atom() | nil
def provider(data) do
case extract(data) do
nil ->
nil
%{url: url} ->
cond do
String.contains?(url, "github.com") -> :github
String.contains?(url, "gitlab.com") -> :gitlab
String.contains?(url, "bitbucket.org") -> :bitbucket
true -> :other
end
end
end
@doc """
Checks if repository info is present.
"""
@spec has_repository?(map()) :: boolean()
def has_repository?(data), do: extract(data) != nil
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, "/") and not String.starts_with?(str, ".") ->
"https://github.com/#{str}"
true ->
str
end
end
defp ensure_git_suffix(url) do
if String.ends_with?(url, ".git"), do: url, else: "#{url}.git"
end
end