2024-11-13 15:24:41 +01:00
|
|
|
{ config, options, lib, pkgs, ... }:
|
|
|
|
let
|
|
|
|
inherit (lib)
|
|
|
|
mkOption
|
|
|
|
types
|
|
|
|
;
|
2024-11-13 15:24:41 +01:00
|
|
|
templates = import ./templates.nix { inherit lib; };
|
2024-11-13 15:24:41 +01:00
|
|
|
# TODO: optionally run the whole thing through the validator
|
|
|
|
# https://github.com/validator/validator
|
2024-11-13 15:24:41 +01:00
|
|
|
render-html = document:
|
|
|
|
let
|
|
|
|
eval = lib.evalModules {
|
|
|
|
class = "DOM";
|
2024-11-13 15:24:41 +01:00
|
|
|
modules = [ document (import ./dom.nix) ];
|
2024-11-13 15:24:41 +01:00
|
|
|
};
|
|
|
|
in
|
|
|
|
toString eval.config;
|
2024-11-13 15:24:41 +01:00
|
|
|
in
|
|
|
|
{
|
2024-11-13 15:24:41 +01:00
|
|
|
options.templates =
|
|
|
|
let
|
|
|
|
# arbitrarily nested attribute set where the leaves are of type `type`
|
|
|
|
# NOTE: due to how `either` works, the first match is significant,
|
|
|
|
# so if `type` happens to be an attrset, the typecheck will consider
|
|
|
|
# `type`, not `attrsOf`
|
|
|
|
recursiveAttrs = type: with types; attrsOf (either type (recursiveAttrs type));
|
|
|
|
in
|
|
|
|
mkOption {
|
|
|
|
description = ''
|
|
|
|
Collection of named functions to convert document contents to a string representation
|
2024-11-13 15:24:41 +01:00
|
|
|
|
2024-11-13 15:24:41 +01:00
|
|
|
Each template function takes the complete site `config` and the document's data structure.
|
|
|
|
'';
|
2024-11-13 15:24:41 +01:00
|
|
|
# TODO: this function should probably take a single attrs,
|
|
|
|
# otherwise it's quite inflexible.
|
|
|
|
# named parameters would also help with readability at the call site
|
2024-11-13 15:24:41 +01:00
|
|
|
type = recursiveAttrs (with types; functionTo (functionTo str));
|
|
|
|
};
|
2024-11-13 15:24:41 +01:00
|
|
|
|
2024-11-13 15:24:41 +01:00
|
|
|
config.templates.html =
|
2024-11-13 15:24:41 +01:00
|
|
|
let
|
|
|
|
commonmark = name: markdown: pkgs.runCommand "${name}.html"
|
|
|
|
{
|
|
|
|
buildInputs = [ pkgs.cmark ];
|
|
|
|
} ''
|
|
|
|
cmark ${builtins.toFile "${name}.md" markdown} > $out
|
|
|
|
'';
|
|
|
|
in
|
|
|
|
{
|
2024-11-13 15:24:41 +01:00
|
|
|
nav = lib.mkDefault templates.nav;
|
2024-11-13 15:24:41 +01:00
|
|
|
page = lib.mkDefault (config: page: render-html {
|
|
|
|
html = {
|
|
|
|
head = {
|
|
|
|
title.text = page.title;
|
|
|
|
meta.description = page.description;
|
|
|
|
link.canonical = lib.head page.locations;
|
|
|
|
};
|
2024-11-13 15:24:41 +01:00
|
|
|
body.content = [
|
|
|
|
(config.menus.main.outputs.html page)
|
|
|
|
{ section.heading.content = page.title; }
|
|
|
|
(builtins.readFile (commonmark page.name page.body))
|
|
|
|
];
|
2024-11-13 15:24:41 +01:00
|
|
|
};
|
2024-11-13 15:24:41 +01:00
|
|
|
});
|
2024-11-13 15:24:41 +01:00
|
|
|
article = lib.mkDefault (config: page: render-html {
|
|
|
|
html = {
|
|
|
|
head = {
|
|
|
|
title.text = page.title;
|
|
|
|
meta.description = page.description;
|
|
|
|
meta.authors = if lib.isList page.author then page.author else [ page.author ];
|
|
|
|
link.canonical = lib.head page.locations;
|
|
|
|
};
|
2024-11-13 15:24:41 +01:00
|
|
|
body.content = [
|
|
|
|
(config.menus.main.outputs.html page)
|
|
|
|
{ section.heading.content = page.title; }
|
|
|
|
(builtins.readFile (commonmark page.name page.body))
|
|
|
|
];
|
2024-11-13 15:24:41 +01:00
|
|
|
};
|
2024-11-13 15:24:41 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
options.files = mkOption {
|
|
|
|
description = ''
|
|
|
|
Files that make up the site, mapping from output path to contents
|
|
|
|
|
|
|
|
By default, all elements in `option`{pages} are converted to files using their template or the default template.
|
|
|
|
Add more files to the output by assigning to this attribute set.
|
|
|
|
'';
|
|
|
|
type = with types; attrsOf path;
|
|
|
|
};
|
|
|
|
|
|
|
|
config.files =
|
2024-11-13 15:24:41 +01:00
|
|
|
# TODO: create static redirects from `tail page.locations`
|
2024-11-13 15:24:41 +01:00
|
|
|
let
|
2024-11-13 15:24:41 +01:00
|
|
|
pages = lib.attrValues config.pages;
|
|
|
|
collections = with lib; concatMap (collection: collection.entry) (attrValues config.collections);
|
2024-11-13 15:24:41 +01:00
|
|
|
in
|
2024-11-13 15:24:41 +01:00
|
|
|
with lib; foldl
|
|
|
|
(acc: elem: acc // {
|
2024-11-13 15:24:41 +01:00
|
|
|
# TODO: we may or may not want to enforce the mapping of file types to output file name suffixes
|
|
|
|
"${head elem.locations}.html" = builtins.toFile "${elem.name}.html" elem.outputs.html;
|
2024-11-13 15:24:41 +01:00
|
|
|
})
|
|
|
|
{ }
|
2024-11-13 15:24:41 +01:00
|
|
|
(pages ++ collections);
|
2024-11-13 15:24:41 +01:00
|
|
|
|
|
|
|
options.build = mkOption {
|
|
|
|
description = ''
|
|
|
|
The final output of the web site
|
|
|
|
'';
|
|
|
|
type = types.package;
|
|
|
|
default =
|
|
|
|
let
|
|
|
|
script = ''
|
|
|
|
mkdir $out
|
|
|
|
'' + lib.join "\n" copy;
|
|
|
|
copy = lib.mapAttrsToList
|
|
|
|
(
|
|
|
|
path: file: ''
|
|
|
|
mkdir -p $out/$(dirname ${path})
|
|
|
|
cp -r ${file} $out/${path}
|
|
|
|
''
|
|
|
|
)
|
|
|
|
config.files;
|
|
|
|
in
|
|
|
|
pkgs.runCommand "source" { } script;
|
|
|
|
};
|
|
|
|
}
|