Created
June 1, 2021 05:34
-
-
Save egze/eff41ae3592532fe6002b0a5aae3d085 to your computer and use it in GitHub Desktop.
Ensure Hex ans Npm have the same version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule MyApp.YarnChecker do | |
use Credo.Check, | |
run_on_all: true, | |
category: :consistency, | |
base_priority: :high, | |
param_defaults: [ | |
package_json: "assets/package.json", | |
yarn_lock: "assets/yarn.lock", | |
packages: ["phoenix", "phoenix_html", "phoenix_live_view"] | |
], | |
explanations: [ | |
check: """ | |
This check ensures that npm packages are the same verion like their hex.pm counterparts. | |
For example phoenix, phoenix_live_view, phoenix_html can be installed from npm, instead of hex, but | |
it's then up to the developer to make sure that their versions match and this something easy to miss. | |
""" | |
] | |
def run_on_all_source_files(exec, _source_files, params) do | |
packages = Keyword.get(params, :packages) | |
yarn_lock_path = Keyword.get(params, :yarn_lock) | |
package_json_path = Keyword.get(params, :package_json) | |
package_json_file = %SourceFile{filename: package_json_path} | |
issues = | |
yarn_lock_path | |
|> parse_yarn_lock() | |
|> get_npm_packages(packages) | |
|> ensure_npm_matches_hex(packages, package_json_file, params) | |
Credo.Execution.ExecutionIssues.append(exec, issues) | |
end | |
defp parse_yarn_lock(path) do | |
{:ok, parsed_yarn_lock} = | |
path | |
|> File.read!() | |
|> YarnParser.decode() | |
parsed_yarn_lock | |
end | |
defp get_npm_packages(parsed_yarn_lock, packages_to_find) do | |
packages = packages_to_find |> Enum.map(&(&1 <> "@")) | |
parsed_yarn_lock.dependencies | |
|> Enum.reduce(%{}, fn {name_with_version, %{"version" => version}}, acc -> | |
cond do | |
String.starts_with?(name_with_version, packages) -> | |
[name | _] = String.split(name_with_version, "@") | |
Map.put(acc, name, version) | |
true -> | |
acc | |
end | |
end) | |
end | |
defp ensure_npm_matches_hex(npm_packages, packages_to_compare, package_json_file, params) do | |
parsed_mix_lock = parse_mix_lock() | |
issue_meta = IssueMeta.for(package_json_file, params) | |
packages_map = | |
packages_to_compare | |
|> Enum.reduce(%{}, fn package, acc -> | |
hex_version = Map.get(parsed_mix_lock, package) | |
npm_version = Map.get(npm_packages, package) | |
Map.put(acc, package, %{hex: hex_version, npm: npm_version}) | |
end) | |
packages_map | |
|> Enum.reduce([], fn {package, versions}, acc -> | |
if versions.hex != versions.npm do | |
[issue_for(issue_meta, package, versions) | acc] | |
else | |
acc | |
end | |
end) | |
end | |
defp issue_for(issue_meta, package, versions) do | |
format_issue( | |
issue_meta, | |
message: "#{package} versions don't match. hex: #{versions.hex}, npm: #{versions.npm}" | |
) | |
end | |
defp parse_mix_lock do | |
"./mix.lock" | |
|> File.read!() | |
|> Code.string_to_quoted(warn_on_unnecessary_quotes: false) | |
|> extract_deps_with_versions() | |
end | |
defp extract_deps_with_versions(mix_lock_ast) do | |
{:ok, {_, _, deps_ast}} = mix_lock_ast | |
deps_ast | |
|> Enum.reduce(%{}, fn {name_atom, dep_tuple}, acc -> | |
{_, _, [_, _, version | _]} = dep_tuple | |
name_str = to_string(name_atom) | |
Map.put(acc, name_str, version) | |
end) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment