lib/griffin_ssg_old.ex

defmodule GriffinSSGOld do
  @moduledoc """
  This is the documentation for the Griffin project.

  By default, Griffin will scan for content in the `priv/content` directory
  and output generated files to the `_site` directory.

  Griffin depends on the following libraries:

    * [Plug](https://hexdocs.pm/plug) - a specification and conveniences
      for composable modules in between web applications
  """

  use Application

  @doc false
  def start(_type, _args) do
    children = [
      {Plug.Cowboy, scheme: :http, plug: GriffinSSG.Web.Plug, options: [port: 4000]}
    ]

    Supervisor.start_link(children, strategy: :one_for_one, name: Griffin.Supervisor)
  end

  @doc """
  Reads a file from disk and returns
  the parsed frontmatter metadata and the file contents.
  """
  def parse_file(path, _options \\ []) do
    file_content = File.read!(path)

    # split file contents into frontmatter metadata and content
    [frontmatter, content] =
      case String.split(file_content, ~r/\n---\n/, parts: 2) do
        [content] ->
          [nil, content]

        [raw_frontmatter, content] ->
          [parse_frontmatter(raw_frontmatter), parse_content(content)]
      end

    {frontmatter, content}
  end

  @doc """
  Compiles a template file from disk into quoted code
  that can then be used by `GriffinSSG.render/3`.
  """
  def compile_layout(path, _options \\ []) do
    EEx.compile_file(path)
  end

  @doc """
  Renders a layout to file, taking in a number of options that affect the rendered output.

  The following `options` are accepted:

    * `frontmatter`   - the frontmatter attributes
    * `content`       - the content to render in the layout
    * `assigns`       - a map with template variables to be used in the layout
  """
  def render(path, layout, options) do
    frontmatter = Keyword.fetch!(options, :frontmatter)
    content = Keyword.fetch!(options, :content)
    assigns = Keyword.get(options, :assigns, %{})

    flat_assigns =
      frontmatter
      |> Map.put(:content, content)
      |> Map.merge(assigns)
      # here we're compiling all existing partials when we might only need a very small subset.
      # TODO compile only required partials by looking at args in the quoted expression
      |> then(fn current_assigns ->
        Map.update(current_assigns, :partials, %{}, fn partials ->
          partials
          |> Enum.map(fn partial ->
            {compiled, _bindings} = Code.eval_quoted(partial, assigns: current_assigns)
            compiled
          end)
          |> Enum.into(%{})
        end)
      end)
      |> Enum.to_list()

    {result, _bindings} = Code.eval_quoted(layout, assigns: flat_assigns)
    File.write!(path, result)
  end

  @doc false
  def process_file(path) do
    IO.inspect("reading from #{path}")
    content = File.read!(path)
    # string split crashes if the file only contains html and
    # does not contain front matter annotations.
    [yaml, markdown] = String.split(content, ~r/\n---\n/, parts: 2)
    [metadata] = YamlElixir.read_all_from_string!(yaml, atoms: true)
    html = Earmark.as_html!(markdown)
    IO.inspect(html)

    metadata =
      metadata
      |> Map.put("content", html)
      |> Enum.map(fn {k, v} -> {String.to_atom(k), v} end)
      |> IO.inspect(label: "metadata")

    # IO.inspect(html, label: "html")
    rendered = EEx.eval_file("installer/blog/priv/layouts/default.html.eex", assigns: metadata)

    unless html == "" do
      output_path = Path.expand("#{path}.html")
      IO.inspect("writing to #{output_path}")
      File.write!(output_path, rendered)
    end

    :ok
  end

  defp parse_frontmatter(yaml) do
    {:ok, [parsed]} = YamlElixir.read_all_from_string(yaml, atoms: true)

    parsed
    |> Enum.map(fn {k, v} -> {String.to_atom(k), v} end)
    |> Enum.into(%{})
  end

  defp parse_content(content) do
    Earmark.as_html!(content)
  end
end