forked from Fediversity/fediversity.eu
Compare commits
4 commits
9da5bdde9e
...
42e20c02d0
Author | SHA1 | Date | |
---|---|---|---|
42e20c02d0 | |||
9329351c6c | |||
f4d40fdadf | |||
38c6ba6536 |
|
@ -6,6 +6,7 @@ in
|
||||||
menus.main = {
|
menus.main = {
|
||||||
label = "Main";
|
label = "Main";
|
||||||
items = [
|
items = [
|
||||||
|
{ page = pages.index // { title = "Start"; }; }
|
||||||
{
|
{
|
||||||
menu.label = "For you";
|
menu.label = "For you";
|
||||||
menu.items = map (page: { inherit page; })
|
menu.items = map (page: { inherit page; })
|
||||||
|
|
38
lib.nix
38
lib.nix
|
@ -1,5 +1,19 @@
|
||||||
{ lib }:
|
{ lib }:
|
||||||
rec {
|
rec {
|
||||||
|
/**
|
||||||
|
Recursively replace occurrences of `from` with `to` within `string`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
replaceStringRec "--" "-" "hello-----world"
|
||||||
|
=> "hello-world"
|
||||||
|
*/
|
||||||
|
replaceStringsRec = from: to: string:
|
||||||
|
let
|
||||||
|
replaced = lib.replaceStrings [ from ] [ to ] string;
|
||||||
|
in
|
||||||
|
if replaced == string then string else replaceStringsRec from to replaced;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Create a URL-safe slug from any string
|
Create a URL-safe slug from any string
|
||||||
*/
|
*/
|
||||||
|
@ -22,14 +36,30 @@ rec {
|
||||||
matched = builtins.match "(-*)([^-].*[^-]|[^-])(-*)" s;
|
matched = builtins.match "(-*)([^-].*[^-]|[^-])(-*)" s;
|
||||||
in
|
in
|
||||||
with lib; optionalString (!isNull matched) (builtins.elemAt matched 1);
|
with lib; optionalString (!isNull matched) (builtins.elemAt matched 1);
|
||||||
|
in
|
||||||
|
trimHyphens (replaceStringsRec "--" "-" replaced);
|
||||||
|
|
||||||
collapseHyphens = s:
|
squash = replaceStringsRec "\n\n" "\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
Trim trailing spaces and squash non-leading spaces
|
||||||
|
*/
|
||||||
|
trim = string:
|
||||||
let
|
let
|
||||||
result = builtins.replaceStrings [ "--" ] [ "-" ] s;
|
trimLine = line:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
# separate leading spaces from the rest
|
||||||
|
parts = split "(^ *)" line;
|
||||||
|
spaces = head (elemAt parts 1);
|
||||||
|
rest = elemAt parts 2;
|
||||||
|
# drop trailing spaces
|
||||||
|
body = head (split " *$" rest);
|
||||||
in
|
in
|
||||||
if result == s then s else collapseHyphens result;
|
if body == "" then "" else
|
||||||
|
spaces + replaceStringsRec " " " " body;
|
||||||
in
|
in
|
||||||
trimHyphens (collapseHyphens replaced);
|
join "\n" (map trimLine (splitLines string));
|
||||||
|
|
||||||
join = lib.concatStringsSep;
|
join = lib.concatStringsSep;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,14 @@ let
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
templates = import ./templates.nix { inherit lib; };
|
templates = import ./templates.nix { inherit lib; };
|
||||||
|
render-html = document:
|
||||||
|
let
|
||||||
|
eval = lib.evalModules {
|
||||||
|
class = "DOM";
|
||||||
|
modules = [ document (import ./dom.nix { inherit lib; }).document ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
toString eval.config;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.templates =
|
options.templates =
|
||||||
|
@ -38,30 +46,38 @@ in
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
nav = lib.mkDefault templates.nav;
|
nav = lib.mkDefault templates.nav;
|
||||||
page = lib.mkDefault (config: page: templates.html {
|
page = lib.mkDefault (config: page: render-html {
|
||||||
head = ''
|
html = {
|
||||||
<title>${page.title}</title>
|
head = {
|
||||||
<meta name="description" content="${page.description}" />
|
title.text = page.title;
|
||||||
<link rel="canonical" href="${lib.head page.locations}" />
|
meta.description = page.description;
|
||||||
'';
|
link.canonical = lib.head page.locations;
|
||||||
body = ''
|
};
|
||||||
|
body.content = ''
|
||||||
${config.menus.main.outputs.html page}
|
${config.menus.main.outputs.html page}
|
||||||
|
|
||||||
|
<h1>${page.title}</h1>
|
||||||
|
|
||||||
${builtins.readFile (commonmark page.name page.body)}
|
${builtins.readFile (commonmark page.name page.body)}
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
});
|
});
|
||||||
article = lib.mkDefault (config: page: templates.html {
|
article = lib.mkDefault (config: page: render-html {
|
||||||
head = ''
|
html = {
|
||||||
<title>${page.title}</title>
|
head = {
|
||||||
<meta name="description" content="${page.description}" />
|
title.text = page.title;
|
||||||
${with lib; join "\n" (map
|
meta.description = page.description;
|
||||||
(author: ''<meta name="author" content="${author}" />'')
|
meta.authors = if lib.isList page.author then page.author else [ page.author ];
|
||||||
(if isList page.author then page.author else [page.author]))
|
link.canonical = lib.head page.locations;
|
||||||
}
|
};
|
||||||
'';
|
body.content = ''
|
||||||
body = ''
|
|
||||||
${config.menus.main.outputs.html page}
|
${config.menus.main.outputs.html page}
|
||||||
|
|
||||||
|
<h1>${page.title}</h1>
|
||||||
|
|
||||||
${builtins.readFile (commonmark page.name page.body)}
|
${builtins.readFile (commonmark page.name page.body)}
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
*/
|
*/
|
||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
let
|
let
|
||||||
|
inherit (lib) mkOption types;
|
||||||
|
|
||||||
# https://html.spec.whatwg.org/multipage/dom.html#content-models
|
# https://html.spec.whatwg.org/multipage/dom.html#content-models
|
||||||
# https://html.spec.whatwg.org/multipage/dom.html#kinds-of-content
|
# https://html.spec.whatwg.org/multipage/dom.html#kinds-of-content
|
||||||
content-categories = [
|
content-categories = [
|
||||||
|
@ -25,6 +27,8 @@ let
|
||||||
|
|
||||||
# base type for all DOM elements
|
# base type for all DOM elements
|
||||||
element = { name, config, ... }: {
|
element = { name, config, ... }: {
|
||||||
|
# TODO: add fields for upstream documentation references
|
||||||
|
# TODO: programmatically generate documentation
|
||||||
options = with lib; {
|
options = with lib; {
|
||||||
attrs = mkAttrs { };
|
attrs = mkAttrs { };
|
||||||
categories = mkOption {
|
categories = mkOption {
|
||||||
|
@ -39,7 +43,7 @@ let
|
||||||
|
|
||||||
# options with types for all the defined DOM elements
|
# options with types for all the defined DOM elements
|
||||||
element-types = lib.mapAttrs
|
element-types = lib.mapAttrs
|
||||||
(name: value: mkOption { type = types.submodule e; })
|
(name: value: mkOption { type = types.submodule value; })
|
||||||
elements;
|
elements;
|
||||||
|
|
||||||
# attrset of categories, where values are module options with the type of the
|
# attrset of categories, where values are module options with the type of the
|
||||||
|
@ -53,30 +57,32 @@ let
|
||||||
(filterAttrs (_: e: elem category (e { name = "dummy"; }).config.categories) elements))
|
(filterAttrs (_: e: elem category (e { name = "dummy"; }).config.categories) elements))
|
||||||
);
|
);
|
||||||
|
|
||||||
global-attrs =
|
global-attrs = lib.mapAttrs (name: value: mkOption value) {
|
||||||
let
|
class = {
|
||||||
inherit (lib) mkOption types;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
class = mkOption {
|
|
||||||
type = with types; listOf nonEmptyStr;
|
type = with types; listOf nonEmptyStr;
|
||||||
|
default = [ ];
|
||||||
};
|
};
|
||||||
hidden = mkOption {
|
hidden = {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
};
|
};
|
||||||
id = mkOption {
|
id = {
|
||||||
type = types.nonEmptyStr;
|
type = with types; nullOr nonEmptyStr;
|
||||||
|
default = null;
|
||||||
};
|
};
|
||||||
lang = mkOption {
|
lang = {
|
||||||
# TODO: https://www.rfc-editor.org/rfc/rfc5646.html
|
# TODO: https://www.rfc-editor.org/rfc/rfc5646.html
|
||||||
type = types.str;
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
};
|
};
|
||||||
style = mkOption {
|
style = {
|
||||||
# TODO: CSS type ;..)
|
# TODO: CSS type ;..)
|
||||||
type = types.str;
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
};
|
};
|
||||||
title = mkOption {
|
title = {
|
||||||
type = types.lines;
|
type = with types; nullOr lines;
|
||||||
|
default = null;
|
||||||
};
|
};
|
||||||
# TODO: more global attributes
|
# TODO: more global attributes
|
||||||
# https://html.spec.whatwg.org/#global-attributes
|
# https://html.spec.whatwg.org/#global-attributes
|
||||||
|
@ -84,25 +90,64 @@ let
|
||||||
# https://html.spec.whatwg.org/multipage/microdata.html#encoding-microdata
|
# https://html.spec.whatwg.org/multipage/microdata.html#encoding-microdata
|
||||||
};
|
};
|
||||||
|
|
||||||
mkAttrs = attrs: with lib; mkOption {
|
attrs = lib.mapAttrs (name: value: mkOption value) {
|
||||||
|
href = {
|
||||||
|
# TODO: https://url.spec.whatwg.org/#valid-url-string
|
||||||
|
# ;..O
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
target = {
|
||||||
|
# https://html.spec.whatwg.org/multipage/document-sequences.html#valid-navigable-target-name-or-keyword
|
||||||
|
type =
|
||||||
|
let
|
||||||
|
is-valid-target = s:
|
||||||
|
let
|
||||||
|
inherit (lib) match;
|
||||||
|
has-lt = s: match ".*<.*" s != null;
|
||||||
|
has-tab-or-newline = s: match ".*[\t\n].*" s != null;
|
||||||
|
has-valid-start = s: match "^[^_].*$" s != null;
|
||||||
|
in
|
||||||
|
has-valid-start s && !(has-lt s && has-tab-or-newline s);
|
||||||
|
in
|
||||||
|
with types; either
|
||||||
|
(enum [ "_blank" "_self" "_parent" "_top" ])
|
||||||
|
(types.addCheck str is-valid-target)
|
||||||
|
;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mkAttrs = attrs: with lib;
|
||||||
|
mkOption {
|
||||||
type = types.submodule {
|
type = types.submodule {
|
||||||
options = global-attrs // attrs;
|
options = global-attrs // attrs;
|
||||||
};
|
};
|
||||||
default = { };
|
default = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
print-element = name: attrs: content:
|
print-attrs = with lib; attrs:
|
||||||
with lib;
|
# TODO: figure out how let attributes know how to print themselves without polluting the interface
|
||||||
let
|
let
|
||||||
attributes = join " " (mapAttrsToList
|
result = trim (join " "
|
||||||
|
(mapAttrsToList
|
||||||
# TODO: this needs to be smarter for boolean attributes
|
# TODO: this needs to be smarter for boolean attributes
|
||||||
# where the value must be written out explicitly.
|
# where the value must be written out explicitly.
|
||||||
# probably the attribute itself should have its own `__toString`.
|
# probably the attribute itself should have its own `__toString`.
|
||||||
(name: value: if isBool value then name else "${name}=${value}")
|
(name: value:
|
||||||
attrs);
|
if isBool value then
|
||||||
|
if value then name else ""
|
||||||
|
# TODO: some attributes must be explicitly empty
|
||||||
|
else optionalString (toString value != "") ''${name}=${value}''
|
||||||
|
)
|
||||||
|
attrs)
|
||||||
|
);
|
||||||
in
|
in
|
||||||
|
optionalString (stringLength result > 0) " " + result
|
||||||
|
;
|
||||||
|
|
||||||
|
print-element = name: attrs: content:
|
||||||
|
with lib;
|
||||||
lib.squash ''
|
lib.squash ''
|
||||||
<${name}${optionalString (stringLength attributes > 0) " ${attributes}"}>
|
<${name}${print-attrs attrs}>
|
||||||
${lib.indent " " content}
|
${lib.indent " " content}
|
||||||
</${name}>
|
</${name}>
|
||||||
'';
|
'';
|
||||||
|
@ -116,7 +161,7 @@ let
|
||||||
|
|
||||||
config.categories = [ ];
|
config.categories = [ ];
|
||||||
config.__toString = self: ''
|
config.__toString = self: ''
|
||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML >
|
||||||
${self.html}
|
${self.html}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
@ -146,32 +191,175 @@ let
|
||||||
type = with types; nullOr (submodule base);
|
type = with types; nullOr (submodule base);
|
||||||
default = null;
|
default = null;
|
||||||
};
|
};
|
||||||
content = with types;
|
# https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-charset
|
||||||
listOf (attrTag (
|
meta.charset = mkOption {
|
||||||
removeAttrs categories.metadata [ "title" "base" ]
|
# TODO: create programmatically from https://encoding.spec.whatwg.org/encodings.json
|
||||||
));
|
type = types.enum [
|
||||||
|
"utf-8"
|
||||||
|
];
|
||||||
|
default = "utf-8";
|
||||||
|
};
|
||||||
|
# https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag#viewport_width_and_screen_width
|
||||||
|
# this should not exist and no one should ever have to think about it
|
||||||
|
meta.viewport = mkOption {
|
||||||
|
type = types.submodule ({ ... }: {
|
||||||
|
# TODO: figure out how to render only non-default values
|
||||||
|
options = {
|
||||||
|
width = mkOption {
|
||||||
|
type = with types; either
|
||||||
|
(ints.between 1 10000)
|
||||||
|
(enum [ "device-width" ]);
|
||||||
|
default = "device-width"; # not default by standard
|
||||||
|
};
|
||||||
|
height = mkOption {
|
||||||
|
type = with types; either
|
||||||
|
(ints.between 1 10000)
|
||||||
|
(enum [ "device-height" ]);
|
||||||
|
default = "device-height"; # not default by standard (but seems to work if you don't set it)
|
||||||
|
};
|
||||||
|
initial-scale = mkOption {
|
||||||
|
type = types.numbers.between 0.1 10;
|
||||||
|
default = 1;
|
||||||
|
};
|
||||||
|
minimum-scale = mkOption {
|
||||||
|
type = types.numbers.between 0.1 10;
|
||||||
|
# TODO: render only as many digits as needed
|
||||||
|
default = 0.1;
|
||||||
|
};
|
||||||
|
maximum-scale = mkOption {
|
||||||
|
type = types.numbers.between 0.1 10;
|
||||||
|
default = 10;
|
||||||
|
};
|
||||||
|
user-scalable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
interactive-widget = mkOption {
|
||||||
|
type = types.enum [
|
||||||
|
"resizes-visual"
|
||||||
|
"resizes-content"
|
||||||
|
"overlays-content"
|
||||||
|
];
|
||||||
|
default = "resizes-visual";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
meta.authors = mkOption {
|
||||||
|
type = with types; listOf str;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
meta.description = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
link.canonical = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
# TODO: figure out `meta` elements
|
||||||
|
# https://html.spec.whatwg.org/multipage/semantics.html#the-meta-element:concept-element-attributes
|
||||||
|
# https://html.spec.whatwg.org/multipage/semantics.html#other-metadata-names
|
||||||
};
|
};
|
||||||
|
|
||||||
config.categories = [ ];
|
config.categories = [ ];
|
||||||
config.__toString = self: print-element name self.attrs ''
|
config.__toString = self:
|
||||||
|
with lib;
|
||||||
|
print-element name self.attrs ''
|
||||||
${self.title}
|
${self.title}
|
||||||
${with lib; optionalString (!isNull self.base) self.base}
|
${with lib; optionalString (!isNull self.base) self.base}
|
||||||
${join "\n" (map (s: "${s}") self.content)}
|
<meta charset="${self.meta.charset}" />
|
||||||
|
|
||||||
|
${/* https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-x-ua-compatible */
|
||||||
|
""}<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="${join ", " (mapAttrsToList
|
||||||
|
(name: value: "${name}=${toString value}") self.meta.viewport)
|
||||||
|
}" />
|
||||||
|
|
||||||
|
${join "\n" (map
|
||||||
|
(author: ''<meta name="author" content="${author}" />'')
|
||||||
|
self.meta.authors)
|
||||||
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
title = { name, ... }: {
|
title = { name, ... }: {
|
||||||
# TODO
|
imports = [ element ];
|
||||||
|
options.text = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
config.categories = [ "metadata" ];
|
config.categories = [ "metadata" ];
|
||||||
|
config.__toString = self: "<${name}${print-attrs self.attrs}>${self.text}</${name}>";
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
base = { name, ... }: {
|
base = { name, ... }: {
|
||||||
# TODO
|
imports = [ element ];
|
||||||
|
# TODO: "A base element must have either an href attribute, a target attribute, or both."
|
||||||
|
attrs = mkAttrs { inherit (attrs) href target; };
|
||||||
config.categories = [ "metadata" ];
|
config.categories = [ "metadata" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
link = { name, ... }: {
|
||||||
|
imports = [ element ];
|
||||||
|
options = mkAttrs
|
||||||
|
{
|
||||||
|
# TODO: more attributes
|
||||||
|
# https://html.spec.whatwg.org/multipage/semantics.html#the-link-element:concept-element-attributes
|
||||||
|
inherit (attrs) href;
|
||||||
|
} // {
|
||||||
|
# XXX: there are variants of `rel` for `link`, `a`/`area`, and `form`
|
||||||
|
rel = mkOption {
|
||||||
|
# https://html.spec.whatwg.org/multipage/semantics.html#attr-link-rel
|
||||||
|
type = with types; listOfUnique str (enum
|
||||||
|
# TODO: work out link types in detail, there are lots of additional constraints
|
||||||
|
# https://html.spec.whatwg.org/multipage/links.html#linkTypes
|
||||||
|
[
|
||||||
|
"alternate"
|
||||||
|
"dns-prefetch"
|
||||||
|
"expect"
|
||||||
|
"help"
|
||||||
|
"icon"
|
||||||
|
"license"
|
||||||
|
"manifest"
|
||||||
|
"modulepreload"
|
||||||
|
"next"
|
||||||
|
"pingback"
|
||||||
|
"preconnect"
|
||||||
|
"prefetch"
|
||||||
|
"preload"
|
||||||
|
"prev"
|
||||||
|
"privacy-policy"
|
||||||
|
"search"
|
||||||
|
"stylesheet"
|
||||||
|
"terms-of-service"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# TODO: figure out how to make body-ok `link` elements
|
||||||
|
# https://html.spec.whatwg.org/multipage/semantics.html#allowed-in-the-body
|
||||||
|
config.categories = [ "metadata" ];
|
||||||
|
config.__toString = self: "<name${print-attrs self.attrs} />";
|
||||||
|
};
|
||||||
|
|
||||||
body = { name, ... }: {
|
body = { name, ... }: {
|
||||||
# TODO
|
imports = [ element ];
|
||||||
|
options = {
|
||||||
|
content = mkOption {
|
||||||
|
type = with types;
|
||||||
|
# HACK: bail out for now
|
||||||
|
# TODO: find a reasonable cut-off for where to place raw content
|
||||||
|
either str (listOf (attrTag categories.flow));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config.categories = [ ];
|
||||||
|
config.__toString = self: with lib;
|
||||||
|
if isList self.content then join "\n" (toString self.content) else self.content;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
|
@ -1,19 +1,5 @@
|
||||||
{ lib }:
|
{ lib }:
|
||||||
rec {
|
rec {
|
||||||
html = { head, body }: ''
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
${lib.indent " " head}
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
${lib.indent " " body}
|
|
||||||
<body>
|
|
||||||
</html>
|
|
||||||
'';
|
|
||||||
nav = menu: page:
|
nav = menu: page:
|
||||||
let
|
let
|
||||||
render-item = item:
|
render-item = item:
|
||||||
|
|
|
@ -22,8 +22,7 @@ in
|
||||||
type = with types; attrsOf (submodule config.content-types.page);
|
type = with types; attrsOf (submodule config.content-types.page);
|
||||||
};
|
};
|
||||||
|
|
||||||
options.collections = mkOption
|
options.collections = mkOption {
|
||||||
{
|
|
||||||
description = ''
|
description = ''
|
||||||
Named collections of unnamed pages
|
Named collections of unnamed pages
|
||||||
|
|
||||||
|
@ -66,8 +65,7 @@ in
|
||||||
example = [ "." ];
|
example = [ "." ];
|
||||||
default = [ config.name ];
|
default = [ config.name ];
|
||||||
};
|
};
|
||||||
entry = mkOption
|
entry = mkOption {
|
||||||
{
|
|
||||||
description = "An entry in the collection";
|
description = "An entry in the collection";
|
||||||
type = types.collection (types.submodule ({
|
type = types.collection (types.submodule ({
|
||||||
imports = [ config.type ];
|
imports = [ config.type ];
|
||||||
|
|
|
@ -46,6 +46,8 @@ in
|
||||||
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 = mkOption {
|
||||||
|
# TODO: figure out how to make this overridable at any granularity.
|
||||||
|
# it should be possible with the DOM module now.
|
||||||
description = ''
|
description = ''
|
||||||
Representations of the document in different formats
|
Representations of the document in different formats
|
||||||
'';
|
'';
|
||||||
|
|
Reference in a new issue