Current section
Files
Jump to
Current section
Files
lib/ex_yarn.ex
defmodule ExYarn do
@moduledoc """
ExYarn is a small library for parsing [Yarn](https://yarnpkg.com/) lockfiles in Elixir. Only the version 1 of Yarn
lockfiles is currently supported.
The library allows you to parse either to a plain Elixir map, or to a utility type, `ExYarn.Lockfile`, which makes
manipulating the parsed dependencies a little easier.
#### Note on performance
This library was built in part as a learning exercise and therefore does not necessarily
apply the best possible practices and tools when it comes to code quality and performance. If performance is important
to you, I recommend using Dorgan's library ([hex.pm](https://hex.pm/packages/yarn_parser),
[Github](https://github.com/doorgan/yarn_parser)), which uses
[NimbleParsec](https://hexdocs.pm/nimble_parsec/NimbleParsec.html) for better performance.
## Example
### Parsing to a map
iex> input = ~s(
...># yarn lockfile v1
...>"@babel/code-frame@7.8.3":
...> version "7.10.4"
...> resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
...> integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
...> dependencies:
...> "@babel/highlight" "^7.10.4"
...>)
...> ExYarn.parse(input)
{
:ok,
{
%{
"@babel/code-frame@7.8.3" => %{
"version" => "7.10.4",
"resolved" => "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a",
"integrity" => "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"dependencies" => %{"@babel/highlight" => "^7.10.4"}
}
},
[" yarn lockfile v1"]
}
}
###Â Parsing to a `ExYarn.Lockfile`
iex> input = ~s(
...># yarn lockfile v1
...>"@babel/code-frame@7.8.3":
...> version "7.10.4"
...> resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
...> integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
...> dependencies:
...> "@babel/highlight" "^7.10.4"
...>)
...>ExYarn.parse_to_lockfile(input)
{
:ok,
%ExYarn.Lockfile{
version: 1,
dependencies: [
%ExYarn.Dependency{
name: "@babel/code-frame@7.8.3",
version: "7.10.4",
resolved: "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a",
integrity: "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
dependencies: [
{"@babel/highlight", "^7.10.4"}
],
optional_dependencies: []
}
],
comments: [" yarn lockfile v1"]}}
"""
alias ExYarn.{Lockfile, Parser}
@doc """
Takes the lockfile's content as input and returns the parsed map
"""
@spec parse(binary) :: {:error, Exception.t()} | {:ok, {map(), [binary]}}
def parse(lockfile_content) do
do_parse(lockfile_content)
end
@doc """
Same as `ExYarn.parse/1` but raises errors instead of returning them
"""
@spec parse!(binary) :: {map, [binary]}
def parse!(lockfile_content) do
case parse(lockfile_content) do
{:ok, result} -> result
{:error, error} -> raise error
end
end
@doc """
Takes the lockfile's content as input and returns a parsed `ExYarn.Lockfile`
"""
@spec parse_to_lockfile(binary) :: {:error, binary | Exception.t()} | {:ok, Lockfile.t()}
def parse_to_lockfile(lockfile_content) do
case parse(lockfile_content) do
{:ok, parse_result} -> build_lockfile(parse_result)
{:error, error} -> {:error, error}
end
end
@doc """
Same as `ExYarn.parse_to_lockfile/1` but raises errors instead of returning them
"""
@spec parse_to_lockfile!(binary) :: ExYarn.Lockfile.t()
def parse_to_lockfile!(lockfile_content) do
case parse_to_lockfile(lockfile_content) do
{:ok, lockfile} -> lockfile
{:error, error} -> raise error
end
end
defp do_parse(lockfile_content) do
parse_result =
lockfile_content
|> String.replace_prefix("\uFEFF", "")
|> Parser.parse()
case parse_result do
{:ok, result, comments} -> {:ok, {result, Enum.reverse(comments)}}
{:error, error} -> {:error, error}
end
end
defp build_lockfile(parse_result) do
parse_result
|> Lockfile.from_parse_result()
end
end