Current section
Files
Jump to
Current section
Files
lib/v1/encoder.ex
defmodule YarnParser.V1.Encoder do
@moduledoc false
def encode(map, opts \\ []) do
version = Keyword.get(opts, :version, 1)
no_header = Keyword.get(opts, :no_header, false)
if no_header do
do_encode(map, top_level: true)
else
"""
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v#{version}
#{do_encode(map, top_level: true)}
"""
end
end
defp do_encode(map, opts) do
indent = Keyword.get(opts, :indent, "")
top_level = Keyword.get(opts, :top_level, false)
keys = Map.keys(map) |> sort_keys()
{_, lines} = Enum.reduce(keys, {[], []}, fn
"comments", {pk, acc} -> {pk, acc}
key, {processed_keys, acc} ->
val = map[key]
cond do
is_map(val) and key not in processed_keys ->
matched_keys = Enum.filter(keys, fn x -> map[x] == val end)
keys_line =
matched_keys
|> Enum.sort()
|> Enum.map(&maybe_wrap/1)
|> Enum.join(", ")
processed_keys = processed_keys ++ matched_keys
trailing_str = if top_level, do: "\n", else: ""
encoded_content = do_encode(val, indent: indent <> " ")
line = "#{keys_line}:\n#{encoded_content}#{trailing_str}"
{processed_keys, acc ++ [line]}
is_map(val) ->
{processed_keys, acc}
true ->
{processed_keys, acc ++ ["#{key} #{maybe_wrap(val)}"]}
end
end)
indent <> Enum.join(lines, "\n#{indent}")
end
defp maybe_wrap(key) when is_atom(key) do
Atom.to_string(key)
end
defp maybe_wrap(key) when is_binary(key) do
if should_wrap_key(key) do
~s("#{key}")
else
key
end
end
defp maybe_wrap(key), do: "#{key}"
defp should_wrap_key(key)do
String.starts_with?(key, "true")
|| String.starts_with?(key, "false")
|| Regex.match?(~r/[:\s\n\\",\[\]]/, key)
|| Regex.match?(~r/^[0-9]/, key)
|| !Regex.match?(~r/^[a-zA-Z]/, key)
end
@priorities [
"name",
"version",
"uid",
"resolved",
"integrity",
"registry",
"dependencies"
]
|> Enum.with_index(1)
|> Enum.into(%{})
defp sort_keys(keys) do
Enum.sort(keys, fn a, b ->
if @priorities[a] || @priorities[b] do
(@priorities[a] || 100) < (@priorities[b] || 100)
else
a < b
end
end)
end
end