Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:
include:
- elixir: 1.17.x
otp: 27.x
- elixir: 1.18.x
otp: 28.x

steps:
- uses: actions/checkout@v4
Expand Down
11 changes: 5 additions & 6 deletions .github/workflows/lint-commit.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Lint Commit
on:
pull_request:
pull_request_target:
types:
- opened
- reopened
Expand All @@ -14,9 +14,8 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Install Deps
run: yarn install
- name: Lint PR Title
run: echo "${PULL_REQUEST_TITLE}" | yarn commitlint
- uses: amannn/action-semantic-pull-request@v5
env:
PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
subjectPattern: ^(?![A-Z]).+$
3 changes: 0 additions & 3 deletions commitlint.config.js

This file was deleted.

17 changes: 9 additions & 8 deletions lib/mix/tasks/tableau.build.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ defmodule Mix.Tasks.Tableau.Build do
{:ok, config} = Tableau.Config.get()
token = Map.put(token, :extensions, %{})

token = mods |> extensions_for(:pre_build) |> run_extensions(token)
token = mods |> extensions_for(:pre_build) |> run_extensions(:pre_build, token)

token = mods |> extensions_for(:pre_render) |> run_extensions(:pre_render, token)
graph = Tableau.Graph.insert(token.graph, mods)

File.mkdir_p!(out)

pages =
for mod <- Graph.vertices(graph), {:ok, :page} == Nodable.type(mod) do
{mod, Map.new(Nodable.opts(mod) || [])}
for page <- Graph.vertices(graph), {:ok, :page} == Nodable.type(page) do
{page, Map.new(Nodable.opts(page) || [])}
end

token = put_in(token.site[:pages], Enum.map(pages, fn {_mod, page} -> page end))
Expand All @@ -62,7 +63,7 @@ defmodule Mix.Tasks.Tableau.Build do

token = put_in(token.site[:pages], pages)

token = mods |> extensions_for(:pre_write) |> run_extensions(token)
token = mods |> extensions_for(:pre_write) |> run_extensions(:pre_write, token)

for %{body: body, permalink: permalink} <- pages do
file_path = build_file_path(out, permalink)
Expand All @@ -77,7 +78,7 @@ defmodule Mix.Tasks.Tableau.Build do
File.cp_r!(config.include_dir, out)
end

token = mods |> extensions_for(:post_write) |> run_extensions(token)
token = mods |> extensions_for(:post_write) |> run_extensions(:post_write, token)

token
end
Expand All @@ -101,14 +102,14 @@ defmodule Mix.Tasks.Tableau.Build do

defp extensions_for(modules, type) do
extensions =
for mod <- modules, Code.ensure_loaded?(mod), {:ok, type} == Tableau.Extension.type(mod) do
for mod <- modules, Code.ensure_loaded?(mod), function_exported?(mod, type, 1) do
mod
end

Enum.sort_by(extensions, & &1.__tableau_extension_priority__())
end

defp run_extensions(extensions, token) do
defp run_extensions(extensions, type, token) do
for module <- extensions, reduce: token do
token ->
raw_config =
Expand All @@ -124,7 +125,7 @@ defmodule Mix.Tasks.Tableau.Build do

token = put_in(token.extensions[key], %{config: config})

case module.run(token) do
case apply(module, type, [token]) do
{:ok, token} ->
token

Expand Down
2 changes: 1 addition & 1 deletion lib/tableau/converter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Tableau.Converter do
@doc """
Converts content into HTML.

Is given the file path, the content of the files (sans front matter), the front matter, and a list of options.
Is given the file path, the frontmatter, the content of the files (sans front matter), and a list of options.
"""
@callback convert(filepath :: String.t(), front_matter :: map(), content :: String.t(), opts :: Keyword.t()) ::
String.t()
Expand Down
57 changes: 35 additions & 22 deletions lib/tableau/extension.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ defmodule Tableau.Extension do
## Options

* `:key` - The key in which the extensions configuration and data is loaded.
* `:type` - The type of extension. See below for a description.
* `:priority` - An integer used for ordering extensions of the same type.
* `:enabled` - Whether or not to enable the extension. Defaults to true, and can be configured differently based on the extension.

## Types
## Callbacks

There are currently the following extension types:
There are currently the following extension callbacks:

- `:pre_build` - executed before tableau builds your site and writes anything to disk.
- `:pre_write` - executed after tableau builds your site but before it writes anything to disk.
Expand All @@ -33,9 +32,10 @@ defmodule Tableau.Extension do

```elixir
defmodule MySite.PostsExtension do
use Tableau.Extension, key: :posts, type: :pre_build, priority: 300
use Tableau.Extension, key: :posts, priority: 300

def run(token) do
@impl Tableau.Extension
def pre_build(token) do
posts =
for post <- Path.wildcard("_posts/**/*.md") do
%Tableau.Page{
Expand All @@ -57,15 +57,35 @@ defmodule Tableau.Extension do
```
'''

@typep extension_type :: :pre_build | :post_write
@type token :: map()

@doc """
The extension entry point.
Called in the pre_build phase.

The function is passed a token and can return a new token with new data loaded into it.
"""
@callback run(token()) :: {:ok, token()} | :error
@callback pre_build(token()) :: {:ok, token()} | :error

@doc """
Called in the pre_render phase.

The function is passed a token and can return a new token with new data loaded into it.
"""
@callback pre_render(token()) :: {:ok, token()} | :error

@doc """
Called in the pre_write phase.

The function is passed a token and can return a new token with new data loaded into it.
"""
@callback pre_write(token()) :: {:ok, token()} | :error

@doc """
Called in the post_write phase.

The function is passed a token and can return a new token with new data loaded into it.
"""
@callback post_write(token()) :: {:ok, token()} | :error

@doc """
Optional callback to validate the config for an extension. Useful for
Expand All @@ -74,15 +94,18 @@ defmodule Tableau.Extension do
@callback config(Keyword.t() | map()) :: {:ok, map()} | {:error, any()}

@optional_callbacks [
config: 1
config: 1,
pre_build: 1,
pre_render: 1,
pre_write: 1,
post_write: 1
]

defmacro __using__(opts) do
opts = Keyword.validate!(opts, [:key, :enabled, :type, :priority])
opts = Keyword.validate!(opts, [:key, :enabled, :priority])

prelude =
quote do
def __tableau_extension_type__, do: unquote(opts)[:type]
def __tableau_extension_key__, do: unquote(opts)[:key]
def __tableau_extension_enabled__, do: Keyword.get(unquote(opts), :enabled, true)
def __tableau_extension_priority__, do: unquote(opts)[:priority] || 0
Expand All @@ -97,17 +120,7 @@ defmodule Tableau.Extension do
end

@doc false
@spec type(module()) :: extension_type()
def type(module) do
if function_exported?(module, :__tableau_extension_type__, 0) do
{:ok, module.__tableau_extension_type__()}
else
:error
end
end

@doc false
@spec key(module()) :: extension_type()
@spec key(module()) :: atom()
def key(module) do
if function_exported?(module, :__tableau_extension_key__, 0) do
{:ok, module.__tableau_extension_key__()}
Expand Down
10 changes: 3 additions & 7 deletions lib/tableau/extensions/common.ex
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,8 @@ defmodule Tableau.Extension.Common do
end
end)

path
|> String.replace(Map.keys(vars), &to_string(Map.fetch!(vars, &1)))
|> String.replace(" ", "-")
|> String.replace("_", "-")
|> String.replace(~r/[^[:alnum:]\/\-.]/, "")
|> String.downcase()
|> URI.encode()
String.replace(path, Map.keys(vars), fn key ->
vars |> Map.fetch!(key) |> to_string() |> Slug.slugify(ignore: ["."])
end)
end
end
6 changes: 4 additions & 2 deletions lib/tableau/extensions/data_extension.ex
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ defmodule Tableau.DataExtension do

<!-- tabs-close -->
"""
use Tableau.Extension, key: :data, type: :pre_build, priority: 200
use Tableau.Extension, key: :data, priority: 200

import Schematic

@impl Tableau.Extension
def config(config) do
unify(
map(%{
Expand All @@ -69,7 +70,8 @@ defmodule Tableau.DataExtension do
)
end

def run(token) do
@impl Tableau.Extension
def pre_build(token) do
data =
for file <- Path.wildcard(Path.join(token.extensions.data.config.dir, "**/*.{yml,yaml,exs}")), into: %{} do
case Path.extname(file) do
Expand Down
53 changes: 31 additions & 22 deletions lib/tableau/extensions/page_extension.ex
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ defmodule Tableau.PageExtension do
As noted above, a converter can be overridden on a specific page, using the frontmatter `:converter` key.
"""

use Tableau.Extension, key: :pages, type: :pre_build, priority: 100
use Tableau.Extension, key: :pages, priority: 100

import Schematic

alias Tableau.Extension.Common

@impl Tableau.Extension
def config(input) do
unify(
map(%{
Expand All @@ -83,7 +84,8 @@ defmodule Tableau.PageExtension do
)
end

def run(token) do
@impl Tableau.Extension
def pre_build(token) do
%{site: %{config: %{converters: converters}}, extensions: %{pages: %{config: config}}} = token

exts = Enum.map_join(converters, ",", fn {ext, _} -> to_string(ext) end)
Expand All @@ -96,40 +98,47 @@ defmodule Tableau.PageExtension do
|> Path.join("**/*.{#{exts}}")
|> Common.paths()
end)
|> Common.entries(fn %{path: path, front_matter: front_matter, pre_convert_body: body, ext: ext} ->
{
build(path, front_matter, body, config),
fn assigns ->
converter =
case front_matter[:converter] do
nil -> converters[ext]
converter -> Module.concat([converter])
end

converter.convert(path, front_matter, body, assigns)
end
}
|> Common.entries(fn entry ->
%{
path: path,
front_matter: front_matter,
pre_convert_body: body,
ext: ext
} = entry

build(path, front_matter, body, config, fn assigns ->
converter =
case front_matter[:converter] do
nil -> converters[ext]
converter -> Module.concat([converter])
end

converter.convert(path, front_matter, body, assigns)
end)
end)

{:ok, Map.put(token, :pages, pages)}
end

@impl Tableau.Extension
def pre_render(token) do
graph =
Tableau.Graph.insert(
token.graph,
Enum.map(pages, fn {page, renderer} ->
%Tableau.Page{parent: page.layout, permalink: page.permalink, template: renderer, opts: page}
Enum.map(token.pages, fn page ->
%Tableau.Page{parent: page.layout, permalink: page.permalink, template: page.renderer, opts: page}
end)
)

{:ok,
token
|> Map.put(:pages, pages |> Enum.unzip() |> elem(0))
|> Map.put(:graph, graph)}
{:ok, Map.put(token, :graph, graph)}
end

defp build(filename, front_matter, body, pages_config) do
defp build(filename, front_matter, body, pages_config, renderer) do
front_matter
|> Map.put(:__tableau_page_extension__, true)
|> Map.put(:body, body)
|> Map.put(:file, filename)
|> Map.put(:renderer, renderer)
|> Map.put(:layout, Module.concat([front_matter[:layout] || pages_config.layout]))
|> Common.build_permalink(pages_config)
end
Expand Down
16 changes: 9 additions & 7 deletions lib/tableau/extensions/post_extension.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ defmodule Tableau.PostExtension do
As noted above, a converter can be overridden on a specific page, using the frontmatter `:converter` key.
"""

use Tableau.Extension, key: :posts, type: :pre_build, priority: 100
use Tableau.Extension, key: :posts, priority: 100

import Schematic

Expand Down Expand Up @@ -100,7 +100,7 @@ defmodule Tableau.PostExtension do
end

@impl Tableau.Extension
def run(token) do
def pre_build(token) do
%{site: %{config: %{converters: converters}}, extensions: %{posts: %{config: config}}} = token
exts = Enum.map_join(converters, ",", fn {ext, _} -> to_string(ext) end)

Expand Down Expand Up @@ -132,18 +132,20 @@ defmodule Tableau.PostExtension do
end
end)

{:ok, Map.put(token, :posts, posts)}
end

@impl Tableau.Extension
def pre_render(token) do
graph =
Tableau.Graph.insert(
token.graph,
Enum.map(posts, fn post ->
Enum.map(token.posts, fn post ->
%Tableau.Page{parent: post.layout, permalink: post.permalink, template: post.renderer, opts: post}
end)
)

{:ok,
token
|> Map.put(:posts, posts)
|> Map.put(:graph, graph)}
{:ok, Map.put(token, :graph, graph)}
end

defp build(filename, attrs, body, posts_config, renderer) do
Expand Down
Loading