diff --git a/website/presentation/default.nix b/website/presentation/default.nix
index 22f9b8b3..aa0dba42 100644
--- a/website/presentation/default.nix
+++ b/website/presentation/default.nix
@@ -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 = ''
-
${page.title}
-
-
- '';
- body = ''
- ${templates.nav { menu = { menu = config.menus.main; }; }}
- ${builtins.readFile (commonmark page.name page.body)}
- '';
- });
+ page = lib.mkDefault (config: page: templates.html {
+ head = ''
+ ${page.title}
+
+
+ '';
+ 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 = ''
- ${page.title}
-
-
- '';
- body = ''
- ${templates.nav { menu = { menu = config.menus.main; }; }}
- ${builtins.readFile (commonmark page.name page.body)}
- '';
- });
+ article = lib.mkDefault (config: page: templates.html {
+ head = ''
+ ${page.title}
+
+
+ '';
+ 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 = ''
diff --git a/website/structure/content-types.nix b/website/structure/content-types.nix
index 98c19c6a..d396010b 100644
--- a/website/structure/content-types.nix
+++ b/website/structure/content-types.nix
@@ -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 = { ... }: {
diff --git a/website/structure/default.nix b/website/structure/default.nix
index df80b94c..70a473ff 100644
--- a/website/structure/default.nix
+++ b/website/structure/default.nix
@@ -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 ];
}));
};