wip: toml frontmatter, more layouts stuff
This commit is contained in:
parent
ae26d0ed11
commit
0d7b9ce002
16 changed files with 138 additions and 51 deletions
|
@ -6,10 +6,13 @@ let plugin = require('tailwindcss/plugin')
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: [
|
content: [
|
||||||
'./js/**/*.js',
|
'./js/**/*.js',
|
||||||
'../lib/*_web.ex',
|
'../lib/sloane_sh/layouts/*.ex',
|
||||||
'../lib/*_web/**/*.*ex'
|
'../priv/site/**/*.*ex',
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
|
container: {
|
||||||
|
center: true,
|
||||||
|
},
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
config :logger, :default_formatter,
|
config :logger, :default_formatter, format: "$time $metadata[$level] $message\n"
|
||||||
format: "$time $metadata[$level] $message\n"
|
|
||||||
|
|
||||||
config :tailwind, version: "3.4.1", default: [
|
config :tailwind,
|
||||||
|
version: "3.4.1",
|
||||||
|
default: [
|
||||||
args: ~w(
|
args: ~w(
|
||||||
--config=tailwind.config.js
|
--config=tailwind.config.js
|
||||||
--input=css/app.css
|
--input=css/app.css
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Mix.Tasks.Site.Dev do
|
||||||
@impl Mix.Task
|
@impl Mix.Task
|
||||||
def run(_args) do
|
def run(_args) do
|
||||||
Mix.Task.run("app.start")
|
Mix.Task.run("app.start")
|
||||||
|
|
||||||
{:ok, watch_pid} =
|
{:ok, watch_pid} =
|
||||||
Task.start_link(fn ->
|
Task.start_link(fn ->
|
||||||
Mix.Task.run("site.watch")
|
Mix.Task.run("site.watch")
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
defmodule SloaneSH.Build do
|
defmodule SloaneSH.Build do
|
||||||
require Logger
|
require Logger
|
||||||
require EEx
|
|
||||||
|
|
||||||
alias SloaneSH.Context
|
alias SloaneSH.Context
|
||||||
|
alias SloaneSH.Layouts
|
||||||
alias SloaneSH.Markdown
|
alias SloaneSH.Markdown
|
||||||
alias SloaneSH.Write
|
alias SloaneSH.Write
|
||||||
|
|
||||||
@layouts_dir Path.join(:code.priv_dir(:sloane_sh), "site/layouts")
|
|
||||||
|
|
||||||
EEx.function_from_file(:def, :root_layout, Path.join(@layouts_dir, "root.html.eex"), [
|
|
||||||
:ctx,
|
|
||||||
:inner_content
|
|
||||||
])
|
|
||||||
|
|
||||||
def run(%Context{} = ctx) do
|
def run(%Context{} = ctx) do
|
||||||
ctx
|
ctx
|
||||||
|> build_pages()
|
|> build_pages()
|
||||||
|
@ -37,8 +30,9 @@ defmodule SloaneSH.Build do
|
||||||
path = Path.join(ctx.config.pages_dir, page)
|
path = Path.join(ctx.config.pages_dir, page)
|
||||||
|
|
||||||
with {:ok, data} <- File.read(path),
|
with {:ok, data} <- File.read(path),
|
||||||
{:ok, inner_content} <- Markdown.transform(ctx, data),
|
{:ok, md} <- Markdown.transform(ctx, data),
|
||||||
html = root_layout(ctx, inner_content),
|
contents = Layouts.page_layout(ctx, md.attrs, md.html),
|
||||||
|
html = Layouts.root_layout(ctx, md.attrs, contents),
|
||||||
:ok <- Write.page(ctx, page, html) do
|
:ok <- Write.page(ctx, page, html) do
|
||||||
Logger.info("Built page: #{page}")
|
Logger.info("Built page: #{page}")
|
||||||
else
|
else
|
||||||
|
@ -50,8 +44,8 @@ defmodule SloaneSH.Build do
|
||||||
path = Path.join(ctx.config.posts_dir, post)
|
path = Path.join(ctx.config.posts_dir, post)
|
||||||
|
|
||||||
with {:ok, data} <- File.read(path),
|
with {:ok, data} <- File.read(path),
|
||||||
{:ok, html} <- Markdown.transform(ctx, data),
|
{:ok, md} <- Markdown.transform(ctx, data),
|
||||||
:ok <- Write.post(ctx, post, html) do
|
:ok <- Write.post(ctx, post, md.html) do
|
||||||
Logger.info("Built post: #{post}")
|
Logger.info("Built post: #{post}")
|
||||||
else
|
else
|
||||||
err -> Logger.error("Failed to build post #{post}: #{inspect(err)}")
|
err -> Logger.error("Failed to build post #{post}: #{inspect(err)}")
|
||||||
|
|
27
lib/sloane_sh/layouts.ex
Normal file
27
lib/sloane_sh/layouts.ex
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
defmodule SloaneSH.Layouts do
|
||||||
|
@moduledoc """
|
||||||
|
`EEx` based layouts
|
||||||
|
"""
|
||||||
|
require EEx
|
||||||
|
import SloaneSH.Layouts.Partials
|
||||||
|
|
||||||
|
@layouts_dir Path.join(:code.priv_dir(:sloane_sh), "site/layouts")
|
||||||
|
|
||||||
|
EEx.function_from_file(:def, :root_layout, Path.join(@layouts_dir, "root.html.eex"), [
|
||||||
|
:ctx,
|
||||||
|
:attrs,
|
||||||
|
:inner_content
|
||||||
|
])
|
||||||
|
|
||||||
|
EEx.function_from_file(:def, :page_layout, Path.join(@layouts_dir, "page.html.eex"), [
|
||||||
|
:ctx,
|
||||||
|
:attrs,
|
||||||
|
:inner_content
|
||||||
|
])
|
||||||
|
|
||||||
|
EEx.function_from_file(:def, :post_layout, Path.join(@layouts_dir, "post.html.eex"), [
|
||||||
|
:ctx,
|
||||||
|
:attrs,
|
||||||
|
:inner_content
|
||||||
|
])
|
||||||
|
end
|
16
lib/sloane_sh/layouts/partials.ex
Normal file
16
lib/sloane_sh/layouts/partials.ex
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
defmodule SloaneSH.Layouts.Partials do
|
||||||
|
@moduledoc """
|
||||||
|
HTML partials for use in HTML layouts
|
||||||
|
"""
|
||||||
|
|
||||||
|
def header(_ctx) do
|
||||||
|
~s"""
|
||||||
|
<header>
|
||||||
|
<nav class="flex flex-row gap-2">
|
||||||
|
<a href="/">home</a>
|
||||||
|
<a href="/about">about</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,19 +3,52 @@ defmodule SloaneSH.Markdown do
|
||||||
Markdown parsing using `Earmark` and `Earmark.Parser`
|
Markdown parsing using `Earmark` and `Earmark.Parser`
|
||||||
"""
|
"""
|
||||||
require Logger
|
require Logger
|
||||||
|
use TypedStruct
|
||||||
|
|
||||||
alias SloaneSH.Context
|
alias SloaneSH.Context
|
||||||
|
alias __MODULE__
|
||||||
|
|
||||||
def transform(%Context{} = _ctx, data) when is_binary(data) do
|
typedstruct do
|
||||||
case Earmark.as_html(data) do
|
field :attrs, map(), default: %{}
|
||||||
{:ok, html_doc, deprecation_messages} ->
|
field :html, String.t(), default: ""
|
||||||
for msg <- deprecation_messages, do: Logger.warning(msg)
|
end
|
||||||
|
|
||||||
{:ok, html_doc}
|
def transform(%Context{} = ctx, data) when is_binary(data) do
|
||||||
|
data
|
||||||
|
|> parse_attrs(ctx)
|
||||||
|
|> parse_markdown(ctx)
|
||||||
|
end
|
||||||
|
|
||||||
{:error, html_doc, error_messages} ->
|
defp parse_attrs("+++" <> rest, _ctx) do
|
||||||
for msg <- error_messages, do: Logger.error(msg)
|
[toml, body] = String.split(rest, ["+++\n", "+++\r\n"], parts: 2)
|
||||||
{:error, html_doc}
|
|
||||||
|
with {:ok, attrs} <- Toml.decode(toml, keys: :atoms) do
|
||||||
|
{:ok, attrs, body}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp parse_attrs(body, _ctx) do
|
||||||
|
{:ok, %{}, body}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_markdown({:ok, attrs, body}, _ctx) do
|
||||||
|
with {:ok, html, msgs} <- Earmark.as_html(body) do
|
||||||
|
for msg <- msgs, do: Logger.warning(msg)
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
%Markdown{
|
||||||
|
attrs: attrs,
|
||||||
|
html: html
|
||||||
|
}}
|
||||||
|
else
|
||||||
|
{:error, _, msgs} ->
|
||||||
|
for msg <- msgs, do: Logger.error(msg)
|
||||||
|
:error
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_markdown(other, _ctx), do: other
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ defmodule SloaneSH.Watch do
|
||||||
|
|
||||||
alias SloaneSH.Build
|
alias SloaneSH.Build
|
||||||
alias SloaneSH.Context
|
alias SloaneSH.Context
|
||||||
|
alias SloaneSH.Layouts
|
||||||
|
|
||||||
typedstruct do
|
typedstruct do
|
||||||
field :ctx, Context.t(), enforce: true
|
field :ctx, Context.t(), enforce: true
|
||||||
|
@ -19,17 +20,21 @@ defmodule SloaneSH.Watch do
|
||||||
def init(%Context{} = ctx) do
|
def init(%Context{} = ctx) do
|
||||||
{:ok, watcher_pid} =
|
{:ok, watcher_pid} =
|
||||||
FileSystem.start_link(
|
FileSystem.start_link(
|
||||||
dirs: [
|
dirs:
|
||||||
|
[
|
||||||
# ctx.config.layouts_dir,
|
# ctx.config.layouts_dir,
|
||||||
"priv/site/layouts",
|
"priv/site/layouts",
|
||||||
ctx.config.pages_dir,
|
ctx.config.pages_dir,
|
||||||
ctx.config.posts_dir
|
ctx.config.posts_dir
|
||||||
]
|
]
|
||||||
|
|> dbg()
|
||||||
)
|
)
|
||||||
|
|
||||||
FileSystem.subscribe(watcher_pid)
|
:ok = FileSystem.subscribe(watcher_pid)
|
||||||
|
|
||||||
Tailwind.install_and_run(:default, ~w[--watch]) |> dbg()
|
Task.start_link(fn ->
|
||||||
|
Tailwind.install_and_run(:default, ~w[--watch])
|
||||||
|
end)
|
||||||
|
|
||||||
state = %__MODULE__{ctx: ctx, watcher_pid: watcher_pid}
|
state = %__MODULE__{ctx: ctx, watcher_pid: watcher_pid}
|
||||||
|
|
||||||
|
@ -45,8 +50,10 @@ defmodule SloaneSH.Watch do
|
||||||
|
|
||||||
@impl GenServer
|
@impl GenServer
|
||||||
def handle_info({:file_event, pid, {path, events}}, %{ctx: ctx, watcher_pid: pid} = state) do
|
def handle_info({:file_event, pid, {path, events}}, %{ctx: ctx, watcher_pid: pid} = state) do
|
||||||
|
dbg(path)
|
||||||
|
|
||||||
if String.match?(path, ~r/layouts/) do
|
if String.match?(path, ~r/layouts/) do
|
||||||
recompile_build()
|
recompile_layouts()
|
||||||
Build.run(ctx)
|
Build.run(ctx)
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
else
|
else
|
||||||
|
@ -70,9 +77,9 @@ defmodule SloaneSH.Watch do
|
||||||
{:stop, :watcher_stopped, pid}
|
{:stop, :watcher_stopped, pid}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp recompile_build do
|
defp recompile_layouts do
|
||||||
build_source = Build.module_info(:compile)[:source] |> List.to_string()
|
layouts_source = Layouts.module_info(:compile)[:source] |> List.to_string()
|
||||||
{:ok, _, _} = Kernel.ParallelCompiler.compile([build_source])
|
{:ok, _, _} = Kernel.ParallelCompiler.compile([layouts_source])
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
3
mix.exs
3
mix.exs
|
@ -28,7 +28,8 @@ defmodule SloaneSH.MixProject do
|
||||||
{:earmark_parser, "~> 1.4"},
|
{:earmark_parser, "~> 1.4"},
|
||||||
{:plug, "~> 1.15"},
|
{:plug, "~> 1.15"},
|
||||||
{:bandit, "~> 1.2"},
|
{:bandit, "~> 1.2"},
|
||||||
{:tailwind, "~> 0.2"}
|
{:tailwind, "~> 0.2"},
|
||||||
|
{:toml, "~> 0.7"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -11,6 +11,7 @@
|
||||||
"tailwind": {:hex, :tailwind, "0.2.2", "9e27288b568ede1d88517e8c61259bc214a12d7eed271e102db4c93fcca9b2cd", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "ccfb5025179ea307f7f899d1bb3905cd0ac9f687ed77feebc8f67bdca78565c4"},
|
"tailwind": {:hex, :tailwind, "0.2.2", "9e27288b568ede1d88517e8c61259bc214a12d7eed271e102db4c93fcca9b2cd", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "ccfb5025179ea307f7f899d1bb3905cd0ac9f687ed77feebc8f67bdca78565c4"},
|
||||||
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
||||||
"thousand_island": {:hex, :thousand_island, "1.3.2", "bc27f9afba6e1a676dd36507d42e429935a142cf5ee69b8e3f90bff1383943cd", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0e085b93012cd1057b378fce40cbfbf381ff6d957a382bfdd5eca1a98eec2535"},
|
"thousand_island": {:hex, :thousand_island, "1.3.2", "bc27f9afba6e1a676dd36507d42e429935a142cf5ee69b8e3f90bff1383943cd", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0e085b93012cd1057b378fce40cbfbf381ff6d957a382bfdd5eca1a98eec2535"},
|
||||||
|
"toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"},
|
||||||
"typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"},
|
"typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"},
|
||||||
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
||||||
}
|
}
|
||||||
|
|
1
priv/site/layouts/page.html.eex
Normal file
1
priv/site/layouts/page.html.eex
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<%= inner_content %>
|
1
priv/site/layouts/post.html.eex
Normal file
1
priv/site/layouts/post.html.eex
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<%= inner_content %>
|
|
@ -1,18 +1,13 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html class="m-4 flex flex-row justify-center">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>sloane.sh</title>
|
<title>sloane.sh | <%= attrs[:title] %></title>
|
||||||
<link rel="stylesheet" href="/assets/app.css" />
|
<link rel="stylesheet" href="/assets/app.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="w-full max-w-3xl">
|
||||||
<header>
|
<%= header(ctx) %>
|
||||||
<nav>
|
|
||||||
<a href="/">home</a>
|
|
||||||
<a href="/about">about</a>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
<%= inner_content %>
|
<%= inner_content %>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
|
+++
|
||||||
|
permalink = "/"
|
||||||
|
title = "home"
|
||||||
|
+++
|
||||||
|
|
||||||
# Hello, World!
|
# Hello, World!
|
||||||
|
|
||||||
my name is sloane. i am a software engineer.
|
my name is sloane. i am a software engineer.
|
||||||
|
|
||||||
|
|
||||||
i'm on the fediverse [@sloane@tech.lgbt](https://tech.lgbt/@sloane)
|
i'm on the fediverse [@sloane@tech.lgbt](https://tech.lgbt/@sloane)
|
||||||
|
|
||||||
|
also on [github](https://github.com/sloanelybutsurely).
|
||||||
|
|
Loading…
Reference in a new issue