Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add plugins #10

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions .github/workflows/elixir.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3

# Step: Install required dependencies
- name: Install inotify-tools
run: |
sudo apt-get update
sudo apt-get install inotify-tools

# Step: Define how to cache deps. Restores existing cache if present.
- name: Cache deps
id: cache-deps
Expand Down
2 changes: 1 addition & 1 deletion guides/working_with_templates/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ import Config

config :griffin_ssg,
collections: %{
"albums": %{
"genres": %{
permalink: "/movie/genres",
list_layout: "list_genres",
show_layout: "show_genre"
Expand Down
2 changes: 1 addition & 1 deletion installer/priv/templates/priv/content/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "<%= @app_name %>"
layout: "default.html"
layout: "default"
---

Quick static site generation with Elixir templates 🧡
62 changes: 61 additions & 1 deletion lib/griffin_ssg.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ defmodule GriffinSSG do

"""

@typedoc "a callback hook to be called during the build process"
@type hook :: {name :: atom(), fun :: fun()} | fun()

@typedoc "configuration of hooks used during the build process"
@type hooks :: %{before: list(hook()), post_parse: list(hook()), after: list(hook())}

@typedoc "griffin application configuration"
@type config :: map()

@doc """
Parses the string contents of a file into two components: front matter and file content.

Expand Down Expand Up @@ -54,12 +63,16 @@ defmodule GriffinSSG do

@doc """
Renders a layout with a given content, front matter and assigns.
Injects additional assigns like filters and partials.

The layout is assumed to be a compiled EEx file or string, such that calling
`Code.eval_quoted/2` on the layout will generate a correct result.
"""
def render(layout, options) do
assigns = Map.get(options, :assigns, %{})
assigns =
default_assigns()
|> Map.merge(Map.get(options, :assigns, %{}))

rerender_partials = Map.get(options, :rerender_partials, true)

content =
Expand Down Expand Up @@ -110,4 +123,51 @@ defmodule GriffinSSG do
|> Enum.map(fn {k, v} -> {String.to_atom(k), v} end)
|> Enum.into(%{})
end

defp default_assigns do
partials_assigns()
|> Map.merge(default_filters())
|> Map.merge(default_shortcodes())
|> Map.merge(get_app_env(:filters, %{}))
|> Map.merge(get_app_env(:shortcodes, %{}))
end

defp partials_assigns() do
try do
:griffin_build_layouts
|> :ets.lookup(:__partials__)
|> then(fn [{:__partials__, partials}] ->
%{partials: partials}
end)
rescue
ArgumentError ->
%{}
end
end

defp default_filters() do
%{
slugify: &Slug.slugify/1,
uppercase: &String.upcase/1,
lowercase: &String.downcase/1
}
end

defp default_shortcodes do
%{
youtube: fn slug ->
"""
<iframe width="560" height="315" src="https://www.youtube.com/embed/#{slug}"
title="YouTube video player" frameborder="0" allow="accelerometer;
autoplay; clipboard-write; encrypted-media; gyroscope;
picture-in-picture; web-share" allowfullscreen>
</iframe>
"""
end
}
end

defp get_app_env(key, default) do
Application.get_env(:griffin_ssg, key, default)
end
end
47 changes: 47 additions & 0 deletions lib/griffin_ssg/file/parser.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
defmodule GriffinSSG.File.Parser do
@moduledoc """
Module responsible for parsing files into maps.
"""

def from_string(string_content) do
try do
{front_matter, content} =
case String.split(string_content, ~r/---\n/, trim: true) do
[binary] ->
if is_nil(Regex.run(~r/---\n/, string_content)) do
# content only file
{%{}, binary}
else
# front matter only file
{parse_frontmatter(binary), ""}
end

[raw_frontmatter, content] ->
{parse_frontmatter(raw_frontmatter), content}
end

{:ok, %{front_matter: front_matter, content: content}}
rescue
MatchError ->
{:error, :parsing_front_matter_failed}
end
end

def from_file(filepath) do
case File.read(filepath) do
{:ok, file} ->
from_string(file)

error ->
error
end
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
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule GriffinSSG.Filesystem.Watcher do
defmodule GriffinSSG.File.Watcher do
@moduledoc """
Module for non-named GenServer responsible for watching for changes.
Executes a generic callback when file changes are detected.
Expand All @@ -7,8 +7,13 @@ defmodule GriffinSSG.Filesystem.Watcher do
use GenServer

@swap_file_extnames [".swp", ".swx"]
@target_events [
[:modified, :closed],
# MacOS specific event
[:created, :modified]
]

def start_link([directories, callback]) do
def start_link({directories, callback}) do
GenServer.start_link(__MODULE__, {directories, callback})
end

Expand All @@ -18,7 +23,8 @@ defmodule GriffinSSG.Filesystem.Watcher do
{:ok, %{callback: callback}}
end

def handle_info({:file_event, _watcher_pid, {file_path, [:modified, :closed]}}, state) do
def handle_info({:file_event, _watcher_pid, {file_path, event}}, state)
when event in @target_events do
unless Path.extname(file_path) in @swap_file_extnames do
state.callback.()
end
Expand Down
Loading
Loading