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 committed by Valentin Gagarin
parent 5ee1c8b006
commit 4aeb9579d6
6 changed files with 83 additions and 69 deletions

View file

@ -37,46 +37,37 @@ in
type = recursiveAttrs (with types; functionTo (functionTo str));
};
config.templates.html =
config.templates.html = {
markdown = name: text:
let
commonmark = name: markdown: pkgs.runCommand "${name}.html"
commonmark = pkgs.runCommand "${name}.html"
{
buildInputs = [ pkgs.cmark ];
} ''
cmark ${builtins.toFile "${name}.md" markdown} > $out
cmark ${builtins.toFile "${name}.md" text} > $out
'';
in
{
nav = lib.mkDefault templates.nav;
page = lib.mkDefault (config: page: render-html {
html = {
head = {
title.text = page.title;
meta.description = page.description;
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))
];
};
});
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;
};
body.content = [
(config.menus.main.outputs.html page)
{ section.heading.content = page.title; }
(builtins.readFile (commonmark page.name page.body))
];
};
});
builtins.readFile commonmark;
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.items))}
</ul>
</nav>
'';
};
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.
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;
};

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
;
cfg = config;
render-html = document:
let
eval = lib.evalModules {
class = "DOM";
modules = [ document (import ../presentation/dom.nix) ];
};
in
toString eval.config;
in
{
content-types.article = { config, collection, ... }: {
@ -27,6 +35,20 @@ in
};
};
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.
default = target: with lib; "${relativePath (head config.locations) (head target.locations)}.html";
};
outputs = mkOption {
# TODO: figure out how to make this overridable at any granularity.
# it should be possible with the DOM module now.
outputs.html = mkOption {
# TODO: make this of type DOM and convert to string at the output.
# the output aggregator then only needs something string-coercible
description = ''
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.
'';
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
;
cfg = config;
render-html = document:
let
eval = lib.evalModules {
class = "DOM";
modules = [ document (import ../presentation/dom.nix) ];
};
in
toString eval.config;
in
{
content-types.page = { name, config, ... }: {
@ -34,6 +42,19 @@ in
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)
];
};
};
};
}