forked from Fediversity/Fediversity
separate templating from file system outputs
This commit is contained in:
parent
59a2fed5e2
commit
829a796f16
|
@ -7,16 +7,24 @@ let
|
|||
templates = import ./templates.nix { inherit lib; };
|
||||
in
|
||||
{
|
||||
options.templates = mkOption {
|
||||
description = ''
|
||||
Collection of named functions to convert page contents to files
|
||||
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
|
||||
|
||||
Each template function takes the complete site `config` and the page data structure.
|
||||
'';
|
||||
type = with types; attrsOf (functionTo (functionTo options.files.type));
|
||||
};
|
||||
Each template function takes the complete site `config` and the document's data structure.
|
||||
'';
|
||||
type = recursiveAttrs (with types; functionTo (functionTo str));
|
||||
};
|
||||
|
||||
config.templates =
|
||||
config.templates.html =
|
||||
let
|
||||
commonmark = name: markdown: pkgs.runCommand "${name}.html"
|
||||
{
|
||||
|
@ -26,37 +34,27 @@ in
|
|||
'';
|
||||
in
|
||||
{
|
||||
page = lib.mkDefault (config: page: {
|
||||
# TODO: create static redirects from `tail page.locations`
|
||||
# TODO: reconsider using `page.outPath` and what to put into `locations`.
|
||||
# maybe we can avoid having ".html" suffixes there.
|
||||
# since templates can output multiple files, `html` is merely one of many things we *could* produce.
|
||||
# TODO: maybe it would even make sense to split routing and rendering altogether
|
||||
${page.outPath} = builtins.toFile "${page.name}.html" (templates.html {
|
||||
head = ''
|
||||
<title>${page.title}</title>
|
||||
<meta name="description" content="${page.description}" />
|
||||
<link rel="canonical" href="${page.outPath}" />
|
||||
'';
|
||||
body = ''
|
||||
${templates.nav { menu = { menu = config.menus.main; }; }}
|
||||
${builtins.readFile (commonmark page.name page.body)}
|
||||
'';
|
||||
});
|
||||
page = lib.mkDefault (config: page: templates.html {
|
||||
head = ''
|
||||
<title>${page.title}</title>
|
||||
<meta name="description" content="${page.description}" />
|
||||
<link rel="canonical" href="${page.outPath}" />
|
||||
'';
|
||||
body = ''
|
||||
${templates.nav { menu = { menu = config.menus.main; }; }}
|
||||
${builtins.readFile (commonmark page.name page.body)}
|
||||
'';
|
||||
});
|
||||
article = lib.mkDefault (config: page: {
|
||||
# TODO: create static redirects from `tail page.locations`
|
||||
${page.outPath} = builtins.toFile "${page.name}.html" (templates.html {
|
||||
head = ''
|
||||
<title>${page.title}</title>
|
||||
<meta name="description" content="${page.description}" />
|
||||
<meta name="author" content="${with lib; if isList page.author then join ", " page.author else page.author}" />
|
||||
'';
|
||||
body = ''
|
||||
${templates.nav { menu = { menu = config.menus.main; }; }}
|
||||
${builtins.readFile (commonmark page.name page.body)}
|
||||
'';
|
||||
});
|
||||
article = lib.mkDefault (config: page: templates.html {
|
||||
head = ''
|
||||
<title>${page.title}</title>
|
||||
<meta name="description" content="${page.description}" />
|
||||
<meta name="author" content="${with lib; if isList page.author then join ", " page.author else page.author}" />
|
||||
'';
|
||||
body = ''
|
||||
${templates.nav { menu = { menu = config.menus.main; }; }}
|
||||
${builtins.readFile (commonmark page.name page.body)}
|
||||
'';
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -71,25 +69,24 @@ in
|
|||
};
|
||||
|
||||
config.files =
|
||||
# TODO: create static redirects from `tail page.locations`
|
||||
let
|
||||
pages = lib.concatMapAttrs
|
||||
(name: page: page.template config page)
|
||||
config.pages;
|
||||
collections =
|
||||
let
|
||||
byCollection = with lib; mapAttrs
|
||||
(_: collection:
|
||||
map (entry: entry.template config entry) collection.entry
|
||||
)
|
||||
config.collections;
|
||||
in
|
||||
with lib; concatMapAttrs
|
||||
(collection: entries:
|
||||
foldl' (acc: entry: acc // entry) { } entries
|
||||
)
|
||||
byCollection;
|
||||
pages = lib.attrValues config.pages;
|
||||
collections = with lib; concatMap (collection: collection.entry) (attrValues config.collections);
|
||||
collections' = with lib; map
|
||||
(
|
||||
entry: recursiveUpdate entry {
|
||||
locations = map (l: "${entry.collection.name}/${l}") entry.locations;
|
||||
}
|
||||
)
|
||||
collections;
|
||||
in
|
||||
pages // collections;
|
||||
with lib; foldl
|
||||
(acc: elem: acc // {
|
||||
"${head elem.locations}" = builtins.toFile "${elem.name}.html" elem.outputs.html;
|
||||
})
|
||||
{ }
|
||||
(pages ++ collections');
|
||||
|
||||
options.build = mkOption {
|
||||
description = ''
|
||||
|
|
|
@ -16,12 +16,15 @@ in
|
|||
config.content-types = {
|
||||
document = { name, config, ... }: {
|
||||
options = {
|
||||
|
||||
name = mkOption {
|
||||
description = "Symbolic name, used as a human-readable identifier";
|
||||
type = types.str;
|
||||
default = name;
|
||||
};
|
||||
# TODO: reconsider using `page.outPath` and what to put into `locations`.
|
||||
# maybe we can avoid having ".html" suffixes there.
|
||||
# since templates can output multiple files, `html` is merely one of many things we *could* produce.
|
||||
# TODO: make `apply` configurable so one can programmatically modify locations
|
||||
locations = mkOption {
|
||||
description = ''
|
||||
List of historic output locations for the resulting file
|
||||
|
@ -44,17 +47,11 @@ in
|
|||
type = types.str;
|
||||
default = lib.head config.locations;
|
||||
};
|
||||
# TODO: maybe it would even make sense to split routing and rendering altogether.
|
||||
# in that case, templates would return strings, and a different
|
||||
# piece of the machinery resolves rendering templates to files
|
||||
# using `locations`.
|
||||
# then we'd have e.g. `templates.html` and `templates.atom` for
|
||||
# different output formats.
|
||||
template = mkOption {
|
||||
outputs = mkOption {
|
||||
description = ''
|
||||
Function that converts the page contents to files
|
||||
Representations of the document in different formats
|
||||
'';
|
||||
type = with types; functionTo (functionTo options.files.type);
|
||||
type = with types; attrsOf str;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -85,12 +82,17 @@ in
|
|||
type = types.str;
|
||||
};
|
||||
};
|
||||
config.template = cfg.templates.page;
|
||||
config.outputs.html = cfg.templates.html.page cfg config;
|
||||
};
|
||||
|
||||
article = { config, collectionName, ... }: {
|
||||
article = { config, collection, ... }: {
|
||||
imports = [ cfg.content-types.page ];
|
||||
options = {
|
||||
collection = mkOption {
|
||||
description = "Collection this article belongs to";
|
||||
type = options.collections.type.nestedTypes.elemType;
|
||||
default = collection;
|
||||
};
|
||||
date = mkOption {
|
||||
description = "Publication date";
|
||||
type = with types; nullOr str;
|
||||
|
@ -103,8 +105,10 @@ in
|
|||
};
|
||||
};
|
||||
config.name = lib.slug config.title;
|
||||
config.outPath = "${collectionName}/${lib.head config.locations}";
|
||||
config.template = lib.mkForce cfg.templates.article;
|
||||
# TODO: this should be covered by the TBD `link` function instead,
|
||||
# taking a historical list of collection names into account
|
||||
config.outPath = "${collection.name}/${lib.head config.locations}";
|
||||
config.outputs.html = lib.mkForce (cfg.templates.html.article cfg config);
|
||||
};
|
||||
|
||||
named-link = { ... }: {
|
||||
|
|
|
@ -42,11 +42,15 @@ in
|
|||
description = "Type of entries in the collection";
|
||||
type = types.deferredModule;
|
||||
};
|
||||
name = mkOption {
|
||||
description = "Symbolic name, used as a human-readable identifier";
|
||||
type = types.str;
|
||||
default = name;
|
||||
};
|
||||
entry = mkOption {
|
||||
description = "An entry in the collection";
|
||||
type = types.collection (types.submodule ({
|
||||
_module.args.collection = config.entry;
|
||||
_module.args.collectionName = name;
|
||||
_module.args.collection = config;
|
||||
imports = [ config.type ];
|
||||
}));
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue