defmodule NPM.DepsOutput do
@moduledoc """
Formats installed npm packages for display, similar to `mix deps`.
"""
@doc """
Formats a lockfile as a `mix deps`-style listing.
Each entry shows: `* name version (npm registry)`
with locked version and status on the next line.
"""
@spec format_lockfile(map()) :: String.t()
def format_lockfile(lockfile) when map_size(lockfile) == 0, do: "No npm dependencies installed."
def format_lockfile(lockfile) do
lockfile
|> Enum.sort_by(&elem(&1, 0))
|> Enum.map_join("\n", &format_entry/1)
end
@doc """
Formats a lockfile diff (added, removed, updated packages).
"""
@spec format_diff(map(), map()) :: String.t()
def format_diff(old, new) do
old_keys = Map.keys(old) |> MapSet.new()
new_keys = Map.keys(new) |> MapSet.new()
added = MapSet.difference(new_keys, old_keys) |> MapSet.to_list() |> Enum.sort()
removed = MapSet.difference(old_keys, new_keys) |> MapSet.to_list() |> Enum.sort()
updated =
MapSet.intersection(old_keys, new_keys)
|> Enum.filter(fn k -> old[k].version != new[k].version end)
|> Enum.sort()
lines =
Enum.map(added, fn n -> " + #{n} #{new[n].version}" end) ++
Enum.map(updated, fn n -> " ↑ #{n} #{old[n].version} → #{new[n].version}" end) ++
Enum.map(removed, fn n -> " - #{n} #{old[n].version}" end)
case lines do
[] -> ""
_ -> Enum.join(lines, "\n")
end
end
@doc """
Formats install summary with timing.
"""
@spec format_summary(non_neg_integer(), non_neg_integer()) :: String.t()
def format_summary(count, ms) do
pkg = if count == 1, do: "package", else: "packages"
"Installed #{count} #{pkg} in #{ms}ms"
end
@doc """
Prints the lockfile listing to the Mix shell.
"""
@spec print(map()) :: :ok
def print(lockfile) do
Mix.shell().info(format_lockfile(lockfile))
end
defp format_entry({name, entry}) do
integrity = format_integrity(Map.get(entry, :integrity, ""))
"* #{name} #{entry.version} (npm registry)\n locked at #{entry.version} #{integrity}\n ok"
end
defp format_integrity(""), do: ""
defp format_integrity(hash) when byte_size(hash) > 16, do: String.slice(hash, 0, 8)
defp format_integrity(hash), do: hash
end