commit 68e705caa3dd573a3f8558769bebe3c53539df46 Author: Mikko Ahlroth Date: Mon Oct 15 22:47:05 2018 +0300 Initial commit diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..525446d --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a76453b --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where 3rd-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +mebe_2-*.tar + +# Ignore secret configz +/config/*.secret.exs +/data + +/.elixir_ls + +# Built frontend assets +/priv/static diff --git a/.on-save.json b/.on-save.json new file mode 100644 index 0000000..68ab8ff --- /dev/null +++ b/.on-save.json @@ -0,0 +1,6 @@ +[ + { + "files": "**/*.{ex,exs}", + "command": "mix format ${srcFile}" + } +] diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f6f001 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Mebe2 + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `mebe_2` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:mebe_2, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at [https://hexdocs.pm/mebe_2](https://hexdocs.pm/mebe_2). + diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..79aac7f --- /dev/null +++ b/config/config.exs @@ -0,0 +1,73 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +# This configuration is loaded before any dependency and is restricted +# to this project. If another project depends on this project, this +# file won't be loaded nor affect the parent project. For this reason, +# if you want to provide default values for your application for +# 3rd-party users, it should be done in your "mix.exs" file. + +# You can configure your application as: +# +# config :mebe_2, key: :value +# +# and access this configuration in your application as: +# +# Application.get_env(:mebe_2, :key) +# +# You can also configure a 3rd-party app: +# +# config :logger, level: :info +# + +config :mebe_2, + # The path to crawl post and page data from. No trailing slash, use an absolute path. + data_path: Path.expand("data"), + + # Port to listen on + port: 2124, + # Basic blog information + blog_name: "My awesome blog", + blog_author: "Author McAuthor", + # Absolute URL to the site, including protocol, no trailing slash + absolute_url: "http://localhost:2124", + # Set to true to show author header from posts, if available (blog_author will be used as default) + multi_author_mode: false, + # If multi author mode is on, use blog_author as default author (if this is false, no author will be set if post has no author header) + use_default_author: true, + # Default timezone to use for posts with time data + time_default_tz: "Europe/Helsinki", + # Force "Read more…" text to display even if there is no more content + force_read_more: false, + # Set to true to enable RSS feeds + enable_feeds: false, + # Show full content in feeds instead of short content + feeds_full_content: false, + posts_per_page: 10, + posts_in_feed: 20, + + # Disqus comments + # Use Disqus comments + disqus_comments: false, + # Show comments for pages too + page_commenting: false, + disqus_shortname: "my-awesome-blog", + + # Extra HTML that is injected to every page, right before . Useful for analytics scripts. + extra_html: """ + + + """ + +if Mix.env() == :dev do + config :exsync, :extensions, [".ex", ".eex"] +end + +# If you wish to compile in secret settings, use the following file. Note that the settings in +# the file will be set at release generation time and cannot be changed later. +if File.exists?("config/config.secret.exs") do + import_config("config.secret.exs") +end diff --git a/lib/engine/crawler.ex b/lib/engine/crawler.ex new file mode 100644 index 0000000..c9e4d56 --- /dev/null +++ b/lib/engine/crawler.ex @@ -0,0 +1,113 @@ +defmodule Mebe2.Engine.Crawler do + @moduledoc """ + The crawler goes through the specified directory, opening and parsing all the matching files + inside concurrently. + """ + require Logger + + alias Mebe2.Engine.{Parser, Utils, SlugUtils} + alias Mebe2.Engine.Models.{Page, Post} + + def crawl(path) do + get_files(path) + |> Enum.map(fn file -> Task.async(__MODULE__, :parse, [file]) end) + |> handle_responses() + |> construct_archives() + end + + def get_files(path) do + path = path <> "/**/*.md" + Logger.info("Searching files using '#{path}' with cwd '#{System.cwd()}'") + files = Path.wildcard(path) + Logger.info("Found files:") + + for file <- files do + Logger.info(file) + end + + files + end + + def parse(file) do + try do + File.read!(file) + |> Parser.parse(Path.basename(file)) + rescue + _ -> + Logger.error("Could not parse file #{file}. Exception: #{inspect(__STACKTRACE__)}") + :error + end + end + + def handle_responses(tasklist) do + Enum.map(tasklist, fn task -> Task.await(task) end) + end + + def construct_archives(datalist) do + Enum.reduce( + datalist, + %{ + pages: %{}, + posts: [], + years: %{}, + months: %{}, + tags: %{}, + authors: %{}, + author_names: %{} + }, + fn pagedata, acc -> + case pagedata do + # Ignore pages/posts that could not be parsed + :error -> + acc + + %Page{} -> + put_in(acc, [:pages, pagedata.slug], pagedata) + + %Post{} -> + {{year, month, _}, _} = Calendar.DateTime.to_erl(pagedata.datetime) + + tags = + Enum.reduce(pagedata.tags, acc.tags, fn tag, tagmap -> + posts = Map.get(tagmap, tag, []) + Map.put(tagmap, tag, [pagedata | posts]) + end) + + {authors, author_names} = form_authors(acc, pagedata) + + year_posts = [pagedata | Map.get(acc.years, year, [])] + month_posts = [pagedata | Map.get(acc.months, {year, month}, [])] + + %{ + acc + | posts: [pagedata | acc.posts], + years: Map.put(acc.years, year, year_posts), + months: Map.put(acc.months, {year, month}, month_posts), + tags: tags, + authors: authors, + author_names: author_names + } + end + end + ) + end + + defp form_authors(datalist, pagedata) do + multi_author_mode = Mebe2.get_conf(:multi_author_mode) + do_form_authors(multi_author_mode, datalist, pagedata) + end + + defp do_form_authors(false, _, _), do: {%{}, %{}} + + defp do_form_authors(true, %{authors: authors, author_names: author_names}, pagedata) do + author_name = Utils.get_author(pagedata) + author_slug = SlugUtils.slugify(author_name) + author_posts = [pagedata | Map.get(authors, author_slug, [])] + authors = Map.put(authors, author_slug, author_posts) + + # Authors end up with the name that was in the post with the first matching slug + author_names = Map.put_new(author_names, author_slug, author_name) + + {authors, author_names} + end +end diff --git a/lib/engine/db.ex b/lib/engine/db.ex new file mode 100644 index 0000000..3782b93 --- /dev/null +++ b/lib/engine/db.ex @@ -0,0 +1,258 @@ +defmodule Mebe2.Engine.DB do + require Logger + alias Mebe2.Engine.{Utils, SlugUtils, Models} + + alias Calendar.DateTime + + @moduledoc """ + Stuff related to storing the blog data to memory (ETS). + """ + + # Table for meta information, like the counts of posts and names + # of authors + @meta_table :mebe2_meta + + # Table for storing pages by slug + @page_table :mebe2_pages + + # Table for sequential retrieval of posts (for list pages) + @post_table :mebe2_posts + + # Table for quick retrieval of single post (with key) + @single_post_table :mebe2_single_posts + + # Table for storing posts with tag as first element of key + @tag_table :mebe2_tags + + # Table for storing posts by specific authors + @author_table :mebe2_authors + + # Table for storing menu data + @menu_table :mebe2_menu + + @spec init() :: :ok + def init() do + # Only create tables if they don't exist already + if :ets.info(@meta_table) == :undefined do + :ets.new(@meta_table, [:named_table, :set, :protected, read_concurrency: true]) + :ets.new(@page_table, [:named_table, :set, :protected, read_concurrency: true]) + :ets.new(@post_table, [:named_table, :ordered_set, :protected, read_concurrency: true]) + :ets.new(@single_post_table, [:named_table, :set, :protected, read_concurrency: true]) + :ets.new(@tag_table, [:named_table, :ordered_set, :protected, read_concurrency: true]) + :ets.new(@menu_table, [:named_table, :ordered_set, :protected, read_concurrency: true]) + + if Mebe2.get_conf(:multi_author_mode) do + :ets.new(@author_table, [:named_table, :ordered_set, :protected, read_concurrency: true]) + end + end + + :ok + end + + @spec destroy() :: :ok + def destroy() do + :ets.delete_all_objects(@meta_table) + :ets.delete_all_objects(@page_table) + :ets.delete_all_objects(@post_table) + :ets.delete_all_objects(@single_post_table) + :ets.delete_all_objects(@tag_table) + :ok + end + + @spec insert_count(:all, integer) :: true + def insert_count(:all, count) do + insert_meta(:all, :all, count) + end + + @spec insert_count(atom, String.t() | integer, integer) :: true + def insert_count(type, key, count) do + insert_meta(type, key, count) + end + + @spec insert_menu([{String.t(), String.t()}]) :: true + def insert_menu(menu) do + # Format for ETS because it needs a tuple + menu = Enum.map(menu, fn menuitem -> {menuitem.slug, menuitem} end) + :ets.insert(@menu_table, menu) + end + + @spec insert_posts([Models.Post.t()]) :: :ok + def insert_posts(posts) do + ordered_posts = + Enum.map(posts, fn post -> + {{year, month, day}, _} = DateTime.to_erl(post.datetime) + {{year, month, day, post.order}, post} + end) + + single_posts = + Enum.map(posts, fn post -> + {{year, month, day}, _} = DateTime.to_erl(post.datetime) + {{year, month, day, post.slug}, post} + end) + + :ets.insert(@post_table, ordered_posts) + :ets.insert(@single_post_table, single_posts) + + if Mebe2.get_conf(:multi_author_mode) do + author_posts = + Enum.filter(posts, fn post -> Map.has_key?(post.extra_headers, "author") end) + |> Enum.map(fn post -> + {{year, month, day}, _} = DateTime.to_erl(post.datetime) + + author_slug = Utils.get_author(post) |> SlugUtils.slugify() + {{author_slug, year, month, day, post.order}, post} + end) + + :ets.insert(@author_table, author_posts) + end + + :ok + end + + @spec insert_page(Models.Page.t()) :: true + def insert_page(page) do + :ets.insert(@page_table, {page.slug, page}) + end + + @spec insert_tag_posts(%{optional(String.t()) => Models.Post.t()}) :: true + def insert_tag_posts(tags) do + tag_posts = + Enum.reduce(Map.keys(tags), [], fn tag, acc -> + Enum.reduce(tags[tag], acc, fn post, inner_acc -> + {{year, month, day}, _} = DateTime.to_erl(post.datetime) + [{{tag, year, month, day, post.order}, post} | inner_acc] + end) + end) + + :ets.insert(@tag_table, tag_posts) + end + + @spec insert_author_posts(%{optional(String.t()) => Models.Post.t()}) :: true + def insert_author_posts(authors) do + author_posts = + Enum.reduce(Map.keys(authors), [], fn author_slug, acc -> + Enum.reduce(authors[author_slug], acc, fn post, inner_acc -> + {{year, month, day}, _} = DateTime.to_erl(post.datetime) + [{{author_slug, year, month, day, post.order}, post} | inner_acc] + end) + end) + + :ets.insert(@author_table, author_posts) + end + + @spec insert_author_names(%{optional(String.t()) => String.t()}) :: true + def insert_author_names(author_names_map) do + author_names = + Enum.reduce(Map.keys(author_names_map), [], fn author_slug, acc -> + [{{:author_name, author_slug}, author_names_map[author_slug]} | acc] + end) + + :ets.insert(@meta_table, author_names) + end + + @spec get_menu() :: [Models.MenuItem.t()] + def get_menu() do + case :ets.match(@menu_table, :"$1") do + [] -> [] + results -> format_menu(results) + end + end + + @spec get_reg_posts(integer(), integer()) :: [Models.Post.t()] + def get_reg_posts(first, last) do + get_post_list(@post_table, [{:"$1", [], [:"$_"]}], first, last) + end + + @spec get_tag_posts(String.t(), integer(), integer()) :: [Models.Post.t()] + def get_tag_posts(tag, first, last) do + get_post_list(@tag_table, [{{{tag, :_, :_, :_, :_}, :"$1"}, [], [:"$_"]}], first, last) + end + + @spec get_author_posts(String.t(), integer(), integer()) :: [Models.Post.t()] + def get_author_posts(author_slug, first, last) do + get_post_list( + @author_table, + [{{{author_slug, :_, :_, :_, :_}, :"$1"}, [], [:"$_"]}], + first, + last + ) + end + + @spec get_year_posts(integer(), integer(), integer()) :: [Models.Post.t()] + def get_year_posts(year, first, last) do + get_post_list(@post_table, [{{{year, :_, :_, :_}, :"$1"}, [], [:"$_"]}], first, last) + end + + @spec get_month_posts(integer(), integer(), integer(), integer()) :: [Models.Post.t()] + def get_month_posts(year, month, first, last) do + get_post_list(@post_table, [{{{year, month, :_, :_}, :"$1"}, [], [:"$_"]}], first, last) + end + + @spec get_page(String.t()) :: Models.Page.t() | nil + def get_page(slug) do + case :ets.match_object(@page_table, {slug, :"$1"}) do + [{_, page}] -> page + _ -> nil + end + end + + @spec get_post(integer(), integer(), integer(), String.t()) :: Models.Post.t() | nil + def get_post(year, month, day, slug) do + case :ets.match_object(@single_post_table, {{year, month, day, slug}, :"$1"}) do + [{_, post}] -> post + _ -> nil + end + end + + @spec get_count(:all) :: integer() + def get_count(:all) do + get_count(:all, :all) + end + + @spec get_count(atom, :all | integer | String.t()) :: integer() + def get_count(type, key) do + get_meta(type, key, 0) + end + + @spec get_author_name(String.t()) :: String.t() + def get_author_name(author_slug) do + get_meta(:author_name, author_slug, author_slug) + end + + @spec insert_meta(atom, :all | integer | String.t(), integer | String.t()) :: true + defp insert_meta(type, key, value) do + :ets.insert(@meta_table, {{type, key}, value}) + end + + @spec get_meta(atom, :all | integer | String.t(), integer | String.t()) :: integer | String.t() + defp get_meta(type, key, default) do + case :ets.match_object(@meta_table, {{type, key}, :"$1"}) do + [{{_, _}, value}] -> value + [] -> default + end + end + + # Combine error handling of different post listing functions + @spec get_post_list(atom, [tuple], integer, integer) :: [Models.Post.t()] + defp get_post_list(table, matchspec, first, last) do + case :ets.select_reverse(table, matchspec, first + last) do + :"$end_of_table" -> + [] + + {result, _} -> + Enum.split(result, first) |> elem(1) |> ets_to_data() + end + end + + # Remove key from data returned from ETS + @spec ets_to_data([{any, any}]) :: any + defp ets_to_data(data) do + for {_, actual} <- data, do: actual + end + + # Format menu results (convert [{slug, %MenuItem{}}] to %MenuItem{}) + @spec format_menu([[{String.t(), Models.MenuItem.t()}]]) :: [Models.MenuItem.t()] + defp format_menu(results) do + for [{_, result}] <- results, do: result + end +end diff --git a/lib/engine/menuparser.ex b/lib/engine/menuparser.ex new file mode 100644 index 0000000..989d5f9 --- /dev/null +++ b/lib/engine/menuparser.ex @@ -0,0 +1,27 @@ +defmodule Mebe2.Engine.MenuParser do + @moduledoc """ + This module handles the parsing of the menu file, which lists the links in the menu bar. + """ + alias Mebe2.Engine.Models.MenuItem + + def parse(data_path) do + (data_path <> "/menu") + |> File.read!() + |> split_lines + |> parse_lines + |> Enum.filter(fn item -> item != nil end) + end + + defp split_lines(menudata) do + String.split(menudata, ~R/\r?\n/) + end + + defp parse_lines(menulines) do + for line <- menulines do + case String.split(line, " ") do + [_] -> nil + [link | rest] -> %MenuItem{slug: link, title: Enum.join(rest, " ")} + end + end + end +end diff --git a/lib/engine/models.ex b/lib/engine/models.ex new file mode 100644 index 0000000..dcfb6e8 --- /dev/null +++ b/lib/engine/models.ex @@ -0,0 +1,69 @@ +defmodule Mebe2.Engine.Models do + @moduledoc """ + This module contains the data models of the blog engine. + """ + + defmodule PageData do + defstruct filename: nil, + title: nil, + headers: [], + content: nil + + @type t :: %__MODULE__{ + filename: String.t(), + title: String.t(), + headers: [{String.t(), String.t()}], + content: String.t() + } + end + + defmodule Post do + defstruct slug: nil, + title: nil, + datetime: nil, + time_given: false, + tags: [], + content: nil, + short_content: nil, + order: 0, + has_more: false, + extra_headers: %{} + + @type t :: %__MODULE__{ + slug: String.t(), + title: String.t(), + datetime: DateTime.t(), + time_given: boolean, + tags: [String.t()], + content: String.t(), + short_content: String.t(), + order: integer, + has_more: boolean, + extra_headers: %{optional(String.t()) => String.t()} + } + end + + defmodule Page do + defstruct slug: nil, + title: nil, + content: nil, + extra_headers: %{} + + @type t :: %__MODULE__{ + slug: String.t(), + title: String.t(), + content: String.t(), + extra_headers: %{optional(String.t()) => String.t()} + } + end + + defmodule MenuItem do + defstruct slug: nil, + title: nil + + @type t :: %__MODULE__{ + slug: String.t(), + title: String.t() + } + end +end diff --git a/lib/engine/parser.ex b/lib/engine/parser.ex new file mode 100644 index 0000000..0c317f9 --- /dev/null +++ b/lib/engine/parser.ex @@ -0,0 +1,171 @@ +defmodule Mebe2.Engine.Parser do + @moduledoc """ + This module contains the parser, which parses page data and returns the contents in the correct format. + """ + + alias Mebe2.Engine.Models.{PageData, Page, Post} + + @time_re ~R/(?\d\d):(?\d\d)(?: (?.*))?/ + + @earmark_opts %Earmark.Options{ + code_class_prefix: "language-" + } + + def parse(pagedata, filename) do + split_lines(pagedata) + |> parse_raw(%PageData{filename: filename}) + |> render_content() + |> format() + end + + def split_lines(pagedata) do + String.split(pagedata, ~R/\r?\n/) + end + + def parse_raw(datalines, pagedata \\ %PageData{}, mode \\ :title) + + def parse_raw([title | rest], pagedata, :title) do + parse_raw(rest, %{pagedata | title: title}, :headers) + end + + def parse_raw(["" | rest], pagedata, :headers) do + # Reverse the headers so they appear in the order that they do in the file + headers = Enum.reverse(pagedata.headers) + parse_raw(rest, %{pagedata | headers: headers}, :content) + end + + def parse_raw([header | rest], pagedata, :headers) do + headers = [header | pagedata.headers] + parse_raw(rest, %{pagedata | headers: headers}, :headers) + end + + def parse_raw(content, pagedata, :content) when is_list(content) do + %{pagedata | content: Enum.join(content, "\n")} + end + + def render_content(pagedata) do + %{pagedata | content: Earmark.as_html!(pagedata.content, @earmark_opts)} + end + + def format(%PageData{ + filename: filename, + title: title, + headers: headers, + content: content + }) do + case Regex.run(~R/^(?:(\d{4})-(\d{2})-(\d{2})(?:-(\d{2}))?-)?(.*?).md$/iu, filename) do + # Pages do not have any date information + [_, "", "", "", "", slug] -> + %Page{ + slug: slug, + title: title, + content: content, + extra_headers: parse_headers(headers) + } + + [_, year, month, day, order, slug] -> + {tags, extra_headers} = split_tags(headers) + extra_headers = parse_headers(extra_headers) + + order = format_order(order) + + split_content = String.split(content, ~R//u) + + date_erl = date_to_int_tuple({year, month, day}) + date = Date.from_erl!(date_erl) + + time_header = Map.get(extra_headers, "time", nil) + {time, tz} = parse_time(time_header) + + datetime = Calendar.DateTime.from_date_and_time_and_zone!(date, time, tz) + + %Post{ + slug: slug, + title: title, + datetime: datetime, + time_given: time_header != nil, + tags: parse_tags(tags), + content: content, + short_content: hd(split_content), + order: order, + has_more: Enum.count(split_content) > 1, + extra_headers: extra_headers + } + end + end + + defp parse_headers(headers) do + # Parse a list of headers into a string keyed map + Enum.reduce(headers, %{}, fn header, acc -> + {key, val} = split_header(header) + Map.put(acc, key, val) + end) + end + + defp split_header(header) do + # Enforce 2 parts + [key | [val]] = String.split(header, ":", parts: 2) + {String.trim(key), String.trim(val)} + end + + # Split tags from top of headers + defp split_tags([]), do: {"", []} + defp split_tags([tags | headers]), do: {tags, headers} + + defp parse_tags(tagline) do + case String.split(tagline, ~R/,\s*/iu) do + [""] -> [] + list -> list + end + end + + # Parse time data from time header + defp parse_time(nil), do: form_time(nil) + + defp parse_time(time_header) when is_binary(time_header) do + with %{"hours" => h, "minutes" => m, "timezone" => tz} <- + Regex.named_captures(@time_re, time_header) do + tz = + if tz == "" do + Mebe2.get_conf(:time_default_tz) + else + tz + end + + form_time({str_to_int(h), str_to_int(m)}, tz) + else + _ -> form_time(nil) + end + end + + # Form time and timezone from given time parts + def form_time(nil) do + # If not given, time is midnight (RSS feeds require a time) + {Time.from_erl!({0, 0, 0}), Mebe2.get_conf(:time_default_tz)} + end + + def form_time({hours, minutes}, tz) do + {Time.from_erl!({hours, minutes, 0}), tz} + end + + defp date_to_int_tuple({year, month, day}) do + { + str_to_int(year), + str_to_int(month), + str_to_int(day) + } + end + + defp str_to_int("00"), do: 0 + + defp str_to_int(str) when is_binary(str) do + {int, _} = + String.trim_leading(str, "0") + |> Integer.parse() + + int + end + + defp format_order(""), do: 0 + defp format_order(order), do: str_to_int(order) +end diff --git a/lib/engine/slug_utils.ex b/lib/engine/slug_utils.ex new file mode 100644 index 0000000..64deba1 --- /dev/null +++ b/lib/engine/slug_utils.ex @@ -0,0 +1,25 @@ +defmodule Mebe2.Engine.SlugUtils do + @moduledoc """ + Utilities related to handling of slugs. + """ + + alias Mebe2.Engine.DB + + @doc """ + Get slug out of a given value. + + Nil is returned as is. + """ + def slugify(nil), do: nil + + def slugify(value) do + Slugger.slugify_downcase(value) + end + + @doc """ + Get the author name related to this slug from the DB. + """ + def unslugify_author(slug) do + DB.get_author_name(slug) + end +end diff --git a/lib/engine/utils.ex b/lib/engine/utils.ex new file mode 100644 index 0000000..3402b1f --- /dev/null +++ b/lib/engine/utils.ex @@ -0,0 +1,44 @@ +defmodule Mebe2.Engine.Utils do + @moduledoc """ + This module contains functions and other stuff that just don't fit anywhere else properly. + """ + + alias Mebe2.Engine.Models + + @doc """ + Get the author of a post. + + Returns a value according to the following pseudocode + + if multi author mode is on then + if post has author then + return post's author + else if use default author is on then + return blog author + else return nil + else if use default author is on then + return blog author + else return nil + """ + @spec get_author(Models.Post.t()) :: String.t() | nil + def get_author(%Models.Post{extra_headers: extra_headers}) do + multi_author_mode = Mebe2.get_conf(:multi_author_mode) + use_default_author = Mebe2.get_conf(:use_default_author) + blog_author = Mebe2.get_conf(:blog_author) + + if multi_author_mode do + cond do + Map.has_key?(extra_headers, "author") -> + Map.get(extra_headers, "author") + + use_default_author -> + blog_author + + true -> + nil + end + else + if use_default_author, do: blog_author, else: nil + end + end +end diff --git a/lib/engine/worker.ex b/lib/engine/worker.ex new file mode 100644 index 0000000..a6bae4c --- /dev/null +++ b/lib/engine/worker.ex @@ -0,0 +1,87 @@ +defmodule Mebe2.Engine.Worker do + @moduledoc """ + This worker initializes the post database and keeps it alive while the server is running. + """ + use GenServer + require Logger + + alias Mebe2.Engine.{Crawler, DB, MenuParser} + + def start_link(opts \\ []) do + GenServer.start_link(__MODULE__, :ok, opts) + end + + def init(:ok) do + load_db() + {:ok, nil} + end + + def handle_call(:refresh, _from, nil) do + refresh_db() + {:reply, :ok, nil} + end + + def refresh_db() do + Logger.info("Destroying database…") + DB.destroy() + Logger.info("Reloading database…") + load_db() + Logger.info("Update done!") + end + + @doc """ + Initialize the database by crawling the configured path and parsing data to the DB. + """ + def load_db() do + data_path = Mebe2.get_conf(:data_path) + + Logger.info("Loading menu from '#{data_path}/menu'…") + + menu = MenuParser.parse(data_path) + + Logger.info("Loading post database from '#{data_path}'…") + + %{ + pages: pages, + posts: posts, + tags: tags, + authors: authors, + author_names: author_names, + years: years, + months: months + } = Crawler.crawl(data_path) + + Logger.info("Loaded #{Enum.count(pages)} pages and #{Enum.count(posts)} posts.") + + DB.init() + + DB.insert_menu(menu) + DB.insert_posts(posts) + DB.insert_count(:all, Enum.count(posts)) + + Enum.each(Map.keys(pages), fn page -> DB.insert_page(pages[page]) end) + + DB.insert_tag_posts(tags) + Enum.each(Map.keys(tags), fn tag -> DB.insert_count(:tag, tag, Enum.count(tags[tag])) end) + + if Mebe2.get_conf(:multi_author_mode) do + DB.insert_author_posts(authors) + DB.insert_author_names(author_names) + + Enum.each(Map.keys(authors), fn author -> + DB.insert_count(:author, author, Enum.count(authors[author])) + end) + end + + # For years and months, only insert the counts (the data can be fetched from main posts table) + Enum.each(Map.keys(years), fn year -> + DB.insert_count(:year, year, Enum.count(years[year])) + end) + + Enum.each(Map.keys(months), fn month -> + DB.insert_count(:month, month, Enum.count(months[month])) + end) + + Logger.info("Posts loaded.") + end +end diff --git a/lib/mebe_2.ex b/lib/mebe_2.ex new file mode 100644 index 0000000..f6385f2 --- /dev/null +++ b/lib/mebe_2.ex @@ -0,0 +1,63 @@ +defmodule Mebe2 do + @moduledoc """ + Documentation for Mebe2. + """ + + @conf_datatypes %{ + multi_author_mode: :bool, + use_default_author: :bool, + force_read_more: :bool, + enable_feeds: :bool, + feeds_full_content: :bool, + posts_per_page: :int, + posts_in_feed: :int, + disqus_comments: :bool, + page_commenting: :bool, + port: :int, + host_port: :int + } + + @doc """ + Get a configuration setting. + + Gets setting from env vars (same name as setting, but with ALL_CAPS), and uses + Application.get_env as backup. If setting has a datatype conversion defined above, that will + be used to map the return datatype. Otherwise return value will be string (if from env var) or + any() (if from config file). + """ + @spec get_conf(atom()) :: any() + def get_conf(key) do + val = + case key |> Atom.to_string() |> String.upcase() |> System.get_env() do + nil -> Application.get_env(:mebe_2, key) + val -> val + end + + case Map.get(@conf_datatypes, key) do + nil -> + val + + atom -> + fun = "get_#{Atom.to_string(atom)}!" |> String.to_atom() + apply(__MODULE__, fun, [val]) + end + end + + @doc """ + Get boolean from env value, strings ("true", "false") or booleans are accepted as, others will + raise. + """ + @spec get_bool!(atom() | String.t()) :: boolean() + def get_bool!(val) when is_boolean(val), do: val + def get_bool!("true"), do: true + def get_bool!("false"), do: false + def get_bool!(val), do: raise("'#{inspect(val)}' is invalid value for boolean.") + + @doc """ + Get integer from env value, integer strings and integers are accepted, others will raise. + """ + @spec get_int!(integer() | String.t()) :: integer() + def get_int!(val) when is_integer(val), do: val + def get_int!(val) when is_binary(val), do: String.to_integer(val) + def get_int!(val), do: raise("'#{inspect(val)}' is invalid value for integer.") +end diff --git a/lib/mebe_2/application.ex b/lib/mebe_2/application.ex new file mode 100644 index 0000000..ee9e72e --- /dev/null +++ b/lib/mebe_2/application.ex @@ -0,0 +1,30 @@ +defmodule Mebe2.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + def start(_type, _args) do + port = Mebe2.get_conf(:port) + + case Code.ensure_loaded(ExSync) do + {:module, ExSync = mod} -> + mod.start() + + {:error, :nofile} -> + :ok + end + + # List all child processes to be supervised + children = [ + Mebe2.Engine.Worker.child_spec(name: Mebe2.Engine.Worker), + {Mebe2.Web.Router, [[], [port: port]]} + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Mebe2.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/lib/mix/tasks/mebe.clean.ex b/lib/mix/tasks/mebe.clean.ex new file mode 100644 index 0000000..21be133 --- /dev/null +++ b/lib/mix/tasks/mebe.clean.ex @@ -0,0 +1,12 @@ +defmodule Mix.Tasks.Mebe.Clean do + use MBU.BuildTask, auto_path: false, create_out_path: false + require Logger + + @shortdoc "Clean frontend build artifacts" + + task _ do + static_path = File.cwd!() |> Path.join("priv") |> Path.join("static") + Logger.debug("Cleaning path #{static_path}...") + File.rm_rf!(static_path) + end +end diff --git a/lib/mix/tasks/mebe.serve.ex b/lib/mix/tasks/mebe.serve.ex new file mode 100644 index 0000000..d366e15 --- /dev/null +++ b/lib/mix/tasks/mebe.serve.ex @@ -0,0 +1,19 @@ +defmodule Mix.Tasks.Mebe.Serve do + use MBU.BuildTask, auto_path: false, create_out_path: false + import MBU.TaskUtils + + @shortdoc "Start Mebe2 server and frontend development tools" + + @deps ["mebe.clean"] + + task _ do + frontend_path = Path.join([File.cwd!(), "lib", "web", "frontend"]) + + [ + exec(System.find_executable("bsb"), ["-w"], name: "ocaml", cd: frontend_path), + exec(System.find_executable("node"), ["fuse"], name: "fusebox", cd: frontend_path), + exec(System.find_executable("mix"), ["run", "--no-halt"]) + ] + |> listen(watch: true) + end +end diff --git a/lib/web/frontend/.gitignore b/lib/web/frontend/.gitignore new file mode 100644 index 0000000..a825ac4 --- /dev/null +++ b/lib/web/frontend/.gitignore @@ -0,0 +1,29 @@ +*.exe +*.obj +*.out +*.compile +*.native +*.byte +*.cmo +*.annot +*.cmi +*.cmx +*.cmt +*.cmti +*.cma +*.a +*.cmxa +*.obj +*~ +*.annot +*.cmj +*.bak +lib/bs +*.mlast +*.mliast +.vscode +.fusebox +.merlin +.bsb.lock +*.bs.js +/node_modules diff --git a/lib/web/frontend/README.md b/lib/web/frontend/README.md new file mode 100644 index 0000000..1c02d2a --- /dev/null +++ b/lib/web/frontend/README.md @@ -0,0 +1,16 @@ + + +# Build +``` +npm run build +``` + +# Watch + +``` +npm run watch +``` + + +# Editor +If you use `vscode`, Press `Windows + Shift + B` it will build automatically \ No newline at end of file diff --git a/lib/web/frontend/bsconfig.json b/lib/web/frontend/bsconfig.json new file mode 100644 index 0000000..30f26f9 --- /dev/null +++ b/lib/web/frontend/bsconfig.json @@ -0,0 +1,18 @@ +{ + "name": "frontend", + "version": "0.1.0", + "sources": { + "dir": "src", + "subdirs": true + }, + "package-specs": { + "module": "commonjs", + "in-source": true + }, + "suffix": ".bs.js", + "bs-dependencies": [], + "warnings": { + "error": "+101" + }, + "refmt": 3 +} \ No newline at end of file diff --git a/lib/web/frontend/fuse.js b/lib/web/frontend/fuse.js new file mode 100644 index 0000000..6f459c9 --- /dev/null +++ b/lib/web/frontend/fuse.js @@ -0,0 +1,43 @@ +const { FuseBox, QuantumPlugin, SassPlugin, CSSPlugin, CSSResourcePlugin } = require("fuse-box"); + +const DIST_PATH = '../../../priv/static'; + +const IS_PRODUCTION = process.env.NODE_ENV === 'production'; + +const fuse = FuseBox.init({ + homeDir: "src", + output: `${DIST_PATH}/$name.js`, + target: "browser@es5", + plugins: [ + [ + SassPlugin(), + CSSResourcePlugin({ dist: `${DIST_PATH}/css-resources` }), + CSSPlugin(), + ], + IS_PRODUCTION && QuantumPlugin({ + bakeApiIntoBundle: 'app', + uglify: true, + css: true, + }) + ] +}); + +if (!IS_PRODUCTION) { + fuse.dev({ + port: 2125, + proxy: { + '/': { + target: 'http://127.0.0.1:2124', + changeOrigin: true + } + } + }); +} + +const app = fuse.bundle('app').instructions(`> index.bs.js + style/index.scss`); + +if (!IS_PRODUCTION) { + app.hmr({ reload: true }).watch(); +} + +fuse.run(); diff --git a/lib/web/frontend/package.json b/lib/web/frontend/package.json new file mode 100644 index 0000000..0077ede --- /dev/null +++ b/lib/web/frontend/package.json @@ -0,0 +1,26 @@ +{ + "name": "frontend", + "version": "0.1.0", + "scripts": { + "clean": "bsb -clean-world", + "build": "bsb -make-world", + "watch": "bsb -make-world -w" + }, + "keywords": [ + "BuckleScript" + ], + "author": "", + "license": "MIT", + "devDependencies": { + "bs-platform": "^4.0.5", + "fuse-box": "^3.5.0", + "http-proxy-middleware": "^0.19.0", + "node-sass": "^4.9.3", + "typescript": "^3.0.3", + "uglify-js": "^3.4.9" + }, + "dependencies": { + "@csstools/normalize.css": "^9.0.1", + "prismjs": "^1.15.0" + } +} diff --git a/lib/web/frontend/src/code_hl.ml b/lib/web/frontend/src/code_hl.ml new file mode 100644 index 0000000..a0c3b9b --- /dev/null +++ b/lib/web/frontend/src/code_hl.ml @@ -0,0 +1,7 @@ +(* Code block highlighting in blog, with PrismJS. *) +[%raw {| + require('prismjs'), + require('prismjs/components/prism-elixir'), + require('prismjs/components/prism-bash'), + require('prismjs/components/prism-c') +|}] diff --git a/lib/web/frontend/src/fonts/FIRA-LICENSE.txt b/lib/web/frontend/src/fonts/FIRA-LICENSE.txt new file mode 100755 index 0000000..75519a9 --- /dev/null +++ b/lib/web/frontend/src/fonts/FIRA-LICENSE.txt @@ -0,0 +1,93 @@ +Copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/lib/web/frontend/src/fonts/FiraSans-Black.ttf b/lib/web/frontend/src/fonts/FiraSans-Black.ttf new file mode 100644 index 0000000..b9f9e1c Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Black.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Black.woff b/lib/web/frontend/src/fonts/FiraSans-Black.woff new file mode 100644 index 0000000..d058d0b Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Black.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Black.woff2 b/lib/web/frontend/src/fonts/FiraSans-Black.woff2 new file mode 100644 index 0000000..bdce619 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Black.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-BlackItalic.ttf b/lib/web/frontend/src/fonts/FiraSans-BlackItalic.ttf new file mode 100644 index 0000000..e728ba7 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-BlackItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-BlackItalic.woff b/lib/web/frontend/src/fonts/FiraSans-BlackItalic.woff new file mode 100644 index 0000000..c41cffd Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-BlackItalic.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-BlackItalic.woff2 b/lib/web/frontend/src/fonts/FiraSans-BlackItalic.woff2 new file mode 100644 index 0000000..6c47058 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-BlackItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Bold.ttf b/lib/web/frontend/src/fonts/FiraSans-Bold.ttf new file mode 100644 index 0000000..a3e7073 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Bold.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Bold.woff b/lib/web/frontend/src/fonts/FiraSans-Bold.woff new file mode 100644 index 0000000..8a04bc5 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Bold.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Bold.woff2 b/lib/web/frontend/src/fonts/FiraSans-Bold.woff2 new file mode 100644 index 0000000..7ca1db8 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Bold.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-BoldItalic.ttf b/lib/web/frontend/src/fonts/FiraSans-BoldItalic.ttf new file mode 100644 index 0000000..b296e8f Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-BoldItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-BoldItalic.woff b/lib/web/frontend/src/fonts/FiraSans-BoldItalic.woff new file mode 100644 index 0000000..5552fc3 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-BoldItalic.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-BoldItalic.woff2 b/lib/web/frontend/src/fonts/FiraSans-BoldItalic.woff2 new file mode 100644 index 0000000..ab649e1 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-BoldItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraBold.ttf b/lib/web/frontend/src/fonts/FiraSans-ExtraBold.ttf new file mode 100644 index 0000000..49a30ff Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraBold.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraBold.woff b/lib/web/frontend/src/fonts/FiraSans-ExtraBold.woff new file mode 100644 index 0000000..bace87f Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraBold.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraBold.woff2 b/lib/web/frontend/src/fonts/FiraSans-ExtraBold.woff2 new file mode 100644 index 0000000..47f003a Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraBold.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraBoldItalic.ttf b/lib/web/frontend/src/fonts/FiraSans-ExtraBoldItalic.ttf new file mode 100644 index 0000000..ef0ba69 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraBoldItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraBoldItalic.woff b/lib/web/frontend/src/fonts/FiraSans-ExtraBoldItalic.woff new file mode 100644 index 0000000..9cc65d6 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraBoldItalic.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraBoldItalic.woff2 b/lib/web/frontend/src/fonts/FiraSans-ExtraBoldItalic.woff2 new file mode 100644 index 0000000..0f39afd Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraBoldItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraLight.ttf b/lib/web/frontend/src/fonts/FiraSans-ExtraLight.ttf new file mode 100644 index 0000000..ec8988e Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraLight.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraLight.woff b/lib/web/frontend/src/fonts/FiraSans-ExtraLight.woff new file mode 100644 index 0000000..f925c3e Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraLight.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraLight.woff2 b/lib/web/frontend/src/fonts/FiraSans-ExtraLight.woff2 new file mode 100644 index 0000000..56d5109 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraLight.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraLightItalic.ttf b/lib/web/frontend/src/fonts/FiraSans-ExtraLightItalic.ttf new file mode 100644 index 0000000..550b474 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraLightItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraLightItalic.woff b/lib/web/frontend/src/fonts/FiraSans-ExtraLightItalic.woff new file mode 100644 index 0000000..e224571 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraLightItalic.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ExtraLightItalic.woff2 b/lib/web/frontend/src/fonts/FiraSans-ExtraLightItalic.woff2 new file mode 100644 index 0000000..2c3acbe Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ExtraLightItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Italic.ttf b/lib/web/frontend/src/fonts/FiraSans-Italic.ttf new file mode 100644 index 0000000..bfd91ca Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Italic.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Italic.woff b/lib/web/frontend/src/fonts/FiraSans-Italic.woff new file mode 100644 index 0000000..06afc88 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Italic.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Italic.woff2 b/lib/web/frontend/src/fonts/FiraSans-Italic.woff2 new file mode 100644 index 0000000..0eec9fa Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Italic.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Light.ttf b/lib/web/frontend/src/fonts/FiraSans-Light.ttf new file mode 100644 index 0000000..f256f88 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Light.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Light.woff b/lib/web/frontend/src/fonts/FiraSans-Light.woff new file mode 100644 index 0000000..e5d92c7 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Light.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Light.woff2 b/lib/web/frontend/src/fonts/FiraSans-Light.woff2 new file mode 100644 index 0000000..3b564c0 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Light.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-LightItalic.ttf b/lib/web/frontend/src/fonts/FiraSans-LightItalic.ttf new file mode 100644 index 0000000..c94d2ad Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-LightItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-LightItalic.woff b/lib/web/frontend/src/fonts/FiraSans-LightItalic.woff new file mode 100644 index 0000000..73fa73f Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-LightItalic.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-LightItalic.woff2 b/lib/web/frontend/src/fonts/FiraSans-LightItalic.woff2 new file mode 100644 index 0000000..b5b25ed Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-LightItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Medium.ttf b/lib/web/frontend/src/fonts/FiraSans-Medium.ttf new file mode 100644 index 0000000..63fdc0b Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Medium.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Medium.woff b/lib/web/frontend/src/fonts/FiraSans-Medium.woff new file mode 100644 index 0000000..623feb2 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Medium.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Medium.woff2 b/lib/web/frontend/src/fonts/FiraSans-Medium.woff2 new file mode 100644 index 0000000..a920413 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Medium.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-MediumItalic.ttf b/lib/web/frontend/src/fonts/FiraSans-MediumItalic.ttf new file mode 100644 index 0000000..2a86671 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-MediumItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-MediumItalic.woff b/lib/web/frontend/src/fonts/FiraSans-MediumItalic.woff new file mode 100644 index 0000000..582a434 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-MediumItalic.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-MediumItalic.woff2 b/lib/web/frontend/src/fonts/FiraSans-MediumItalic.woff2 new file mode 100644 index 0000000..23ca518 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-MediumItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Regular.ttf b/lib/web/frontend/src/fonts/FiraSans-Regular.ttf new file mode 100644 index 0000000..b761d2b Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Regular.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Regular.woff b/lib/web/frontend/src/fonts/FiraSans-Regular.woff new file mode 100644 index 0000000..713b610 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Regular.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Regular.woff2 b/lib/web/frontend/src/fonts/FiraSans-Regular.woff2 new file mode 100644 index 0000000..426e540 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Regular.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-SemiBold.ttf b/lib/web/frontend/src/fonts/FiraSans-SemiBold.ttf new file mode 100644 index 0000000..637e132 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-SemiBold.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-SemiBold.woff b/lib/web/frontend/src/fonts/FiraSans-SemiBold.woff new file mode 100644 index 0000000..668f44d Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-SemiBold.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-SemiBold.woff2 b/lib/web/frontend/src/fonts/FiraSans-SemiBold.woff2 new file mode 100644 index 0000000..cd3534c Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-SemiBold.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-SemiBoldItalic.ttf b/lib/web/frontend/src/fonts/FiraSans-SemiBoldItalic.ttf new file mode 100644 index 0000000..211f959 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-SemiBoldItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-SemiBoldItalic.woff b/lib/web/frontend/src/fonts/FiraSans-SemiBoldItalic.woff new file mode 100644 index 0000000..ea43b93 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-SemiBoldItalic.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-SemiBoldItalic.woff2 b/lib/web/frontend/src/fonts/FiraSans-SemiBoldItalic.woff2 new file mode 100644 index 0000000..19f0868 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-SemiBoldItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Thin.ttf b/lib/web/frontend/src/fonts/FiraSans-Thin.ttf new file mode 100644 index 0000000..44ac7a9 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Thin.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Thin.woff b/lib/web/frontend/src/fonts/FiraSans-Thin.woff new file mode 100644 index 0000000..bb6441d Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Thin.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-Thin.woff2 b/lib/web/frontend/src/fonts/FiraSans-Thin.woff2 new file mode 100644 index 0000000..3997555 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-Thin.woff2 differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ThinItalic.ttf b/lib/web/frontend/src/fonts/FiraSans-ThinItalic.ttf new file mode 100644 index 0000000..4895c3d Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ThinItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ThinItalic.woff b/lib/web/frontend/src/fonts/FiraSans-ThinItalic.woff new file mode 100644 index 0000000..8f020b7 Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ThinItalic.woff differ diff --git a/lib/web/frontend/src/fonts/FiraSans-ThinItalic.woff2 b/lib/web/frontend/src/fonts/FiraSans-ThinItalic.woff2 new file mode 100644 index 0000000..036303c Binary files /dev/null and b/lib/web/frontend/src/fonts/FiraSans-ThinItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/LATO-LICENSE.txt b/lib/web/frontend/src/fonts/LATO-LICENSE.txt new file mode 100755 index 0000000..98383e3 --- /dev/null +++ b/lib/web/frontend/src/fonts/LATO-LICENSE.txt @@ -0,0 +1,93 @@ +Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato" + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/lib/web/frontend/src/fonts/Lato-Black.ttf b/lib/web/frontend/src/fonts/Lato-Black.ttf new file mode 100644 index 0000000..1ab2b6e Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Black.ttf differ diff --git a/lib/web/frontend/src/fonts/Lato-Black.woff b/lib/web/frontend/src/fonts/Lato-Black.woff new file mode 100644 index 0000000..7ff0a27 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Black.woff differ diff --git a/lib/web/frontend/src/fonts/Lato-Black.woff2 b/lib/web/frontend/src/fonts/Lato-Black.woff2 new file mode 100644 index 0000000..2fc23ff Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Black.woff2 differ diff --git a/lib/web/frontend/src/fonts/Lato-BlackItalic.ttf b/lib/web/frontend/src/fonts/Lato-BlackItalic.ttf new file mode 100644 index 0000000..56d12d8 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-BlackItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/Lato-BlackItalic.woff b/lib/web/frontend/src/fonts/Lato-BlackItalic.woff new file mode 100644 index 0000000..8150a00 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-BlackItalic.woff differ diff --git a/lib/web/frontend/src/fonts/Lato-BlackItalic.woff2 b/lib/web/frontend/src/fonts/Lato-BlackItalic.woff2 new file mode 100644 index 0000000..852c320 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-BlackItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/Lato-Bold.ttf b/lib/web/frontend/src/fonts/Lato-Bold.ttf new file mode 100644 index 0000000..a7216cc Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Bold.ttf differ diff --git a/lib/web/frontend/src/fonts/Lato-Bold.woff b/lib/web/frontend/src/fonts/Lato-Bold.woff new file mode 100644 index 0000000..f18c5be Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Bold.woff differ diff --git a/lib/web/frontend/src/fonts/Lato-Bold.woff2 b/lib/web/frontend/src/fonts/Lato-Bold.woff2 new file mode 100644 index 0000000..9a81911 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Bold.woff2 differ diff --git a/lib/web/frontend/src/fonts/Lato-BoldItalic.ttf b/lib/web/frontend/src/fonts/Lato-BoldItalic.ttf new file mode 100644 index 0000000..43db5ae Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-BoldItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/Lato-BoldItalic.woff b/lib/web/frontend/src/fonts/Lato-BoldItalic.woff new file mode 100644 index 0000000..8e6cfd8 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-BoldItalic.woff differ diff --git a/lib/web/frontend/src/fonts/Lato-BoldItalic.woff2 b/lib/web/frontend/src/fonts/Lato-BoldItalic.woff2 new file mode 100644 index 0000000..e7ba003 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-BoldItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/Lato-Hairline.ttf b/lib/web/frontend/src/fonts/Lato-Hairline.ttf new file mode 100644 index 0000000..cc872e2 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Hairline.ttf differ diff --git a/lib/web/frontend/src/fonts/Lato-Hairline.woff b/lib/web/frontend/src/fonts/Lato-Hairline.woff new file mode 100644 index 0000000..575b6ba Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Hairline.woff differ diff --git a/lib/web/frontend/src/fonts/Lato-Hairline.woff2 b/lib/web/frontend/src/fonts/Lato-Hairline.woff2 new file mode 100644 index 0000000..e9b2755 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Hairline.woff2 differ diff --git a/lib/web/frontend/src/fonts/Lato-HairlineItalic.ttf b/lib/web/frontend/src/fonts/Lato-HairlineItalic.ttf new file mode 100644 index 0000000..0a4dff5 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-HairlineItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/Lato-HairlineItalic.woff b/lib/web/frontend/src/fonts/Lato-HairlineItalic.woff new file mode 100644 index 0000000..a21e4d3 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-HairlineItalic.woff differ diff --git a/lib/web/frontend/src/fonts/Lato-HairlineItalic.woff2 b/lib/web/frontend/src/fonts/Lato-HairlineItalic.woff2 new file mode 100644 index 0000000..cf6514c Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-HairlineItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/Lato-Italic.ttf b/lib/web/frontend/src/fonts/Lato-Italic.ttf new file mode 100644 index 0000000..c05d815 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Italic.ttf differ diff --git a/lib/web/frontend/src/fonts/Lato-Italic.woff b/lib/web/frontend/src/fonts/Lato-Italic.woff new file mode 100644 index 0000000..238dcd2 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Italic.woff differ diff --git a/lib/web/frontend/src/fonts/Lato-Italic.woff2 b/lib/web/frontend/src/fonts/Lato-Italic.woff2 new file mode 100644 index 0000000..b3c2d32 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Italic.woff2 differ diff --git a/lib/web/frontend/src/fonts/Lato-Light.ttf b/lib/web/frontend/src/fonts/Lato-Light.ttf new file mode 100644 index 0000000..5e53377 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Light.ttf differ diff --git a/lib/web/frontend/src/fonts/Lato-Light.woff b/lib/web/frontend/src/fonts/Lato-Light.woff new file mode 100644 index 0000000..da30f1c Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Light.woff differ diff --git a/lib/web/frontend/src/fonts/Lato-Light.woff2 b/lib/web/frontend/src/fonts/Lato-Light.woff2 new file mode 100644 index 0000000..612e7c8 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Light.woff2 differ diff --git a/lib/web/frontend/src/fonts/Lato-LightItalic.ttf b/lib/web/frontend/src/fonts/Lato-LightItalic.ttf new file mode 100644 index 0000000..237816b Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-LightItalic.ttf differ diff --git a/lib/web/frontend/src/fonts/Lato-LightItalic.woff b/lib/web/frontend/src/fonts/Lato-LightItalic.woff new file mode 100644 index 0000000..10b7a99 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-LightItalic.woff differ diff --git a/lib/web/frontend/src/fonts/Lato-LightItalic.woff2 b/lib/web/frontend/src/fonts/Lato-LightItalic.woff2 new file mode 100644 index 0000000..5ff9b03 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-LightItalic.woff2 differ diff --git a/lib/web/frontend/src/fonts/Lato-Regular.ttf b/lib/web/frontend/src/fonts/Lato-Regular.ttf new file mode 100644 index 0000000..b98b861 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Regular.ttf differ diff --git a/lib/web/frontend/src/fonts/Lato-Regular.woff b/lib/web/frontend/src/fonts/Lato-Regular.woff new file mode 100644 index 0000000..3cd2e20 Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Regular.woff differ diff --git a/lib/web/frontend/src/fonts/Lato-Regular.woff2 b/lib/web/frontend/src/fonts/Lato-Regular.woff2 new file mode 100644 index 0000000..53501fa Binary files /dev/null and b/lib/web/frontend/src/fonts/Lato-Regular.woff2 differ diff --git a/lib/web/frontend/src/index.ml b/lib/web/frontend/src/index.ml new file mode 100644 index 0000000..59fbc97 --- /dev/null +++ b/lib/web/frontend/src/index.ml @@ -0,0 +1,16 @@ +[%raw {| + require('./style/index.scss'), + require('./code_hl.bs.js') +|}] + +external window_hash: string = "hash" [@@bs.val][@@bs.scope "window", "location"] + +let change_url: (string -> unit) = [%raw fun new_url -> "window.location.href = new_url"] + +let run () = + let new_hash = Old_hash_redirector.run_hash_check window_hash in + match new_hash with + | Some h -> change_url h + | None -> () + +let () = run() diff --git a/lib/web/frontend/src/old_hash_redirector.ml b/lib/web/frontend/src/old_hash_redirector.ml new file mode 100644 index 0000000..1df220b --- /dev/null +++ b/lib/web/frontend/src/old_hash_redirector.ml @@ -0,0 +1,48 @@ +(* + * This script will check old Laine-style hashes when the page has been loaded and redirects + * to the correct address if such a hash is found. + * This is done in JS because the hash is not sent to the server. + *) + +type hash_matcher = + { old_hash : Js.Re.t; + new_hash : string; + } + +let hash_re = [ + { old_hash = [%re "/^\\#\\!\\/(\\d{4})\\/(\\d\\d)\\/(\\d\\d)\\/(.*)$/"]; + new_hash = "/$1/$2/$3/$4"; + }; + { old_hash = [%re "/^\\#\\!\\/tag\\/([^\\/]+)$/"]; + new_hash = "/tag/$1"; + }; + { old_hash = [%re "/^\\#\\!\\/tag\\/([^\\/]+)(\\/\\d+)$/"]; + new_hash = "/tag/$1/p/$2"; + }; + { old_hash = [%re "/^\\#\\!\\/archives\\/(\\d{4})$/"]; + new_hash = "/archive/$1"; + }; + { old_hash = [%re "/^\\#\\!\\/archives\\/(\\d{4})\\/(\\d\\d)$/"]; + new_hash = "/archive/$1/$2"; + }; + { old_hash = [%re "/^\\#\\!\\/archives\\/(\\d{4})(\\/\\d+)$/"]; + new_hash = "/archive/$1/p/$2"; + }; + { old_hash = [%re "/\\#\\!\\/archives\\/(\\d{4})\\/(\\d\\d)(\\/\\d+)$/"]; + new_hash = "/archive/$1/$2/p/$3"; + }; + { old_hash = [%re "/^\\#\\!\\/(.*)$/"]; + new_hash = "/$1"; + } +] + +let rec check_single_hash hash_list current_hash = match hash_list with + | matcher :: tail -> + let is_match = Js.Re.test current_hash matcher.old_hash in + if is_match + then Some(Js.String.replaceByRe matcher.old_hash matcher.new_hash current_hash) + else check_single_hash tail current_hash + | [] -> None + +let run_hash_check hash = + check_single_hash hash_re hash diff --git a/lib/web/frontend/src/style/base-layout.scss b/lib/web/frontend/src/style/base-layout.scss new file mode 100644 index 0000000..884322c --- /dev/null +++ b/lib/web/frontend/src/style/base-layout.scss @@ -0,0 +1,100 @@ +// Base layout of the blog + +$layout-padding: 20px; + +html { + height: 100%; +} + +body { + display: grid; + grid-template: 'hd' 'mn' 1fr 'ft' / auto; + margin: 0; + padding: 0; + height: 100%; + + @media #{$md} { + grid-template: 'hd mn' + 'hd ft' + / 1fr 3fr; + } + + // Blog header that also works as a sidebar on big screens + >header { + grid-area: hd; + + display: flex; + flex-direction: column; + justify-content: start; + align-items: start; + + padding: $layout-padding; + background-color: $accent-background; + + a { + color: $accent-link; + + $active-color: darken($accent-link, 5%); + + &:hover h1 { + color: $active-color; + } + + &:visited { + color: $active-color; + } + } + + h1 { + color: $blog-name-color; + text-transform: uppercase; + font-weight: bold; + font-size: 2.1em; + margin: 20px 0 0; + } + + nav#menunav { + ul { + list-style-type: none; + padding-left: 0; + + li { + display: inline-block; + border: 2px solid $accent-link; + padding: 3px 5px; + border-radius: 3px; + } + } + } + } + + // Footer after all blog posts + >footer { + grid-area: ft; + + padding: $layout-padding; + max-width: $content-max-width; + + text-align: center; + + @media #{$sm} { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + + >p { + flex: 1; + margin: 0; + } + } + } + + // Main blog post area + >main { + grid-area: mn; + + padding: $layout-padding; + max-width: $content-max-width; + } +} diff --git a/lib/web/frontend/src/style/fonts.scss b/lib/web/frontend/src/style/fonts.scss new file mode 100644 index 0000000..6117330 --- /dev/null +++ b/lib/web/frontend/src/style/fonts.scss @@ -0,0 +1,283 @@ +// The following definitions were created by Transfonter + +$font-path: '../fonts/'; + +@font-face { + font-family: 'Lato'; + src: local('Lato Regular'), local('Lato-Regular'), + url('#{$font-path}Lato-Regular.woff2') format('woff2'), + url('#{$font-path}Lato-Regular.woff') format('woff'), + url('#{$font-path}Lato-Regular.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'Lato'; + src: local('Lato Bold Italic'), local('Lato-BoldItalic'), + url('#{$font-path}Lato-BoldItalic.woff2') format('woff2'), + url('#{$font-path}Lato-BoldItalic.woff') format('woff'), + url('#{$font-path}Lato-BoldItalic.ttf') format('truetype'); + font-weight: bold; + font-style: italic; +} + +@font-face { + font-family: 'Lato'; + src: local('Lato Bold'), local('Lato-Bold'), + url('#{$font-path}Lato-Bold.woff2') format('woff2'), + url('#{$font-path}Lato-Bold.woff') format('woff'), + url('#{$font-path}Lato-Bold.ttf') format('truetype'); + font-weight: bold; + font-style: normal; +} + +@font-face { + font-family: 'Lato'; + src: local('Lato Black Italic'), local('Lato-BlackItalic'), + url('#{$font-path}Lato-BlackItalic.woff2') format('woff2'), + url('#{$font-path}Lato-BlackItalic.woff') format('woff'), + url('#{$font-path}Lato-BlackItalic.ttf') format('truetype'); + font-weight: 900; + font-style: italic; +} + +@font-face { + font-family: 'Lato Hairline'; + src: local('Lato-HairlineItalic'), + url('#{$font-path}Lato-HairlineItalic.woff2') format('woff2'), + url('#{$font-path}Lato-HairlineItalic.woff') format('woff'), + url('#{$font-path}Lato-HairlineItalic.ttf') format('truetype'); + font-weight: 300; + font-style: italic; +} + +@font-face { + font-family: 'Lato'; + src: local('Lato-Light'), + url('#{$font-path}Lato-Light.woff2') format('woff2'), + url('#{$font-path}Lato-Light.woff') format('woff'), + url('#{$font-path}Lato-Light.ttf') format('truetype'); + font-weight: 300; + font-style: normal; +} + +@font-face { + font-family: 'Lato'; + src: local('Lato Black'), local('Lato-Black'), + url('#{$font-path}Lato-Black.woff2') format('woff2'), + url('#{$font-path}Lato-Black.woff') format('woff'), + url('#{$font-path}Lato-Black.ttf') format('truetype'); + font-weight: 900; + font-style: normal; +} + +@font-face { + font-family: 'Lato'; + src: local('Lato-LightItalic'), + url('#{$font-path}Lato-LightItalic.woff2') format('woff2'), + url('#{$font-path}Lato-LightItalic.woff') format('woff'), + url('#{$font-path}Lato-LightItalic.ttf') format('truetype'); + font-weight: 300; + font-style: italic; +} + +@font-face { + font-family: 'Lato'; + src: local('Lato Italic'), local('Lato-Italic'), + url('#{$font-path}Lato-Italic.woff2') format('woff2'), + url('#{$font-path}Lato-Italic.woff') format('woff'), + url('#{$font-path}Lato-Italic.ttf') format('truetype'); + font-weight: normal; + font-style: italic; +} + +@font-face { + font-family: 'Lato Hairline'; + src: local('Lato-Hairline'), + url('#{$font-path}Lato-Hairline.woff2') format('woff2'), + url('#{$font-path}Lato-Hairline.woff') format('woff'), + url('#{$font-path}Lato-Hairline.ttf') format('truetype'); + font-weight: 300; + font-style: normal; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans ExtraBold Italic'), local('FiraSans-ExtraBoldItalic'), + url('#{$font-path}FiraSans-ExtraBoldItalic.woff2') format('woff2'), + url('#{$font-path}FiraSans-ExtraBoldItalic.woff') format('woff'), + url('#{$font-path}FiraSans-ExtraBoldItalic.ttf') format('truetype'); + font-weight: 800; + font-style: italic; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Medium Italic'), local('FiraSans-MediumItalic'), + url('#{$font-path}FiraSans-MediumItalic.woff2') format('woff2'), + url('#{$font-path}FiraSans-MediumItalic.woff') format('woff'), + url('#{$font-path}FiraSans-MediumItalic.ttf') format('truetype'); + font-weight: 500; + font-style: italic; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans SemiBold'), local('FiraSans-SemiBold'), + url('#{$font-path}FiraSans-SemiBold.woff2') format('woff2'), + url('#{$font-path}FiraSans-SemiBold.woff') format('woff'), + url('#{$font-path}FiraSans-SemiBold.ttf') format('truetype'); + font-weight: 600; + font-style: normal; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Light Italic'), local('FiraSans-LightItalic'), + url('#{$font-path}FiraSans-LightItalic.woff2') format('woff2'), + url('#{$font-path}FiraSans-LightItalic.woff') format('woff'), + url('#{$font-path}FiraSans-LightItalic.ttf') format('truetype'); + font-weight: 300; + font-style: italic; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Bold Italic'), local('FiraSans-BoldItalic'), + url('#{$font-path}FiraSans-BoldItalic.woff2') format('woff2'), + url('#{$font-path}FiraSans-BoldItalic.woff') format('woff'), + url('#{$font-path}FiraSans-BoldItalic.ttf') format('truetype'); + font-weight: bold; + font-style: italic; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans SemiBold Italic'), local('FiraSans-SemiBoldItalic'), + url('#{$font-path}FiraSans-SemiBoldItalic.woff2') format('woff2'), + url('#{$font-path}FiraSans-SemiBoldItalic.woff') format('woff'), + url('#{$font-path}FiraSans-SemiBoldItalic.ttf') format('truetype'); + font-weight: 600; + font-style: italic; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans ExtraBold'), local('FiraSans-ExtraBold'), + url('#{$font-path}FiraSans-ExtraBold.woff2') format('woff2'), + url('#{$font-path}FiraSans-ExtraBold.woff') format('woff'), + url('#{$font-path}FiraSans-ExtraBold.ttf') format('truetype'); + font-weight: 800; + font-style: normal; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Medium'), local('FiraSans-Medium'), + url('#{$font-path}FiraSans-Medium.woff2') format('woff2'), + url('#{$font-path}FiraSans-Medium.woff') format('woff'), + url('#{$font-path}FiraSans-Medium.ttf') format('truetype'); + font-weight: 500; + font-style: normal; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Thin'), local('FiraSans-Thin'), + url('#{$font-path}FiraSans-Thin.woff2') format('woff2'), + url('#{$font-path}FiraSans-Thin.woff') format('woff'), + url('#{$font-path}FiraSans-Thin.ttf') format('truetype'); + font-weight: 100; + font-style: normal; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Black Italic'), local('FiraSans-BlackItalic'), + url('#{$font-path}FiraSans-BlackItalic.woff2') format('woff2'), + url('#{$font-path}FiraSans-BlackItalic.woff') format('woff'), + url('#{$font-path}FiraSans-BlackItalic.ttf') format('truetype'); + font-weight: 900; + font-style: italic; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Bold'), local('FiraSans-Bold'), + url('#{$font-path}FiraSans-Bold.woff2') format('woff2'), + url('#{$font-path}FiraSans-Bold.woff') format('woff'), + url('#{$font-path}FiraSans-Bold.ttf') format('truetype'); + font-weight: bold; + font-style: normal; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Light'), local('FiraSans-Light'), + url('#{$font-path}FiraSans-Light.woff2') format('woff2'), + url('#{$font-path}FiraSans-Light.woff') format('woff'), + url('#{$font-path}FiraSans-Light.ttf') format('truetype'); + font-weight: 300; + font-style: normal; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Thin Italic'), local('FiraSans-ThinItalic'), + url('#{$font-path}FiraSans-ThinItalic.woff2') format('woff2'), + url('#{$font-path}FiraSans-ThinItalic.woff') format('woff'), + url('#{$font-path}FiraSans-ThinItalic.ttf') format('truetype'); + font-weight: 100; + font-style: italic; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans ExtraLight Italic'), local('FiraSans-ExtraLightItalic'), + url('#{$font-path}FiraSans-ExtraLightItalic.woff2') format('woff2'), + url('#{$font-path}FiraSans-ExtraLightItalic.woff') format('woff'), + url('#{$font-path}FiraSans-ExtraLightItalic.ttf') format('truetype'); + font-weight: 200; + font-style: italic; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Black'), local('FiraSans-Black'), + url('#{$font-path}FiraSans-Black.woff2') format('woff2'), + url('#{$font-path}FiraSans-Black.woff') format('woff'), + url('#{$font-path}FiraSans-Black.ttf') format('truetype'); + font-weight: 900; + font-style: normal; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans ExtraLight'), local('FiraSans-ExtraLight'), + url('#{$font-path}FiraSans-ExtraLight.woff2') format('woff2'), + url('#{$font-path}FiraSans-ExtraLight.woff') format('woff'), + url('#{$font-path}FiraSans-ExtraLight.ttf') format('truetype'); + font-weight: 200; + font-style: normal; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Italic'), local('FiraSans-Italic'), + url('#{$font-path}FiraSans-Italic.woff2') format('woff2'), + url('#{$font-path}FiraSans-Italic.woff') format('woff'), + url('#{$font-path}FiraSans-Italic.ttf') format('truetype'); + font-weight: normal; + font-style: italic; +} + +@font-face { + font-family: 'Fira Sans'; + src: local('Fira Sans Regular'), local('FiraSans-Regular'), + url('#{$font-path}FiraSans-Regular.woff2') format('woff2'), + url('#{$font-path}FiraSans-Regular.woff') format('woff'), + url('#{$font-path}FiraSans-Regular.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} diff --git a/lib/web/frontend/src/style/index.scss b/lib/web/frontend/src/style/index.scss new file mode 100644 index 0000000..5bec24d --- /dev/null +++ b/lib/web/frontend/src/style/index.scss @@ -0,0 +1,16 @@ +@import '../../node_modules/@csstools/normalize.css/normalize'; +@import '../../node_modules/prismjs/themes/prism'; + +*, +*::before, +*::after { + // UGH + box-sizing: border-box; +} + +@import './settings'; +@import './fonts'; +@import './typography'; +@import './media'; +@import './base-layout'; +@import './post-layout'; diff --git a/lib/web/frontend/src/style/media.scss b/lib/web/frontend/src/style/media.scss new file mode 100644 index 0000000..f526966 --- /dev/null +++ b/lib/web/frontend/src/style/media.scss @@ -0,0 +1,9 @@ +figure { + text-align: center; + margin-left: 0; +} + +figcaption { + font-style: italic; + text-align: center; +} diff --git a/lib/web/frontend/src/style/post-layout.scss b/lib/web/frontend/src/style/post-layout.scss new file mode 100644 index 0000000..729d3a9 --- /dev/null +++ b/lib/web/frontend/src/style/post-layout.scss @@ -0,0 +1,25 @@ +.post { + header { + border-bottom: 1px solid lighten($accent-background, 50%); + } + + h1 { + margin: 20px 0 10px; + } + + .post-meta { + margin: 0 0 3px; + + color: lighten($base-text, 30%); + font-style: italic; + font-size: 90%; + + a { + display: inline-block; + padding: 2px; + color: $accent-background; + background-color: $accent-link; + border-radius: 2px; + } + } +} diff --git a/lib/web/frontend/src/style/settings.scss b/lib/web/frontend/src/style/settings.scss new file mode 100644 index 0000000..a8fe5fc --- /dev/null +++ b/lib/web/frontend/src/style/settings.scss @@ -0,0 +1,44 @@ +// Reasonable estimate for the size of the scrollbar +$scrollbar-width: 30px; + +// Custom sizes for media queries +$small: 480px; +$medium: 768px; +$large: 960px; +$extra-large: 1100px; + +// Custom media queries +$sm: '(min-width: #{$small})'; +$md: '(min-width: #{$medium})'; +$lg: '(min-width: #{$large})'; +$xl: '(min-width: #{$extra-large})'; + +// +// Color Palette + +$background-color: #fff; + +// Accent color for differentiating special elements or layout "pop" +$accent-text: #fcfcfe; +$accent-headings: $accent-text; +$accent-background: #3d4f5d; +$accent-link: lighten(#becacd, 15%); +$accent-link-visited: $accent-link; +$blog-name-color: #3d92c9; + +// Basic colors, the usual colors where others aren't appropriate +$base-text: #000; +$base-headings: $base-text; +$base-background: $background-color; +$base-link: lighten($accent-background, 10%); +$base-link-visited: $base-link; + +// +// Layout + +// Max width of content column +$content-max-width: 1000px; + +// Min padding to edges of screen that should be maintained +$min-edge-padding: 10px; + diff --git a/lib/web/frontend/src/style/typography.scss b/lib/web/frontend/src/style/typography.scss new file mode 100644 index 0000000..32c933f --- /dev/null +++ b/lib/web/frontend/src/style/typography.scss @@ -0,0 +1,133 @@ +// Font size for small devices +$font-size: 16; + +// Font size for big devices +$font-size-secondary: 18; + +// Font families +$font-headings: 'Lato', 'Helvetica', 'Arial', sans-serif; +$font-body: 'Fira Sans', 'Arial', sans-serif; +$font-mono: 'Consolas', monospace; + +// Type scale +$h1-size: 3.000rem; +$h2-size: 2.369rem; +$h3-size: 1.777rem; +$h4-size: 1.333rem; +$h5-size: 1.222rem; +$h6-size: 1.111rem; + +// Make headings smaller on smaller screen sizes +@mixin smallinize-heading($orig-size) { + font-size: max($orig-size * 0.65, 1rem); + + @media #{$sm} { + font-size: max($orig-size * 0.85, 1rem); + } + + @media #{$md} { + font-size: $orig-size; + } +} + +// Make text a teeny bit smaller on smaller screen sizes +@mixin smallinize-text($orig-size) { + font-size: ($orig-size * 0.9); + + @media #{$sm} { + font-size: ($orig-size * 0.95); + } + + @media #{$md} { + font-size: $orig-size; + } +} + +body { + background-color: $base-background; + font-family: $font-body; + font-weight: 400; + line-height: 1.45; + color: $base-text; + + @include smallinize-text(1em); +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: $font-headings; +} + +p { + margin-bottom: 1.3em; + + // No top margin if paragraph is first element in container. + &:first-child { + margin-top: 0; + } +} + +h1, +h2, +h3, +h4 { + margin: 1.414em 0 0.5em; + font-weight: inherit; + line-height: 1.2; +} + +h1 { + @include smallinize-heading($h1-size); + margin-top: 0.5em; +} + +h2 { + @include smallinize-heading($h2-size); +} + +h3 { + @include smallinize-heading($h3-size); +} + +h4 { + @include smallinize-heading($h4-size); +} + +h5 { + @include smallinize-heading($h5-size); +} + +h6 { + @include smallinize-heading($h6-size); +} + +small, +.font-small { + font-size: 0.75em; +} + +.text-center { + text-align: center; +} + +a { + color: $base-link; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + + &:visited { + color: $base-link-visited; + } +} + +code { + background-color: darken($background-color, 5%); + white-space: pre-wrap; +} diff --git a/lib/web/frontend/src/tsconfig.json b/lib/web/frontend/src/tsconfig.json new file mode 100644 index 0000000..149993e --- /dev/null +++ b/lib/web/frontend/src/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES5", + "jsx": "react", + "importHelpers": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true + } +} \ No newline at end of file diff --git a/lib/web/frontend/yarn.lock b/lib/web/frontend/yarn.lock new file mode 100644 index 0000000..14e7a1b --- /dev/null +++ b/lib/web/frontend/yarn.lock @@ -0,0 +1,2849 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@csstools/normalize.css@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-9.0.1.tgz#c27b391d8457d1e893f1eddeaf5e5412d12ffbb5" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + +acorn-jsx@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-4.1.1.tgz#e8e41e48ea2fe0c896740610ab6a4ffd8add225e" + dependencies: + acorn "^5.0.3" + +acorn@^5.0.3, acorn@^5.1.2: + version "5.7.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.2.tgz#91fa871883485d06708800318404e72bfb26dcc5" + +ajax-request@^1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/ajax-request/-/ajax-request-1.2.3.tgz#99fcbec1d6d2792f85fa949535332bd14f5f3790" + dependencies: + file-system "^2.1.1" + utils-extend "^1.0.7" + +ajv@^5.1.0, ajv@^5.3.0: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + +ansi-escapes@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +ansi@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21" + +anymatch@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + dependencies: + micromatch "^2.1.5" + normalize-path "^2.0.0" + +app-root-path@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-1.4.0.tgz#6335d865c9640d0fad99004e5a79232238e92dfa" + +app-root-path@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.1.0.tgz#98bf6599327ecea199309866e8140368fd2e646a" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async-foreach@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +atob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.6.0, aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base64-img@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/base64-img/-/base64-img-1.0.4.tgz#3e22d55d6c74a24553d840d2b1bc12a7db078d35" + dependencies: + ajax-request "^1.2.0" + file-system "^2.1.0" + +base64-js@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + dependencies: + tweetnacl "^0.14.3" + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +body-parser@1.18.2: + version "1.18.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.1" + http-errors "~1.6.2" + iconv-lite "0.4.19" + on-finished "~2.3.0" + qs "6.5.1" + raw-body "2.3.2" + type-is "~1.6.15" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +bs-platform@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-4.0.5.tgz#d4fd9bbdd11765af5b75110a5655065ece05eed6" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +chain-able@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chain-able/-/chain-able-1.0.1.tgz#b48ac9bdc18f2192ec730abc66609f90aab5605f" + +chain-able@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chain-able/-/chain-able-3.0.0.tgz#dcffe8b04f3da210941a23843bc1332bb288ca9f" + +chalk@^1.1.1: + version "1.1.3" + resolved "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + +chokidar@^1.6.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +chownr@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clean-css@^4.1.9: + version "4.2.1" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" + dependencies: + source-map "~0.6.0" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + +clipboard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.1.tgz#a12481e1c13d8a50f5f036b0560fe5d16d74e46a" + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +combined-stream@1.0.6, combined-stream@~1.0.5, combined-stream@~1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" + dependencies: + delayed-stream "~1.0.0" + +commander@~2.17.1: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cross-spawn@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +decamelize@^1.1.1, decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +depd@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" + +depd@~1.1.1, depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + dependencies: + is-arrayish "^0.2.1" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escodegen@^1.8.1: + version "1.11.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + +estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + +eventemitter3@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" + +exec-sh@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.2.tgz#2a5e7ffcbd7d0ba2755bdecb16e5a427dfbdec36" + dependencies: + merge "^1.2.0" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +express@^4.14.0: + version "4.16.3" + resolved "http://registry.npmjs.org/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" + dependencies: + accepts "~1.3.5" + array-flatten "1.1.1" + body-parser "1.18.2" + content-disposition "0.5.2" + content-type "~1.0.4" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.3" + qs "6.5.1" + range-parser "~1.2.0" + safe-buffer "5.1.1" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.1, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + +external-editor@^2.0.4: + version "2.2.0" + resolved "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fast-deep-equal@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + +file-match@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/file-match/-/file-match-1.0.2.tgz#c9cad265d2c8adf3a81475b0df475859069faef7" + dependencies: + utils-extend "^1.0.6" + +file-system@^2.1.0, file-system@^2.1.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/file-system/-/file-system-2.2.2.tgz#7d65833e3a2347dcd956a813c677153ed3edd987" + dependencies: + file-match "^1.0.1" + utils-extend "^1.0.4" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +finalhandler@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.4.0" + unpipe "~1.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +fliplog@^0.3.13: + version "0.3.13" + resolved "https://registry.yarnpkg.com/fliplog/-/fliplog-0.3.13.tgz#dd0d786e821822aae272e0ddc84012596a96154c" + dependencies: + chain-able "^1.0.1" + +follow-redirects@^1.0.0: + version "1.5.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.7.tgz#a39e4804dacb90202bca76a9e2ac10433ca6a69a" + dependencies: + debug "^3.1.0" + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.3.1, form-data@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" + dependencies: + asynckit "^0.4.0" + combined-stream "1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + +fs-extra@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.0.tgz#8cc3f47ce07ef7b3593a11b9fb245f7e34c041d6" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + dependencies: + minipass "^2.2.1" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" + dependencies: + nan "^2.9.2" + node-pre-gyp "^0.10.0" + +fstream@^1.0.0, fstream@^1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +fuse-box@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/fuse-box/-/fuse-box-3.5.0.tgz#4b7bdb57a33a84e4e271e1d3e73009d3cfd01907" + dependencies: + acorn "^5.1.2" + acorn-jsx "^4.0.1" + ansi "^0.3.1" + app-root-path "^2.0.1" + base64-img "^1.0.3" + base64-js "^1.2.0" + chokidar "^1.6.1" + clean-css "^4.1.9" + escodegen "^1.8.1" + express "^4.14.0" + fliplog "^0.3.13" + fs-extra "^7.0.0" + fuse-concat-with-sourcemaps "^1.0.5" + getopts "^2.1.1" + glob "^7.1.1" + ieee754 "^1.1.8" + inquirer "^3.0.6" + lego-api "^1.0.7" + mustache "^2.3.0" + postcss "^6.0.1" + pretty-time "^0.2.0" + prettysize "0.0.3" + realm-utils "^1.0.9" + regexpu-core "^4.1.3" + request "^2.79.0" + shorthash "0.0.2" + source-map "^0.7.1" + tslib "^1.8.0" + watch "^1.0.1" + ws "^1.1.1" + +fuse-concat-with-sourcemaps@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fuse-concat-with-sourcemaps/-/fuse-concat-with-sourcemaps-1.0.5.tgz#9c6a521f675cff5cdbb48db1ca9c181ae49a7b97" + dependencies: + source-map "^0.6.1" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gaze@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" + dependencies: + globule "^1.0.0" + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + +getopts@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.2.1.tgz#68120d77abf420e1ade52291977ce050f33ce54e" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globule@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" + dependencies: + glob "~7.1.1" + lodash "~4.17.10" + minimatch "~3.0.2" + +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + dependencies: + delegate "^3.1.2" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + dependencies: + ajv "^5.1.0" + har-schema "^2.0.0" + +har-validator@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29" + dependencies: + ajv "^5.3.0" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +hosted-git-info@^2.1.4: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + +http-errors@1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" + dependencies: + depd "1.1.1" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-proxy-middleware@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.0.tgz#40992b5901dc44bc7bc3795da81b0b248eca02d8" + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.10" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" + dependencies: + eventemitter3 "^3.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +iconv-lite@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + +iconv-lite@^0.4.17, iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.8: + version "1.1.12" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + dependencies: + minimatch "^3.0.4" + +in-publish@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + dependencies: + repeating "^2.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +inquirer@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.4" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +ipaddr.js@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + +is-number@^2.0.2, is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +js-base64@^2.1.8: + version "2.4.9" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.9.tgz#748911fb04f48a60c4771b375cac45a80df11c03" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +lego-api@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/lego-api/-/lego-api-1.0.8.tgz#5e26be726c5e11d540f89e7c6b1abf8c5834bd01" + dependencies: + chain-able "^3.0.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + +lodash.clonedeep@^4.3.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + +lodash.mergewith@^4.6.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" + +lodash@^4.0.0, lodash@^4.17.10, lodash@^4.3.0, lodash@~4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lru-cache@^4.0.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + +math-random@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +meow@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +merge@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +micromatch@^2.1.5: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +micromatch@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +mime-db@~1.36.0: + version "1.36.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.19: + version "2.1.20" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" + dependencies: + mime-db "~1.36.0" + +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minipass@^2.2.1, minipass@^2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.4.tgz#4768d7605ed6194d6d576169b9e12ef71e9d9957" + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" + dependencies: + minipass "^2.2.1" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.1" + resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +mustache@^2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-2.3.2.tgz#a6d4d9c3f91d13359ab889a812954f9230a3d0c5" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + +nan@^2.10.0, nan@^2.9.2: + version "2.11.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099" + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +nanoseconds@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/nanoseconds/-/nanoseconds-0.1.0.tgz#69ec39fcd00e77ab3a72de0a43342824cd79233a" + +needle@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.2.tgz#1120ca4c41f2fcc6976fd28a8968afe239929418" + dependencies: + debug "^2.1.2" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + +node-gyp@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "^2.87.0" + rimraf "2" + semver "~5.3.0" + tar "^2.0.0" + which "1" + +node-pre-gyp@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +node-sass@^4.9.3: + version "4.9.3" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.3.tgz#f407cf3d66f78308bb1e346b24fa428703196224" + dependencies: + async-foreach "^0.1.3" + chalk "^1.1.1" + cross-spawn "^3.0.0" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + in-publish "^2.0.0" + lodash.assign "^4.2.0" + lodash.clonedeep "^4.3.2" + lodash.mergewith "^4.6.0" + meow "^3.7.0" + mkdirp "^0.5.1" + nan "^2.10.0" + node-gyp "^3.8.0" + npmlog "^4.0.0" + request "2.87.0" + sass-graph "^2.2.4" + stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" + +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.0, normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +npm-bundled@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979" + +npm-packlist@^1.1.6: + version "1.1.11" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de" + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +options@>=0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@0, osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + +postcss@^6.0.1: + version "6.0.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +pretty-time@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-0.2.0.tgz#7a3bdec4049c620cd7c42b7f342b74d56e73d74e" + dependencies: + is-number "^2.0.2" + nanoseconds "^0.1.0" + +prettysize@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/prettysize/-/prettysize-0.0.3.tgz#14afff6a645e591a4ddf1c72919c23b4146181a1" + +prismjs@^1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.15.0.tgz#8801d332e472091ba8def94976c8877ad60398d9" + optionalDependencies: + clipboard "^2.0.0" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +proxy-addr@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.8.0" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +psl@^1.1.24: + version "1.1.29" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +qs@6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + +qs@~6.5.1, qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + +randomatic@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.0.tgz#36f2ca708e9e567f5ed2ec01949026d50aa10116" + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + +range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +raw-body@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + dependencies: + bytes "3.0.0" + http-errors "1.6.2" + iconv-lite "0.4.19" + unpipe "1.0.0" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +realm-utils@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/realm-utils/-/realm-utils-1.0.9.tgz#5c76a5ff39e4816af2c133a161f4221d6628eff4" + dependencies: + app-root-path "^1.3.0" + mkdirp "^0.5.1" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +regenerate-unicode-properties@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c" + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + dependencies: + is-equal-shallow "^0.1.3" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpu-core@^4.1.3: + version "4.2.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.2.0.tgz#a3744fa03806cffe146dea4421a3e73bdcc47b1d" + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^7.0.0" + regjsgen "^0.4.0" + regjsparser "^0.3.0" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.0.2" + +regjsgen@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.4.0.tgz#c1eb4c89a209263f8717c782591523913ede2561" + +regjsparser@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.3.0.tgz#3c326da7fcfd69fa0d332575a41c8c0cdf588c96" + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request@2.87.0: + version "2.87.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + +request@^2.79.0, request@^2.87.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + +rimraf@2, rimraf@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + +safe-buffer@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +sass-graph@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" + dependencies: + glob "^7.0.0" + lodash "^4.0.0" + scss-tokenizer "^0.2.3" + yargs "^7.0.0" + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +scss-tokenizer@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" + dependencies: + js-base64 "^2.1.8" + source-map "^0.4.2" + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0: + version "5.5.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" + +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + +serve-static@1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.2" + send "0.16.2" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + +shorthash@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/shorthash/-/shorthash-0.0.2.tgz#59b268eecbde59038b30da202bcfbddeb2c4a4eb" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +source-map@^0.7.1: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + +spdx-correct@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + +sshpk@^1.7.0: + version "1.14.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + safer-buffer "^2.0.2" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + +stdout-stream@^1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de" + dependencies: + readable-stream "^2.0.1" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^5.3.0, supports-color@^5.4.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + dependencies: + has-flag "^3.0.0" + +tar@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +tar@^4: + version "4.4.6" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b" + dependencies: + chownr "^1.0.1" + fs-minipass "^1.2.5" + minipass "^2.3.3" + minizlib "^1.1.0" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +tiny-emitter@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tough-cookie@~2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" + dependencies: + punycode "^1.4.1" + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + +"true-case-path@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d" + dependencies: + glob "^7.1.2" + +tslib@^1.8.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-is@~1.6.15, type-is@~1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.18" + +typescript@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.3.tgz#4853b3e275ecdaa27f78fda46dc273a7eb7fc1c8" + +uglify-js@^3.4.9: + version "3.4.9" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" + dependencies: + commander "~2.17.1" + source-map "~0.6.1" + +ultron@1.0.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz#9f1dc76926d6ccf452310564fd834ace059663d4" + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz#5a533f31b4317ea76f17d807fa0d116546111dd0" + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +utils-extend@^1.0.4, utils-extend@^1.0.6, utils-extend@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/utils-extend/-/utils-extend-1.0.8.tgz#ccfd7b64540f8e90ee21eec57769d0651cab8a5f" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + +uuid@^3.1.0, uuid@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +watch@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/watch/-/watch-1.0.2.tgz#340a717bde765726fa0aa07d721e0147a551df0c" + dependencies: + exec-sh "^0.2.0" + minimist "^1.2.0" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + +which@1, which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + dependencies: + string-width "^1.0.2 || 2" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +ws@^1.1.1: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" + dependencies: + options ">=0.0.5" + ultron "1.0.x" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" + +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + dependencies: + camelcase "^3.0.0" + +yargs@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^5.0.0" diff --git a/lib/web/middleware/request_time.ex b/lib/web/middleware/request_time.ex new file mode 100644 index 0000000..58dbb8a --- /dev/null +++ b/lib/web/middleware/request_time.ex @@ -0,0 +1,61 @@ +defmodule Mebe2.Web.Middleware.RequestTime do + require Logger + + @timer_key :mebe2_request_started + + defmacro __using__(_opts) do + quote do + @before_compile unquote(__MODULE__) + end + end + + defmacro __before_compile__(_env) do + quote do + defoverridable Raxx.Server + + @impl Raxx.Server + def handle_head(head, config) do + unquote(__MODULE__).put_response_timer() + super(head, config) + end + + @impl Raxx.Server + def handle_data(data, config) do + unquote(__MODULE__).put_response_timer() + super(data, config) + end + + @impl Raxx.Server + def handle_tail(tail, config) do + unquote(__MODULE__).put_response_timer() + super(tail, config) + end + + @impl Raxx.Server + def handle_info(message, config) do + unquote(__MODULE__).put_response_timer() + super(message, config) + end + end + end + + @spec put_response_timer() :: :ok + def put_response_timer() do + Process.put(@timer_key, get_time()) + :ok + end + + @spec get() :: String.t() + def get() do + old_time = Process.get(@timer_key) + diff = get_time() - old_time + + cond do + diff >= 1_000_000 -> "#{Float.round(diff / 1_000_000, 2)} s" + diff >= 1_000 -> "#{Float.round(diff / 1_000, 2)} ms" + true -> "#{diff} µs" + end + end + + defp get_time(), do: :erlang.monotonic_time(:micro_seconds) +end diff --git a/lib/web/router.ex b/lib/web/router.ex new file mode 100644 index 0000000..2cb2129 --- /dev/null +++ b/lib/web/router.ex @@ -0,0 +1,12 @@ +defmodule Mebe2.Web.Router do + use Ace.HTTP.Service, port: 2142, cleartext: true + use Raxx.Logger + use Mebe2.Web.Middleware.RequestTime + + use Raxx.Router, [ + {%{method: :GET, path: [_year, _month, _day, _slug]}, Mebe2.Web.Routes.Post}, + {%{method: :GET, path: [_slug]}, Mebe2.Web.Routes.Page}, + {%{method: :GET, path: []}, Mebe2.Web.Routes.Index}, + {_, Mebe2.Web.Routes.NotFound} + ] +end diff --git a/lib/web/routes/index.ex b/lib/web/routes/index.ex new file mode 100644 index 0000000..e9d98d8 --- /dev/null +++ b/lib/web/routes/index.ex @@ -0,0 +1,12 @@ +defmodule Mebe2.Web.Routes.Index do + use Raxx.Server + alias Mebe2.Engine.DB + + @impl Raxx.Server + def handle_request(%Raxx.Request{} = _req, _state) do + posts = DB.get_reg_posts(0, Mebe2.get_conf(:posts_per_page)) + + response(200) + |> Mebe2.Web.Views.Index.render(posts) + end +end diff --git a/lib/web/routes/not_found.ex b/lib/web/routes/not_found.ex new file mode 100644 index 0000000..e4355e7 --- /dev/null +++ b/lib/web/routes/not_found.ex @@ -0,0 +1,9 @@ +defmodule Mebe2.Web.Routes.NotFound do + use Raxx.Server + + @impl Raxx.Server + def handle_request(_req, _state) do + response(404) + |> Mebe2.Web.Views.NotFound.render() + end +end diff --git a/lib/web/routes/page.ex b/lib/web/routes/page.ex new file mode 100644 index 0000000..4220930 --- /dev/null +++ b/lib/web/routes/page.ex @@ -0,0 +1,14 @@ +defmodule Mebe2.Web.Routes.Page do + use Raxx.Server + alias Mebe2.Engine.{DB, Models} + + @impl Raxx.Server + def handle_request(%Raxx.Request{path: [slug]} = req, state) do + with {:page, %Models.Page{} = page} <- {:page, DB.get_page(slug)} do + response(200) + |> Mebe2.Web.Views.Page.render(page) + else + _ -> Mebe2.Web.Routes.NotFound.handle_request(req, state) + end + end +end diff --git a/lib/web/routes/post.ex b/lib/web/routes/post.ex new file mode 100644 index 0000000..9101575 --- /dev/null +++ b/lib/web/routes/post.ex @@ -0,0 +1,17 @@ +defmodule Mebe2.Web.Routes.Post do + use Raxx.Server + alias Mebe2.Engine.{DB, Models} + + @impl Raxx.Server + def handle_request(%Raxx.Request{path: [y_str, m_str, d_str, slug]} = req, state) do + with {:year, {year, ""}} <- {:year, Integer.parse(y_str)}, + {:month, {month, ""}} <- {:month, Integer.parse(m_str)}, + {:day, {day, ""}} <- {:day, Integer.parse(d_str)}, + {:post, %Models.Post{} = post} <- {:post, DB.get_post(year, month, day, slug)} do + response(200) + |> Mebe2.Web.Views.SinglePost.render(post) + else + _ -> Mebe2.Web.Routes.NotFound.handle_request(req, state) + end + end +end diff --git a/lib/web/views/base_layout.ex b/lib/web/views/base_layout.ex new file mode 100644 index 0000000..2dbaea6 --- /dev/null +++ b/lib/web/views/base_layout.ex @@ -0,0 +1,4 @@ +defmodule Mebe2.Web.Views.BaseLayout do + use Raxx.Layout, + layout: "base_layout.html.eex" +end diff --git a/lib/web/views/base_layout.html.eex b/lib/web/views/base_layout.html.eex new file mode 100644 index 0000000..ab7e29f --- /dev/null +++ b/lib/web/views/base_layout.html.eex @@ -0,0 +1,43 @@ + + + + + <%= Mebe2.get_conf(:blog_name) %> + + +
+ +

<%= Mebe2.get_conf(:blog_name) %>

+
+ +
+
+ <%= __content__ %> +
+ + + <%= raw(Mebe2.get_conf(:extra_html)) %> + + diff --git a/lib/web/views/comments.ex b/lib/web/views/comments.ex new file mode 100644 index 0000000..7c17c2d --- /dev/null +++ b/lib/web/views/comments.ex @@ -0,0 +1,5 @@ +defmodule Mebe2.Web.Views.Comments do + use Raxx.View, + template: "comments.html.eex", + arguments: [:title] +end diff --git a/lib/web/views/comments.html.eex b/lib/web/views/comments.html.eex new file mode 100644 index 0000000..a8ebbe1 --- /dev/null +++ b/lib/web/views/comments.html.eex @@ -0,0 +1,16 @@ +
+ + diff --git a/lib/web/views/index.ex b/lib/web/views/index.ex new file mode 100644 index 0000000..41aeb90 --- /dev/null +++ b/lib/web/views/index.ex @@ -0,0 +1,5 @@ +defmodule Mebe2.Web.Views.Index do + use Mebe2.Web.Views.BaseLayout, + template: "index.html.eex", + arguments: [:posts] +end diff --git a/lib/web/views/index.html.eex b/lib/web/views/index.html.eex new file mode 100644 index 0000000..e5c4f82 --- /dev/null +++ b/lib/web/views/index.html.eex @@ -0,0 +1 @@ +<%= Mebe2.Web.Views.PostList.html(posts) %> diff --git a/lib/web/views/not_found.ex b/lib/web/views/not_found.ex new file mode 100644 index 0000000..af94c01 --- /dev/null +++ b/lib/web/views/not_found.ex @@ -0,0 +1,5 @@ +defmodule Mebe2.Web.Views.NotFound do + use Mebe2.Web.Views.BaseLayout, + template: "not_found.html.eex", + arguments: [] +end diff --git a/lib/web/views/not_found.html.eex b/lib/web/views/not_found.html.eex new file mode 100644 index 0000000..679aadb --- /dev/null +++ b/lib/web/views/not_found.html.eex @@ -0,0 +1 @@ +NOT FOUND diff --git a/lib/web/views/page.ex b/lib/web/views/page.ex new file mode 100644 index 0000000..541c5f6 --- /dev/null +++ b/lib/web/views/page.ex @@ -0,0 +1,5 @@ +defmodule Mebe2.Web.Views.Page do + use Mebe2.Web.Views.BaseLayout, + template: "page.html.eex", + arguments: [:page] +end diff --git a/lib/web/views/page.html.eex b/lib/web/views/page.html.eex new file mode 100644 index 0000000..7b907ae --- /dev/null +++ b/lib/web/views/page.html.eex @@ -0,0 +1,5 @@ +
+

<%= page.title %>

+ + <%= raw(page.content) %> +
diff --git a/lib/web/views/post.ex b/lib/web/views/post.ex new file mode 100644 index 0000000..e192f2b --- /dev/null +++ b/lib/web/views/post.ex @@ -0,0 +1,21 @@ +defmodule Mebe2.Web.Views.Post do + use Raxx.View, + template: "post.html.eex", + arguments: [:post, :multi] + + @doc """ + Format a datetime for display in the blog. + """ + @spec format_datetime(DateTime.t()) :: String.t() + def format_datetime(%DateTime{} = dt) do + Calendar.Strftime.strftime!(dt, "%e %b %Y, %R %Z") + end + + @doc """ + Format a date for display in the blog. + """ + @spec format_date(DateTime.t()) :: String.t() + def format_date(%DateTime{} = dt) do + Calendar.Strftime.strftime!(dt, "%e %b %Y") + end +end diff --git a/lib/web/views/post.html.eex b/lib/web/views/post.html.eex new file mode 100644 index 0000000..188421d --- /dev/null +++ b/lib/web/views/post.html.eex @@ -0,0 +1,35 @@ +
+ <%= raw(if multi do %> + + <% end) %> + +

<%= post.title %>

+ + <%= raw(if multi do %>
<% end) %> + + + + + <%= raw(if multi && not is_nil(post.short_content) do %> + <%= raw(post.short_content) %> + <% else %> + <%= raw(post.content) %> + <% end) %> + + <%= + raw(if not multi && Mebe2.get_conf(:disqus_comments) do + Mebe2.Web.Views.Comments.html(post.title) + end) + %> +
diff --git a/lib/web/views/post_list.ex b/lib/web/views/post_list.ex new file mode 100644 index 0000000..ab20a28 --- /dev/null +++ b/lib/web/views/post_list.ex @@ -0,0 +1,5 @@ +defmodule Mebe2.Web.Views.PostList do + use Raxx.View, + template: "post_list.html.eex", + arguments: [:posts] +end diff --git a/lib/web/views/post_list.html.eex b/lib/web/views/post_list.html.eex new file mode 100644 index 0000000..ea9a242 --- /dev/null +++ b/lib/web/views/post_list.html.eex @@ -0,0 +1,3 @@ +<%= raw(for post <- posts do %> + <%= Mebe2.Web.Views.Post.html(post, true) %> +<% end) %> diff --git a/lib/web/views/single_post.ex b/lib/web/views/single_post.ex new file mode 100644 index 0000000..460a655 --- /dev/null +++ b/lib/web/views/single_post.ex @@ -0,0 +1,5 @@ +defmodule Mebe2.Web.Views.SinglePost do + use Mebe2.Web.Views.BaseLayout, + template: "single_post.html.eex", + arguments: [:post] +end diff --git a/lib/web/views/single_post.html.eex b/lib/web/views/single_post.html.eex new file mode 100644 index 0000000..bcd938d --- /dev/null +++ b/lib/web/views/single_post.html.eex @@ -0,0 +1 @@ +<%= Mebe2.Web.Views.Post.html(post, false) %> diff --git a/lib/web/views/utils.ex b/lib/web/views/utils.ex new file mode 100644 index 0000000..edb0273 --- /dev/null +++ b/lib/web/views/utils.ex @@ -0,0 +1,20 @@ +defmodule Mebe2.Web.Views.Utils do + @moduledoc """ + Utility functions to use in views. + """ + + @doc """ + Get the relative path to a given post. + + ## Examples + + iex> Mebe2.Web.Views.Utils.get_post_path( + ...> %Mebe2.Engine.Models.Post{datetime: ~N[2010-08-09T10:00:00], slug: "foo-bar"} + ...> ) + "/2010/08/09/foo-bar" + """ + def get_post_path(%Mebe2.Engine.Models.Post{} = post) do + dstr = Calendar.Strftime.strftime!(post.datetime, "%Y/%m/%d") + "/#{dstr}/#{post.slug}" + end +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..45a9713 --- /dev/null +++ b/mix.exs @@ -0,0 +1,34 @@ +defmodule Mebe2.MixProject do + use Mix.Project + + def project do + [ + app: :mebe_2, + version: "0.1.0", + elixir: "~> 1.7", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger], + mod: {Mebe2.Application, []} + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:earmark, "~> 1.2.5"}, + {:raxx, "~> 0.16.1"}, + {:ace, "~> 0.17.1"}, + {:calendar, "~> 0.17.4"}, + {:slugger, "~> 0.3.0"}, + {:mbu, "~> 3.0.0", runtime: false}, + {:exsync, "~> 0.2.3", only: :dev} + ] + end +end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..cd19f17 --- /dev/null +++ b/mix.lock @@ -0,0 +1,29 @@ +%{ + "ace": {:hex, :ace, "0.17.1", "89c6d0940151f194f6572cfeb0e62295d14f344f90f01eeb625dd2b60ab7cd79", [:mix], [{:hpack, "~> 0.2.3", [hex: :hpack_erl, repo: "hexpm", optional: false]}, {:raxx, "~> 0.16.0", [hex: :raxx, repo: "hexpm", optional: false]}], "hexpm"}, + "calendar": {:hex, :calendar, "0.17.4", "22c5e8d98a4db9494396e5727108dffb820ee0d18fed4b0aa8ab76e4f5bc32f1", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, + "certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "cookie": {:hex, :cookie, "0.1.1", "89438362ee0f0ed400e9f076d617d630f82d682e3fbcf767072a46a6e1ed5781", [:mix], [], "hexpm"}, + "cowboy": {:hex, :cowboy, "2.4.0", "f1b72fabe9c8a5fc64ac5ac85fb65474d64733d1df52a26fad5d4ba3d9f70a9f", [:rebar3], [{:cowlib, "~> 2.3.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.5.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, + "cowlib": {:hex, :cowlib, "2.3.0", "bbd58ef537904e4f7c1dd62e6aa8bc831c8183ce4efa9bd1150164fe15be4caa", [:rebar3], [], "hexpm"}, + "earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"}, + "eex_html": {:hex, :eex_html, "0.1.1", "df7ad68245068d5fea0dab2a38e5afdc386ec00a5b6445eac6402ed7aa6a2b12", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, + "exsync": {:hex, :exsync, "0.2.3", "a1ac11b4bd3808706003dbe587902101fcc1387d9fc55e8b10972f13a563dd15", [:mix], [{:file_system, "~> 0.2", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"}, + "file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm"}, + "hackney": {:hex, :hackney, "1.13.0", "24edc8cd2b28e1c652593833862435c80661834f6c9344e84b6a2255e7aeef03", [:rebar3], [{:certifi, "2.3.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.2", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "hpack": {:hex, :hpack_erl, "0.2.3", "17670f83ff984ae6cd74b1c456edde906d27ff013740ee4d9efaa4f1bf999633", [:rebar3], [], "hexpm"}, + "idna": {:hex, :idna, "5.1.2", "e21cb58a09f0228a9e0b95eaa1217f1bcfc31a1aaa6e1fdf2f53a33f7dbd9494", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, + "jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "mbu": {:hex, :mbu, "3.0.0", "a00c3358cfdd5d5872466abd3580f962e86cd87db9554046f9206ac77fcdc70c", [:mix], [{:file_system, "~> 0.2.4", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, + "mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"}, + "plug": {:hex, :plug, "1.6.1", "c62fe7623d035020cf989820b38490460e6903ab7eee29e234b7586e9b6c91d6", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, + "ranch": {:hex, :ranch, "1.5.0", "f04166f456790fee2ac1aa05a02745cc75783c2bfb26d39faf6aefc9a3d3a58a", [:rebar3], [], "hexpm"}, + "raxx": {:hex, :raxx, "0.16.1", "3737b61f198755138bb2c8c61ed22dc06f9c0cf3218320189ddcda02da91d94a", [:mix], [{:cookie, "~> 0.1.0", [hex: :cookie, repo: "hexpm", optional: false]}, {:eex_html, "~> 0.1.1", [hex: :eex_html, repo: "hexpm", optional: false]}], "hexpm"}, + "raxx_static": {:hex, :raxx_static, "0.6.1", "8b48254fc3d1b8b1e473b7c307fbba0ae767c60482754ce823c664544c85d729", [:mix], [{:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:raxx, "~> 0.15.2", [hex: :raxx, repo: "hexpm", optional: false]}], "hexpm"}, + "slugger": {:hex, :slugger, "0.3.0", "efc667ab99eee19a48913ccf3d038b1fb9f165fa4fbf093be898b8099e61b6ed", [:mix], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"}, + "tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"}, +} diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/test/web/views/utils_test.exs b/test/web/views/utils_test.exs new file mode 100644 index 0000000..4df5c75 --- /dev/null +++ b/test/web/views/utils_test.exs @@ -0,0 +1,4 @@ +defmodule Mebe2.Web.Views.UtilsTest do + use ExUnit.Case, async: true + doctest Mebe2.Web.Views.Utils +end