defmodule NPM.Migration do @moduledoc """ Provides migration guidance between npm and lockfile versions. """ @doc """ Determines required lockfile version for an npm version. """ @spec lockfile_version(String.t()) :: non_neg_integer() def lockfile_version(npm_version) do case major_version(npm_version) do n when n >= 9 -> 3 n when n >= 7 -> 2 _ -> 1 end end @doc """ Checks if a lockfile migration is needed. """ @spec needs_migration?(non_neg_integer(), String.t()) :: boolean() def needs_migration?(current_lockfile_version, target_npm_version) do lockfile_version(target_npm_version) != current_lockfile_version end @doc """ Returns breaking changes between npm major versions. """ @spec breaking_changes(non_neg_integer(), non_neg_integer()) :: [String.t()] def breaking_changes(from, to) when from < to do from..to |> Enum.flat_map(&changes_for_version/1) end def breaking_changes(_, _), do: [] @doc """ Returns migration steps. """ @spec steps(non_neg_integer(), non_neg_integer()) :: [String.t()] def steps(from_lockfile, to_lockfile) when from_lockfile < to_lockfile do base = ["Delete node_modules/", "Delete package-lock.json", "Run npm install"] if to_lockfile >= 3 do base ++ ["Verify package-lock.json uses lockfileVersion #{to_lockfile}"] else base end end def steps(from, to) when from == to, do: ["No migration needed."] def steps(_, _), do: ["Downgrade not recommended."] @doc """ Formats migration guide. """ @spec format_guide(non_neg_integer(), non_neg_integer()) :: String.t() def format_guide(from, to) do step_list = steps(from, to) |> Enum.with_index(1) |> Enum.map_join("\n", fn {s, i} -> "#{i}. #{s}" end) "Migration from lockfileVersion #{from} to #{to}:\n#{step_list}" end defp major_version(version) do case String.split(version, ".", parts: 2) do [major | _] -> String.to_integer(major) _ -> 0 end end defp changes_for_version(7) do ["package-lock.json v2 format", "Automatic peer dependency installation"] end defp changes_for_version(9) do ["package-lock.json v3 format", "Dropped support for Node.js 14"] end defp changes_for_version(10) do ["package-lock.json v3 format", "Dropped support for Node.js 16"] end defp changes_for_version(_), do: [] end