separate DOM mapping and generic templating

the templates collection will soon only be there for reusable snippets,
while the HTML representation of document types will be attached to
those types directly.
This commit is contained in:
Valentin Gagarin 2024-11-13 15:24:41 +01:00
parent 2eeb7e5bcc
commit 4160b6e976
6 changed files with 83 additions and 69 deletions

View file

@ -37,46 +37,37 @@ in
type = recursiveAttrs (with types; functionTo (functionTo str)); type = recursiveAttrs (with types; functionTo (functionTo str));
}; };
config.templates.html = config.templates.html = {
markdown = name: text:
let let
commonmark = name: markdown: pkgs.runCommand "${name}.html" commonmark = pkgs.runCommand "${name}.html"
{ {
buildInputs = [ pkgs.cmark ]; buildInputs = [ pkgs.cmark ];
} '' } ''
cmark ${builtins.toFile "${name}.md" markdown} > $out cmark ${builtins.toFile "${name}.md" text} > $out
''; '';
in in
{ builtins.readFile commonmark;
nav = lib.mkDefault templates.nav; nav = menu: page:
page = lib.mkDefault (config: page: render-html { let
html = { render-item = item:
head = { if item ? menu then ''
title.text = page.title; <li>${item.menu.label}
meta.description = page.description; ${lib.indent " " (item.menu.outputs.html page)}
link.canonical = lib.head page.locations; </li>
}; ''
body.content = [ else if item ? page then ''<li><a href="${page.link item.page}">${item.page.title}</a></li>''
(config.menus.main.outputs.html page) else ''<li><a href="${item.link.url}">${item.link.label}</a></li>''
{ section.heading.content = page.title; } ;
(builtins.readFile (commonmark page.name page.body)) in
]; ''
}; <nav>
}); <ul>
article = lib.mkDefault (config: page: render-html { ${with lib; indent " " (join "\n" (map render-item menu.items))}
html = { </ul>
head = { </nav>
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;
};
body.content = [
(config.menus.main.outputs.html page)
{ section.heading.content = page.title; }
(builtins.readFile (commonmark page.name page.body))
];
};
});
}; };
options.files = mkOption { options.files = mkOption {
@ -86,6 +77,8 @@ in
By default, all elements in `option`{pages} are converted to files using their template or the default template. 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. Add more files to the output by assigning to this attribute set.
''; '';
# TODO: this should be attrsOf string-coercible instead.
# we can convert this to file at the very end.
type = with types; attrsOf path; type = with types; attrsOf path;
}; };

View file

@ -1,22 +0,0 @@
{ lib }:
rec {
nav = menu: page:
let
render-item = item:
if item ? menu then ''
<li>${item.menu.label}
${lib.indent " " (item.menu.outputs.html page)}
</li>
''
else if item ? page then ''<li><a href="${page.link item.page}">${item.page.title}</a></li>''
else ''<li><a href="${item.link.url}">${item.link.label}</a></li>''
;
in
''
<nav>
<ul>
${with lib; indent " " (join "\n" (map render-item menu.menu.items))}
</ul>
</nav>
'';
}

View file

@ -5,6 +5,14 @@ let
types types
; ;
cfg = config; cfg = config;
render-html = document:
let
eval = lib.evalModules {
class = "DOM";
modules = [ document (import ../presentation/dom.nix) ];
};
in
toString eval.config;
in in
{ {
content-types.article = { config, collection, ... }: { content-types.article = { config, collection, ... }: {
@ -27,6 +35,20 @@ in
}; };
}; };
config.name = lib.slug config.title; config.name = lib.slug config.title;
config.outputs.html = lib.mkForce (cfg.templates.html.article cfg config); config.outputs.html = lib.mkForce (render-html {
html = {
head = {
title.text = config.title;
meta.description = config.description;
meta.authors = if lib.isList config.author then config.author else [ config.author ];
link.canonical = lib.head config.locations;
};
body.content = [
(cfg.menus.main.outputs.html config)
{ section.heading.content = config.title; }
(cfg.templates.html.markdown config.name config.body)
];
};
});
}; };
} }

View file

@ -45,13 +45,13 @@ in
# names is soft. # names is soft.
default = target: with lib; "${relativePath (head config.locations) (head target.locations)}.html"; default = target: with lib; "${relativePath (head config.locations) (head target.locations)}.html";
}; };
outputs = mkOption { outputs.html = mkOption {
# TODO: figure out how to make this overridable at any granularity. # TODO: make this of type DOM and convert to string at the output.
# it should be possible with the DOM module now. # the output aggregator then only needs something string-coercible
description = '' description = ''
Representations of the document in different formats Representations of the document in different formats
''; '';
type = with types; attrsOf str; type = with types; str;
}; };
}; };
}; };

View file

@ -56,7 +56,7 @@ in
It must be a function that takes the page on which the navigation is to be shown, such that relative links get computed correctly. It must be a function that takes the page on which the navigation is to be shown, such that relative links get computed correctly.
''; '';
type = with types; attrsOf (functionTo str); type = with types; attrsOf (functionTo str);
default.html = cfg.templates.html.nav { menu = config; }; default.html = cfg.templates.html.nav config;
}; };
}; };
}; };

View file

@ -5,6 +5,14 @@ let
types types
; ;
cfg = config; cfg = config;
render-html = document:
let
eval = lib.evalModules {
class = "DOM";
modules = [ document (import ../presentation/dom.nix) ];
};
in
toString eval.config;
in in
{ {
content-types.page = { name, config, ... }: { content-types.page = { name, config, ... }: {
@ -34,6 +42,19 @@ in
type = types.str; type = types.str;
}; };
}; };
config.outputs.html = cfg.templates.html.page cfg config; config.outputs.html = render-html {
html = {
head = {
title.text = config.title;
meta.description = config.description;
link.canonical = lib.head config.locations;
};
body.content = [
(cfg.menus.main.outputs.html config)
{ section.heading.content = config.title; }
(cfg.templates.html.markdown config.name config.body)
];
};
};
}; };
} }