2023-09-18 18:21:59 +00:00
|
|
|
defmodule PreDotHn do
|
|
|
|
@moduledoc """
|
|
|
|
Documentation for `PreDotHn`.
|
|
|
|
"""
|
|
|
|
|
2023-09-22 12:58:31 +00:00
|
|
|
use Phoenix.Component
|
2023-09-18 18:21:59 +00:00
|
|
|
|
2023-09-22 12:58:31 +00:00
|
|
|
require Logger
|
2023-09-18 18:21:59 +00:00
|
|
|
|
2023-09-22 12:58:31 +00:00
|
|
|
import Phoenix.LiveViewTest, only: [rendered_to_string: 1]
|
2023-09-18 18:21:59 +00:00
|
|
|
|
2023-09-22 12:58:31 +00:00
|
|
|
alias PreDotHn.Frontmatter
|
|
|
|
alias PreDotHn.Markdown
|
|
|
|
|
|
|
|
def read(path) do
|
|
|
|
path
|
|
|
|
|> File.read!()
|
|
|
|
|> Frontmatter.front_matter_split()
|
|
|
|
|> then(fn {frontmatter_text, body_text} ->
|
|
|
|
{Frontmatter.make_frontmatter(path, frontmatter_text), body_text}
|
|
|
|
end)
|
|
|
|
|> then(fn {frontmatter, body_text} ->
|
|
|
|
body = Markdown.render(body_text)
|
|
|
|
|
|
|
|
Map.merge(frontmatter, %{"body" => body, "path" => path})
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
def validate(%{"slug" => nil, "path" => path}) do
|
|
|
|
{:error, "slug missing from #{path}"}
|
|
|
|
end
|
|
|
|
|
|
|
|
def validate(%{"slug" => "", "path" => path}) do
|
|
|
|
{:error, "slug missing from #{path}"}
|
|
|
|
end
|
|
|
|
|
|
|
|
def validate(other), do: {:ok, other}
|
|
|
|
|
|
|
|
def run() do
|
2023-09-23 17:15:30 +00:00
|
|
|
posts =
|
|
|
|
"site/**/*.md"
|
|
|
|
|> Path.wildcard()
|
|
|
|
|> Enum.map(&read/1)
|
|
|
|
|> Enum.map(&validate/1)
|
|
|
|
|> Enum.filter(fn
|
|
|
|
{:error, error} ->
|
|
|
|
Logger.warn(error)
|
|
|
|
|
|
|
|
false
|
|
|
|
|
|
|
|
{:ok, _other} ->
|
|
|
|
true
|
|
|
|
end)
|
|
|
|
|> Enum.map(&elem(&1, 1))
|
|
|
|
|> Enum.sort_by(&Map.get(&1, "date"), :desc)
|
|
|
|
|> Enum.map(&atomize_keys/1)
|
|
|
|
|
|
|
|
index = make_index(posts)
|
|
|
|
|
|
|
|
Enum.map([index | posts], &write_page/1)
|
|
|
|
end
|
2023-09-22 12:58:31 +00:00
|
|
|
|
2023-09-23 17:15:30 +00:00
|
|
|
def atomize_keys(page) do
|
|
|
|
page
|
|
|
|
|> Enum.map(fn {key, value} -> {String.to_atom(key), value} end)
|
|
|
|
|> Enum.into(%{})
|
|
|
|
end
|
|
|
|
|
|
|
|
def make_index(posts) do
|
|
|
|
assigns = %{posts: posts}
|
|
|
|
|
|
|
|
body =
|
|
|
|
~H"""
|
|
|
|
<ul>
|
|
|
|
<%= for post <- @posts do %>
|
|
|
|
<li style="margin-bottom: 1rem">
|
|
|
|
<span class="text-yellow align-top"><%= post.date %></span>
|
|
|
|
<a class="text-ellipsis overflow-hidden" style="max-width: 40ch" href={"/#{post.slug}/"}><%= post.title %></a>
|
|
|
|
</li>
|
|
|
|
<% end %>
|
|
|
|
</ul>
|
|
|
|
"""
|
|
|
|
|> rendered_to_string()
|
|
|
|
|
|
|
|
%{
|
|
|
|
title: "Blog",
|
|
|
|
slug: "blog",
|
|
|
|
date: "2023-09-23",
|
|
|
|
body: body
|
|
|
|
}
|
2023-09-22 12:58:31 +00:00
|
|
|
end
|
|
|
|
|
2023-09-23 17:15:30 +00:00
|
|
|
attr(:title, :string, required: true)
|
|
|
|
attr(:description, :string)
|
2023-09-22 12:58:31 +00:00
|
|
|
slot(:inner_block, required: true)
|
|
|
|
|
|
|
|
def layout(assigns) do
|
|
|
|
~H"""
|
|
|
|
<!doctype html>
|
|
|
|
<html lang="en">
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
|
2023-09-23 17:15:30 +00:00
|
|
|
<title><%= @title %> | Robert Prehn</title>
|
|
|
|
<%= if assigns[:description] do %>
|
|
|
|
<meta name="description" content={@description} />
|
|
|
|
<% end %>
|
|
|
|
<meta name="author" content="Robert Prehn">
|
2023-09-22 12:58:31 +00:00
|
|
|
|
|
|
|
<link rel="icon" href="/favicon.ico">
|
|
|
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
|
|
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="/assets/app.css?v=1.0">
|
|
|
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
<body class="bg-dark font-mono text-light">
|
2023-09-23 17:15:30 +00:00
|
|
|
<.menu />
|
|
|
|
<%= render_slot(@inner_block) %>
|
2023-09-22 12:58:31 +00:00
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2023-09-23 17:15:30 +00:00
|
|
|
def menu(assigns) do
|
|
|
|
~H"""
|
|
|
|
<nav class="block bg-blue text-dark">
|
|
|
|
<a href="/">About Me</a>
|
|
|
|
<a href="/blog">Blog</a>
|
|
|
|
</nav>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2023-09-22 12:58:31 +00:00
|
|
|
def write_page(%{slug: slug} = assigns) do
|
2023-09-23 17:15:30 +00:00
|
|
|
path =
|
|
|
|
if slug == "index" do
|
|
|
|
Path.join(["priv", "static", "index.html"])
|
|
|
|
else
|
|
|
|
directory = Path.join(["priv", "static", slug])
|
|
|
|
File.mkdir_p!(directory)
|
|
|
|
Path.join(["priv", "static", slug, "index.html"])
|
|
|
|
end
|
2023-09-22 12:58:31 +00:00
|
|
|
|
|
|
|
~H"""
|
2023-09-23 17:15:30 +00:00
|
|
|
<.layout title={@title} description={assigns[:description]}>
|
2023-09-22 12:58:31 +00:00
|
|
|
<main class="container mx-auto">
|
|
|
|
<section class="box border border-light">
|
|
|
|
<%= {:safe, @body} %>
|
|
|
|
</section>
|
|
|
|
</main>
|
|
|
|
</.layout>
|
|
|
|
"""
|
|
|
|
|> rendered_to_string()
|
|
|
|
|> then(&File.write!(path, &1))
|
2023-09-18 18:21:59 +00:00
|
|
|
end
|
|
|
|
end
|