forked from Fediversity/Fediversity
move things to more appropriate places
This commit is contained in:
parent
6f90db7193
commit
f71bc89921
|
@ -4,19 +4,10 @@ let
|
|||
mkOption
|
||||
types
|
||||
;
|
||||
templates = import ./templates.nix { inherit lib; };
|
||||
# TODO: optionally run the whole thing through the validator
|
||||
# https://github.com/validator/validator
|
||||
render-html = document:
|
||||
let
|
||||
eval = lib.evalModules {
|
||||
class = "DOM";
|
||||
modules = [ document (import ./dom.nix) ];
|
||||
};
|
||||
in
|
||||
toString eval.config;
|
||||
in
|
||||
{
|
||||
imports = lib.nixFiles ./.;
|
||||
|
||||
options.templates =
|
||||
let
|
||||
# arbitrarily nested attribute set where the leaves are of type `type`
|
||||
|
@ -33,65 +24,15 @@ in
|
|||
type = recursiveAttrs (with types; functionTo (coercedTo attrs toString str));
|
||||
};
|
||||
|
||||
config.templates.html = {
|
||||
markdown = { name, body }:
|
||||
let
|
||||
commonmark = pkgs.runCommand "${name}.html"
|
||||
{
|
||||
buildInputs = [ pkgs.cmark ];
|
||||
} ''
|
||||
cmark ${builtins.toFile "${name}.md" body} > $out
|
||||
'';
|
||||
in
|
||||
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 {
|
||||
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.
|
||||
'';
|
||||
# TODO: this should be attrsOf string-coercible instead.
|
||||
# we can convert this to file at the very end.
|
||||
type = with types; attrsOf path;
|
||||
};
|
||||
|
||||
config.files =
|
||||
# TODO: create static redirects from `tail page.locations`
|
||||
let
|
||||
pages = lib.attrValues config.pages;
|
||||
collections = with lib; concatMap (collection: collection.entry) (attrValues config.collections);
|
||||
in
|
||||
with lib; foldl
|
||||
(acc: elem: acc // {
|
||||
# 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;
|
||||
})
|
||||
{ }
|
||||
(pages ++ collections);
|
||||
|
||||
options.build = mkOption {
|
||||
description = ''
|
||||
The final output of the web site
|
||||
|
|
50
website/presentation/templates.nix
Normal file
50
website/presentation/templates.nix
Normal file
|
@ -0,0 +1,50 @@
|
|||
{ config, options, lib, pkgs, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
# TODO: optionally run the whole thing through the validator
|
||||
# https://github.com/validator/validator
|
||||
render-html = document:
|
||||
let
|
||||
eval = lib.evalModules {
|
||||
class = "DOM";
|
||||
modules = [ document (import ./dom.nix) ];
|
||||
};
|
||||
in
|
||||
toString eval.config;
|
||||
in
|
||||
{
|
||||
config.templates.html = {
|
||||
markdown = { name, body }:
|
||||
let
|
||||
commonmark = pkgs.runCommand "${name}.html"
|
||||
{
|
||||
buildInputs = [ pkgs.cmark ];
|
||||
} ''
|
||||
cmark ${builtins.toFile "${name}.md" body} > $out
|
||||
'';
|
||||
in
|
||||
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>
|
||||
'';
|
||||
};
|
||||
}
|
75
website/structure/collections.nix
Normal file
75
website/structure/collections.nix
Normal file
|
@ -0,0 +1,75 @@
|
|||
{ config, options, lib, pkgs, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
cfg = config;
|
||||
in
|
||||
{
|
||||
options.collections = mkOption {
|
||||
description = ''
|
||||
Named collections of unnamed pages
|
||||
|
||||
Define the content type of a new collection `example` to be `article`:
|
||||
|
||||
```nix
|
||||
config.collections.example.type = config.types.article;
|
||||
```
|
||||
|
||||
Add a new entry to the `example` collection:
|
||||
|
||||
```nix
|
||||
config.collections.example.entry = {
|
||||
# contents here
|
||||
}
|
||||
```
|
||||
'';
|
||||
type = with types; attrsOf (submodule ({ name, config, ... }: {
|
||||
options = {
|
||||
type = mkOption {
|
||||
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;
|
||||
};
|
||||
prefixes = mkOption {
|
||||
description = ''
|
||||
List of historic output locations for files in the collection
|
||||
|
||||
The first element is the canonical location.
|
||||
All other elements are used to create redirects to the canonical location.
|
||||
|
||||
The default entry is the symbolic name of the collection.
|
||||
When changing the symbolic name, append the old one to your custom list and use `lib.mkForce` to make sure the default element will be overridden.
|
||||
'';
|
||||
type = with types; nonEmptyListOf str;
|
||||
example = [ "." ];
|
||||
default = [ config.name ];
|
||||
};
|
||||
entry = mkOption {
|
||||
description = "An entry in the collection";
|
||||
type = types.collection (types.submodule ({
|
||||
imports = [ config.type ];
|
||||
_module.args.collection = config;
|
||||
process-locations = ls: with lib; concatMap (l: map (p: "${p}/${l}") config.prefixes) ls;
|
||||
}));
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
config.files =
|
||||
# TODO: create static redirects from `tail <collection>.locations`
|
||||
let
|
||||
collections = with lib; concatMap (collection: collection.entry) (attrValues config.collections);
|
||||
in
|
||||
with lib; foldl
|
||||
(acc: elem: acc // {
|
||||
"${head elem.locations}.html" = builtins.toFile "${elem.name}.html" elem.outputs.html;
|
||||
})
|
||||
{ }
|
||||
collections;
|
||||
}
|
|
@ -14,73 +14,53 @@ in
|
|||
type = with types; attrsOf deferredModule;
|
||||
};
|
||||
|
||||
# TODO: enable i18n, e.g. via a nested attribute for language-specific content
|
||||
options.pages = mkOption {
|
||||
description = ''
|
||||
Collection of pages on the site
|
||||
'';
|
||||
type = with types; attrsOf (submodule config.content-types.page);
|
||||
};
|
||||
|
||||
options.collections = mkOption {
|
||||
description = ''
|
||||
Named collections of unnamed pages
|
||||
|
||||
Define the content type of a new collection `example` to be `article`:
|
||||
|
||||
```nix
|
||||
config.collections.example.type = config.types.article;
|
||||
```
|
||||
|
||||
Add a new entry to the `example` collection:
|
||||
|
||||
```nix
|
||||
config.collections.example.entry = {
|
||||
# contents here
|
||||
}
|
||||
```
|
||||
'';
|
||||
type = with types; attrsOf (submodule ({ name, config, ... }: {
|
||||
options = {
|
||||
type = mkOption {
|
||||
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;
|
||||
};
|
||||
prefixes = mkOption {
|
||||
description = ''
|
||||
List of historic output locations for files in the collection
|
||||
|
||||
The first element is the canonical location.
|
||||
All other elements are used to create redirects to the canonical location.
|
||||
|
||||
The default entry is the symbolic name of the collection.
|
||||
When changing the symbolic name, append the old one to your custom list and use `lib.mkForce` to make sure the default element will be overridden.
|
||||
'';
|
||||
type = with types; nonEmptyListOf str;
|
||||
example = [ "." ];
|
||||
default = [ config.name ];
|
||||
};
|
||||
entry = mkOption {
|
||||
description = "An entry in the collection";
|
||||
type = types.collection (types.submodule ({
|
||||
imports = [ config.type ];
|
||||
_module.args.collection = config;
|
||||
process-locations = ls: with lib; concatMap (l: map (p: "${p}/${l}") config.prefixes) ls;
|
||||
}));
|
||||
};
|
||||
config.content-types.document = { name, config, options, link, ... }: {
|
||||
config._module.args.link = config.link;
|
||||
options = {
|
||||
name = mkOption {
|
||||
description = "Symbolic name, used as a human-readable identifier";
|
||||
type = types.str;
|
||||
default = name;
|
||||
};
|
||||
}));
|
||||
};
|
||||
locations = mkOption {
|
||||
description = ''
|
||||
List of historic output locations for the resulting file
|
||||
|
||||
options.menus = mkOption {
|
||||
description = ''
|
||||
Collection navigation menus
|
||||
'';
|
||||
type = with types; attrsOf (submodule config.content-types.navigation);
|
||||
Elements are relative paths to output files, without suffix.
|
||||
The suffix will be added depending on output file type.
|
||||
|
||||
The first element is the canonical location.
|
||||
All other elements are used to create redirects to the canonical location.
|
||||
|
||||
The default entry is the symbolic name of the document.
|
||||
When changing the symbolic name, append the old one to your custom list and use `lib.mkForce` to make sure the default element will be overridden.
|
||||
'';
|
||||
type = with types; nonEmptyListOf str;
|
||||
apply = config.process-locations;
|
||||
example = [ "about/overview" "index" ];
|
||||
default = [ config.name ];
|
||||
};
|
||||
process-locations = mkOption {
|
||||
description = "Function to post-process the output locations of contained document";
|
||||
type = types.functionTo options.locations.type;
|
||||
default = lib.id;
|
||||
};
|
||||
link = mkOption {
|
||||
description = "Helper function for transparent linking to other pages";
|
||||
type = with types; functionTo str;
|
||||
# TODO: we may want links to other representations,
|
||||
# and currently the mapping of output types to output file
|
||||
# names is soft.
|
||||
default = target: with lib; "${relativePath (head config.locations) (head target.locations)}.html";
|
||||
};
|
||||
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; str;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
in
|
||||
{
|
||||
content-types.document = { name, config, options, link, ... }: {
|
||||
config._module.args.link = config.link;
|
||||
options = {
|
||||
name = mkOption {
|
||||
description = "Symbolic name, used as a human-readable identifier";
|
||||
type = types.str;
|
||||
default = name;
|
||||
};
|
||||
locations = mkOption {
|
||||
description = ''
|
||||
List of historic output locations for the resulting file
|
||||
|
||||
Elements are relative paths to output files, without suffix.
|
||||
The suffix will be added depending on output file type.
|
||||
|
||||
The first element is the canonical location.
|
||||
All other elements are used to create redirects to the canonical location.
|
||||
|
||||
The default entry is the symbolic name of the document.
|
||||
When changing the symbolic name, append the old one to your custom list and use `lib.mkForce` to make sure the default element will be overridden.
|
||||
'';
|
||||
type = with types; nonEmptyListOf str;
|
||||
apply = config.process-locations;
|
||||
example = [ "about/overview" "index" ];
|
||||
default = [ config.name ];
|
||||
};
|
||||
process-locations = mkOption {
|
||||
description = "Function to post-process the output locations of contained document";
|
||||
type = types.functionTo options.locations.type;
|
||||
default = lib.id;
|
||||
};
|
||||
link = mkOption {
|
||||
description = "Helper function for transparent linking to other pages";
|
||||
type = with types; functionTo str;
|
||||
# TODO: we may want links to other representations,
|
||||
# and currently the mapping of output types to output file
|
||||
# names is soft.
|
||||
default = target: with lib; "${relativePath (head config.locations) (head target.locations)}.html";
|
||||
};
|
||||
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; str;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -16,7 +16,14 @@ let
|
|||
];
|
||||
in
|
||||
{
|
||||
content-types.named-link = { ... }: {
|
||||
options.menus = mkOption {
|
||||
description = ''
|
||||
Collection navigation menus
|
||||
'';
|
||||
type = with types; attrsOf (submodule config.content-types.navigation);
|
||||
};
|
||||
|
||||
config.content-types.named-link = { ... }: {
|
||||
options = {
|
||||
label = mkOption {
|
||||
description = "Link label";
|
||||
|
@ -29,7 +36,7 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
content-types.navigation = { name, config, ... }: {
|
||||
config.content-types.navigation = { name, config, ... }: {
|
||||
options = {
|
||||
name = mkOption {
|
||||
description = "Symbolic name, used as a human-readable identifier";
|
||||
|
|
|
@ -15,7 +15,24 @@ let
|
|||
toString eval.config;
|
||||
in
|
||||
{
|
||||
content-types.page = { name, config, ... }: {
|
||||
# TODO: enable i18n, e.g. via a nested attribute for language-specific content
|
||||
options.pages = mkOption {
|
||||
description = ''
|
||||
Collection of pages on the site
|
||||
'';
|
||||
type = with types; attrsOf (submodule config.content-types.page);
|
||||
};
|
||||
config.files = with lib;
|
||||
foldl'
|
||||
(acc: elem: acc // {
|
||||
# TODO: create static redirects from `tail page.locations`
|
||||
# TODO: the file name could correspond to the canonical location in the HTML representation
|
||||
"${head elem.locations}.html" = builtins.toFile "${elem.name}.html" elem.outputs.html;
|
||||
})
|
||||
{ }
|
||||
(attrValues config.pages);
|
||||
|
||||
config.content-types.page = { name, config, ... }: {
|
||||
imports = [ cfg.content-types.document ];
|
||||
options = {
|
||||
title = mkOption {
|
||||
|
|
Loading…
Reference in a new issue