alejandra

This commit is contained in:
tristan 2024-01-02 10:53:45 +00:00
parent b1426fb1ca
commit dc33fdf093
14 changed files with 1029 additions and 976 deletions

View file

@ -37,11 +37,11 @@
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 1704187012, "lastModified": 1704192724,
"narHash": "sha256-td0PQa1cTvjurjBmLpCynAgDV425Mh8sRObRtbTGxiQ=", "narHash": "sha256-Cx9os5FQU4UTjxt/uE1trPHxegXyhvV/wSiIiBWTDt0=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "249bd9cda4fe9b02f3c85b22179109794eb57256", "rev": "b800bb46955d69cf7693d7c788a4d624eff2717f",
"revCount": 2, "revCount": 3,
"type": "git", "type": "git",
"url": "https://git.tristans.cloud/tristan/tix" "url": "https://git.tristans.cloud/tristan/tix"
}, },

View file

@ -1,14 +1,19 @@
{ {
description = "A site in nix?"; description = "A site in nix?";
inputs = { tix.url = "git+https://git.tristans.cloud/tristan/tix"; }; inputs = {tix.url = "git+https://git.tristans.cloud/tristan/tix";};
outputs = { self, nixpkgs, tix }: outputs = {
let self,
system = "x86_64-linux"; nixpkgs,
pkgs = import nixpkgs { inherit system; }; tix,
nixite = import ./nixite/. { inherit pkgs; }; }: let
in nixite // { system = "x86_64-linux";
pkgs = import nixpkgs {inherit system;};
nixite = import ./nixite/. {inherit pkgs;};
in
nixite
// {
packages.${system} = { packages.${system} = {
raw = nixite.mkSite (let raw = nixite.mkSite (let
readme = { readme = {
@ -16,33 +21,34 @@
content = nixite.md.mdToPage ./README.md; content = nixite.md.mdToPage ./README.md;
}; };
markup = { markup = {
"index.html" = with nixite.elems; "index.html" = with nixite.elems; let
let blue = nixite.style.component span "blue" {
blue = nixite.style.component span "blue" { style = {color = "blue";};
style = { color = "blue"; }; };
}; underblue = nixite.style.component blue "under" {
underblue = nixite.style.component blue "under" { style = {text-decoration = "underline";};
style = { text-decoration = "underline"; }; };
}; in (Doc {} [
in (Doc { } [ [(title "Nixite")]
[ (title "Nixite") ] (main [
(main [ (a {href = nixite.site.link readme;} "Readme")
(a { href = nixite.site.link readme; } "Readme") (a "/blog" "blog")
(a "/blog" "blog") (List {} ["item 1" "item 2" "item 3"])
(List { } [ "item 1" "item 2" "item 3" ]) (p [
(p [ "check out my"
"check out my" (blue "blue span")
(blue "blue span") "isn't it"
"isn't it" (underblue {onclick = "alert(1)";} "great!")
(underblue { onclick = "alert(1)"; } "great!")
])
]) ])
]); ])
]);
blog = nixite.md.readDir ./testing/blog; blog = nixite.md.readDir ./testing/blog;
}; };
site = (nixite.site.prepare { favicon = ./testing/src/favicon.png; } site =
markup); nixite.site.prepare {favicon = ./testing/src/favicon.png;}
in site); markup;
in
site);
default = nixite.serve self.packages.${system}.raw; default = nixite.serve self.packages.${system}.raw;
@ -54,7 +60,7 @@
./testing/style.test.nix ./testing/style.test.nix
]; ];
watch = tix.watch "nix run .#test --show-trace" "fx"; watch = tix.watch "nix run .#test | ${pkgs.fx}/bin/fx" "fx";
dev = tix.watch "nix run .# --show-trace" "caddy"; dev = tix.watch "nix run .# --show-trace" "caddy";
}; };

View file

@ -1,9 +1,8 @@
{ pkgs, ... }: { {pkgs, ...}: {
mkSite = import ./make-site.nix pkgs; mkSite = import ./make-site.nix pkgs;
serve = site: serve = site: (pkgs.writeShellScriptBin "serve" ''
(pkgs.writeShellScriptBin "serve" '' ${pkgs.caddy}/bin/caddy file-server --root ${site}
${pkgs.caddy}/bin/caddy file-server --root ${site} '');
'');
md = import ./md.nix; md = import ./md.nix;
html = import ./html.nix; html = import ./html.nix;
elems = import ./elems.nix; elems = import ./elems.nix;

View file

@ -1,4 +1,5 @@
let html = import ./html.nix; let
html = import ./html.nix;
in { in {
html = html.tag "html"; html = html.tag "html";
# Document metadata # Document metadata
@ -41,13 +42,15 @@ in {
main = html.tag "main"; main = html.tag "main";
div = html.tag "div"; div = html.tag "div";
# Text-level semantics # Text-level semantics
a = html.tag "a" // { a =
__functor = self: href: html.tag "a"
(if builtins.isString href then // {
(self // html.baseTag) { inherit href; } __functor = self: href: (
else if builtins.isString href
(self // html.baseTag) href); then (self // html.baseTag) {inherit href;}
}; else (self // html.baseTag) href
);
};
em = html.tag "em"; em = html.tag "em";
strong = html.tag "strong"; strong = html.tag "strong";
small = html.tag "small"; small = html.tag "small";
@ -130,24 +133,31 @@ in {
canvas = html.tag "canvas"; canvas = html.tag "canvas";
# custom # custom
List = html.tag "ul" // { List =
__functor = self: children: html.tag "ul"
(if builtins.isList children then // {
(self // html.baseTag) (map (html.tag "li") children) __functor = self: children: (
else if builtins.isList children
html.incorporateAttrs self children); then (self // html.baseTag) (map (html.tag "li") children)
}; else html.incorporateAttrs self children
);
};
Doc = params: child: Doc = params: child:
assert builtins.isList child; assert builtins.isList child;
assert builtins.length child == 2; assert builtins.length child == 2;
assert builtins.isList (builtins.elemAt child 0); assert builtins.isList (builtins.elemAt child 0);
html.tag "html" ({ lang = "en"; } // params) [ html.tag "html" ({lang = "en";} // params) [
(html.tag "head" { } (builtins.elemAt child 0)) (html.tag "head" {} (builtins.elemAt child 0))
(html.tag "body" { } (builtins.elemAt child 1)) (html.tag "body" {} (builtins.elemAt child 1))
]; ];
Stylesheet = params: Stylesheet = params:
html.tag "link" ({ html.tag "link" ({
rel = "stylesheet"; rel = "stylesheet";
} // (if builtins.isString params then { href = params; } else params)) ""; }
// (
if builtins.isString params
then {href = params;}
else params
)) "";
} }

View file

@ -1,70 +1,78 @@
let let
keyvalue = key: value: keyvalue = key: value:
assert builtins.isString key; assert builtins.isString key;
if builtins.isAttrs value then if builtins.isAttrs value
builtins.trace "Skipping ${key} as it is a set" "" then builtins.trace "Skipping ${key} as it is a set" ""
else if value == "" || value == [ ] || value == false then else if value == "" || value == [] || value == false
"" then ""
else if value == true then else if value == true
key then key
else else ''${key}="${toString value}"'';
''${key}="${toString value}"'';
in rec { in rec {
toHTML = elem: toHTML = elem:
if builtins.isString elem then if builtins.isString elem
elem then elem
else if builtins.isList elem then else if builtins.isList elem
builtins.toString (map toHTML elem) then builtins.toString (map toHTML elem)
else else "<${elem.tag} ${writeAttrs elem.attrs or {}}>${
"<${elem.tag} ${writeAttrs elem.attrs or { }}>${ toHTML elem.child or ""
toHTML elem.child or "" }</${elem.tag}>";
}</${elem.tag}>";
writeAttrs = attrs: writeAttrs = attrs:
toString (builtins.filter (value: value != "") (builtins.attrValues toString (builtins.filter (value: value != "") (builtins.attrValues
(builtins.mapAttrs (key: value: (builtins.mapAttrs (key: value:
if (builtins.isPath value) then if (builtins.isPath value)
keyvalue key (baseNameOf value) then keyvalue key (baseNameOf value)
else if (builtins.substring 0 2 key) == "__" then else if (builtins.substring 0 2 key) == "__"
"" then ""
else else keyvalue key value)
keyvalue key value) attrs))); attrs)));
tag = t: tag = t: ({
({
tag = t; tag = t;
attrs = { }; attrs = {};
} // baseTag); }
// baseTag);
baseTag = { baseTag = {
__toString = self: toString (self ""); __toString = self: toString (self "");
__functor = self: child: __functor = self: child: (
(if !(isTag child) then if !(isTag child)
(if isSet child then then
incorporateAttrs self child (
else if isSet child
throw "tag child must be tag, list, or string, got ${ then incorporateAttrs self child
builtins.typeOf child else
}") throw "tag child must be tag, list, or string, got ${
builtins.typeOf child
}"
)
else else
self // { self
// {
inherit child; inherit child;
__toString = toHTML; __toString = toHTML;
}); }
);
}; };
incorporateAttrs = self: attrs: self // ({ attrs = self.attrs // attrs; }); incorporateAttrs = self: attrs: self // {attrs = self.attrs // attrs;};
isSet = a: builtins.isAttrs a && !a ? __toString; isSet = a: builtins.isAttrs a && !a ? __toString;
isTag = tag: isTag = tag: (builtins.isString tag
(builtins.isString tag || builtins.isList tag || builtins.isList tag
|| (tag ? __toString && builtins.isFunction tag.__toString)); || (tag ? __toString && builtins.isFunction tag.__toString));
addToHead = page: heads: addToHead = page: heads:
page // { page
child = map // {
(e: if e.tag == "head" then e // { child = e.child ++ heads; } else e) child =
map
(e:
if e.tag == "head"
then e // {child = e.child ++ heads;}
else e)
page.child; page.child;
}; };
} }

View file

@ -1,16 +1,16 @@
{ stdenv, ... }: {stdenv, ...}: raw: let
raw: site = import ./site.nix;
let site = import ./site.nix; in
in stdenv.mkDerivation { stdenv.mkDerivation {
name = "site"; name = "site";
src = ./.; src = ./.;
buildPhase = '' buildPhase = ''
mkdir dist mkdir dist
${site.copyTo "dist" raw} ${site.copyTo "dist" raw}
''; '';
installPhase = '' installPhase = ''
cp -r dist $out cp -r dist $out
''; '';
} }

View file

@ -1,57 +1,55 @@
let elems = import ./elems.nix; let
elems = import ./elems.nix;
in rec { in rec {
readMd = md: readMd = md:
if builtins.isPath md then if builtins.isPath md
processMd (builtins.readFile md) then processMd (builtins.readFile md)
else else processMd md;
processMd md;
processMd = processStr; processMd = processStr;
recReadMd = root: recReadMd = root:
assert builtins.isPath root; assert builtins.isPath root;
builtins.mapAttrs (path: type: builtins.mapAttrs (path: type:
if type == "directory" then if type == "directory"
recReadMd (root + (/. + path)) then recReadMd (root + (/. + path))
else if type == "regular" then else if type == "regular"
mdToPage (root + (/. + path)) then mdToPage (root + (/. + path))
else else throw "Cannot read ${path}, file type ${type}") (builtins.readDir root);
throw "Cannot read ${path}, file type ${type}") (builtins.readDir root);
recFixAppendix = site: recFixAppendix = site:
builtins.listToAttrs (builtins.attrValues (builtins.mapAttrs (name: value: { builtins.listToAttrs (builtins.attrValues (builtins.mapAttrs (name: value: {
name = fixAppendix name; name = fixAppendix name;
value = if builtins.isAttrs value then recFixAppendix value else value; value =
}) site)); if builtins.isAttrs value
then recFixAppendix value
else value;
})
site));
fixAppendix = builtins.replaceStrings [ ".md" ] [ ".html" ]; fixAppendix = builtins.replaceStrings [".md"] [".html"];
readDir = root: recFixAppendix (recReadMd root); readDir = root: recFixAppendix (recReadMd root);
mdToPage = md: mdToPage = md:
elems.Doc { } [ [ (elems.title { } "markdown file") ] (readMd md) ]; elems.Doc {} [[(elems.title {} "markdown file")] (readMd md)];
code = block:
matchThen "(```)(.*)(```)" block (m: elems.code (builtins.elemAt m 1));
list = block: list = block:
matchThen ('' matchThen "(- .+\n)*(- .+\n?)"
(- .+ block (m: elems.List (splitList block));
)*(- .+
?)'') block (m: elems.List (splitList block));
splitList = block: splitList = block:
map (listItem) (builtins.filter (s: builtins.isString s && s != "") map listItem (builtins.filter (s: builtins.isString s && s != "")
(builtins.split "\n" block)); (builtins.split "\n" block));
listItem = str: listItem = str: let
let li = builtins.match "- (.*)" str;
li = builtins.match "- (.*)" str; checkbox = builtins.match "- \\[(.)] (.*)" str;
checkbox = builtins.match "- \\[(.)] (.*)" str; checked = builtins.elemAt checkbox 0;
checked = builtins.elemAt checkbox 0; content = builtins.elemAt checkbox 1;
content = builtins.elemAt checkbox 1; in
in if checkbox == null then if checkbox == null
li then li
else [ else [
(elems.input { (elems.input {
type = "checkbox"; type = "checkbox";
@ -61,28 +59,41 @@ in rec {
content content
]; ];
replace = regex: apply: block: replace = regex: apply: block: (let
(let m = builtins.match regex block;
m = builtins.match regex block; before = let
before = let v = builtins.elemAt m 0; in if v == null then "" else v; v = builtins.elemAt m 0;
after = toString (builtins.elemAt m (matchCount - 1)); in
matchCount = builtins.length m; if v == null
in if m == null then then ""
block else v;
else after = toString (builtins.elemAt m (matchCount - 1));
(replace regex apply before) + (apply m) + after); matchCount = builtins.length m;
in
if m == null
then block
else (replace regex apply before) + (apply m) + after);
rule = matcher: apply: blocks: rule = matcher: apply: blocks:
map (b: if builtins.isString b then replace matcher apply b else b) blocks; map (b:
if builtins.isString b
then replace matcher apply b
else b)
blocks;
applyRules = i: rules: input: applyRules = i: rules: input: let
let group =
group = if builtins.isString input then [ input ] else input; if builtins.isString input
len = builtins.length rules; then [input]
rule = builtins.elemAt rules i; else input;
next = i + 1; len = builtins.length rules;
in assert i < len; rule = builtins.elemAt rules i;
if next < len then rule (applyRules next rules group) else rule group; next = i + 1;
in
assert i < len;
if next < len
then rule (applyRules next rules group)
else rule group;
basicRule = matcher: elem: rule matcher (m: elem (builtins.elemAt m 1)); basicRule = matcher: elem: rule matcher (m: elem (builtins.elemAt m 1));
@ -96,60 +107,27 @@ in rec {
(basicRule (wrap "~~") elems.del) (basicRule (wrap "~~") elems.del)
(basicRule (wrap "\\*\\*") elems.strong) (basicRule (wrap "\\*\\*") elems.strong)
(basicRule (wrapBreak "__") elems.strong) (basicRule (wrapBreak "__") elems.strong)
(rule (contains "\\[(.*)]\\((.*)\\)") (m: (rule (contains "\\[(.*)]\\((.*)\\)") (m: let
let href = builtins.elemAt m 2;
href = builtins.elemAt m 2; text = builtins.elemAt m 1;
text = builtins.elemAt m 1; in (elems.a href text)))
in (elems.a href text))) (rule "(.*\n)([^-][^\n]+\n)((- [^\n]+\n)+)(.*)" (
(rule ('' l: (elems.ul (basicRule "(.*\n)?- ([^\n]+)\n(.*)" (m:
(.* elems.li (basicRule "()\\[(.)](.*)" (check:
)([^-][^ elems.input {
]+ type = "checkbox";
)((- [^ checked = check != " ";
]+ disabled = true;
)+)(.*)'') ( }) [m])) [(builtins.elemAt l 2)]))
))
l: (basicRule "(.*\n\n)?(.+)\n(.*)?" elems.p)
(elems.ul (basicRule ('' (basicRule "(.*\n\n)?```(.*)```(.*)?" (elems.textarea {readonly = true;}))
(.* (basicRule (containsBreak "###### ([^\n]+)") (elems.h6))
)?- ([^ (basicRule (containsBreak "##### ([^\n]+)") (elems.h5))
]+) (basicRule (containsBreak "#### ([^\n]+)") (elems.h4))
(.*)'') (m: (basicRule (containsBreak "### ([^\n]+)") (elems.h3))
elems.li (basicRule ("()\\[(.)](.*)") (check: (basicRule (containsBreak "## ([^\n]+)") (elems.h2))
elems.input { (basicRule (containsBreak "# ([^\n]+)") (elems.h1))
type = "checkbox";
checked = check != " ";
disabled = true;
}) [ m ])) [ (builtins.elemAt l 2) ]))
))
(basicRule (''
(.*
)?(.+)
(.*)?'') elems.p)
(basicRule (''
(.*
)?```(.*)```(.*)?'') (elems.textarea { readonly = true; }))
(basicRule (containsBreak ''
###### ([^
]+)'') (elems.h6))
(basicRule (containsBreak ''
##### ([^
]+)'') (elems.h5))
(basicRule (containsBreak ''
#### ([^
]+)'') (elems.h4))
(basicRule (containsBreak ''
### ([^
]+)'') (elems.h3))
(basicRule (containsBreak ''
## ([^
]+)'') (elems.h2))
(basicRule (containsBreak ''
# ([^
]+)'') (elems.h1))
(basicRule (containsBreak "<(${linkmatcher})>") (m: elems.a m m)) (basicRule (containsBreak "<(${linkmatcher})>") (m: elems.a m m))
]; ];
@ -160,10 +138,11 @@ in rec {
containsBreak = matcher: "(.*[[:space:]])?${matcher}(.*)"; containsBreak = matcher: "(.*[[:space:]])?${matcher}(.*)";
wrapBreak = matcher: containsBreak "${matcher}([^${matcher}]+)${matcher}"; wrapBreak = matcher: containsBreak "${matcher}([^${matcher}]+)${matcher}";
matchThen = matcher: block: func: matchThen = matcher: block: func: let
let m = builtins.match matcher block; m = builtins.match matcher block;
in if m == null then in
dontMatch block if m == null
then dontMatch block
else { else {
matched = true; matched = true;
block = func m; block = func m;
@ -173,5 +152,4 @@ in rec {
matched = false; matched = false;
inherit block; inherit block;
}; };
} }

View file

@ -3,13 +3,17 @@ let
elems = import ./elems.nix; elems = import ./elems.nix;
style = import ./style.nix; style = import ./style.nix;
in rec { in rec {
prepare = { favicon ? null, styles ? { }, }: prepare = {
site: favicon ? null,
let styles ? {},
nullFn = p: p; }: site: let
allStyles = styles // getStyles site; nullFn = p: p;
doFavicon = if favicon != null then (applyFavicon favicon) else nullFn; allStyles = styles // getStyles site;
in (extractPaths (extractLinks (applyStyle allStyles (doFavicon site)))); doFavicon =
if favicon != null
then (applyFavicon favicon)
else nullFn;
in (extractPaths (extractLinks (applyStyle allStyles (doFavicon site))));
getStyles = site: getStyles = site:
builtins.zipAttrsWith (name: value: builtins.elemAt value 0) builtins.zipAttrsWith (name: value: builtins.elemAt value 0)
@ -17,27 +21,25 @@ in rec {
flatten = site: flatten = site:
map (page: map (page:
if builtins.isAttrs page && page ? "__toString" then if builtins.isAttrs page && page ? "__toString"
page then page
else if builtins.isAttrs page then else if builtins.isAttrs page
flatten page then flatten page
else else page) (builtins.attrValues site);
page) (builtins.attrValues site);
applyStyle = style: site: ((linkStyle site) // { "style.css" = style; }); applyStyle = style: site: ((linkStyle site) // {"style.css" = style;});
linkStyle = site: linkStyle = site:
eachPage site eachPage site
(content: html.addToHead content [ (elems.Stylesheet "/style.css") ]); (content: html.addToHead content [(elems.Stylesheet "/style.css")]);
eachPage = site: callback: eachPage = site: callback: (builtins.mapAttrs (name: content:
(builtins.mapAttrs (name: content: if builtins.isAttrs content && content ? "__toString"
if builtins.isAttrs content && content ? "__toString" then then callback content
callback content else if builtins.isAttrs content
else if builtins.isAttrs content then then eachPage content callback
eachPage content callback else content)
else site);
content) site);
applyFavicon = icon: site: applyFavicon = icon: site:
eachPage site (content: eachPage site (content:
@ -49,19 +51,21 @@ in rec {
]); ]);
extractPaths = content: extractPaths = content:
switchPaths content // { switchPaths content
static = (content.static or { }) // getPaths content; // {
static = (content.static or {}) // getPaths content;
}; };
extractLinks = content: extractLinks = content:
switchLinks content // { switchLinks content
static = (content.static or { }) // getLinks content; // {
static = (content.static or {}) // getLinks content;
}; };
isLink = value: isLink = value:
builtins.isAttrs value && value ? type && value.type == "link"; builtins.isAttrs value && value ? type && value.type == "link";
switchLinks = runDeep (isLink) (value: "/static/" + value.name); switchLinks = runDeep isLink (value: "/static/" + value.name);
getLinks = content: (builtins.listToAttrs (getLinksKV content)); getLinks = content: (builtins.listToAttrs (getLinksKV content));
@ -72,19 +76,30 @@ in rec {
}; };
runLink = f: runLink = f:
if !builtins.isFunction f then if !builtins.isFunction f
null then null
else else
(let res = f { }; in (if !builtins.isAttrs res then null else res)); (let
res = f {};
in (
if !builtins.isAttrs res
then null
else res
));
runDeep = when: do: set: runDeep = when: do: set: (
(if builtins.isAttrs set then if builtins.isAttrs set
then
builtins.mapAttrs builtins.mapAttrs
(key: value: if when value then do value else runDeep when do value) set (key: value:
else if builtins.isList set then if when value
(map (runDeep when do) set) then do value
else else runDeep when do value)
set); set
else if builtins.isList set
then (map (runDeep when do) set)
else set
);
switchPaths = runDeep builtins.isPath (value: "/static/" + baseNameOf value); switchPaths = runDeep builtins.isPath (value: "/static/" + baseNameOf value);
@ -96,35 +111,60 @@ in rec {
value = v: v; value = v: v;
}; };
getAllKV = { when, key, value }@params: getAllKV = {
path: when,
(if when path then [{ key,
name = key path; value,
value = value path; } @ params: path: (
}] else if builtins.isAttrs path then if when path
then [
{
name = key path;
value = value path;
}
]
else if builtins.isAttrs path
then
builtins.concatMap (getAllKV params) (builtins.attrValues builtins.concatMap (getAllKV params) (builtins.attrValues
(builtins.mapAttrs (builtins.mapAttrs
(n: v: if (builtins.substring 0 2 n == "__") then null else v) path)) (n: v:
else if builtins.isList path then if (builtins.substring 0 2 n == "__")
builtins.concatMap (getAllKV params) path then null
else else v)
[ ]); path))
else if builtins.isList path
then builtins.concatMap (getAllKV params) path
else []
);
link = { name, content }@page: page // { type = "link"; }; link = {
name,
content,
} @ page:
page // {type = "link";};
copyTo = prefix: site: copyTo = prefix: site:
builtins.toString (builtins.attrValues (builtins.mapAttrs (name: content: builtins.toString (builtins.attrValues (builtins.mapAttrs (name: content:
if builtins.isString content then '' if builtins.isString content
then ''
cp ${builtins.toFile name content} ${prefix}/${name} cp ${builtins.toFile name content} ${prefix}/${name}
'' else if builtins.isPath content then '' ''
else if builtins.isPath content
then ''
cp -r ${content} ${prefix}/${name} cp -r ${content} ${prefix}/${name}
'' else if builtins.isAttrs content && content ? "__toString" then '' ''
else if builtins.isAttrs content && content ? "__toString"
then ''
cp ${builtins.toFile name (toString content)} ${prefix}/${name} cp ${builtins.toFile name (toString content)} ${prefix}/${name}
'' else if builtins.isAttrs content then '' ''
else if builtins.isAttrs content
then ''
mkdir -p ${prefix}/${name} mkdir -p ${prefix}/${name}
${copyTo "${prefix}/${name}" content} ${copyTo "${prefix}/${name}" content}
'' else ''
else
throw "Site page must be string, path or attrset, but got ${ throw "Site page must be string, path or attrset, but got ${
builtins.typeOf content builtins.typeOf content
}: [${toString content}]") site)); }: [${toString content}]")
site));
} }

View file

@ -1,57 +1,63 @@
let let
styleToString = identifier: styles: styleToString = identifier: styles:
if !builtins.isAttrs styles then if !builtins.isAttrs styles
"" then ""
else if styles == { } then else if styles == {}
"" then ""
else else
assert builtins.isString identifier; '' assert builtins.isString identifier; ''
${identifier} { ${identifier} {
${ ${
toString (builtins.attrValues toString (builtins.attrValues
(builtins.mapAttrs (key: value: "${key}: ${value};") styles)) (builtins.mapAttrs (key: value: "${key}: ${value};") styles))
} }
} }
''; '';
getStyle = element: getStyle = element:
if builtins.isAttrs element && element.attrs ? __id then if builtins.isAttrs element && element.attrs ? __id
then
({ ({
${element.attrs.__id} = element.attrs.style or { }; ${element.attrs.__id} = element.attrs.style or {};
} // (if element.attrs ? __extends then { }
${element.attrs.__extends.attrs.__id or element.attrs.__extends.tag} = // (
element.attrs.__extends.attrs.style or { }; if element.attrs ? __extends
} else then {
{ })) ${element.attrs.__extends.attrs.__id or element.attrs.__extends.tag} =
else element.attrs.__extends.attrs.style or {};
{ }; }
else {}
))
else {};
getStyles = element: getStyles = element:
(getStyle element) // (if builtins.isList element then (getStyle element)
getStylesFromList element // (
else if element ? child then if builtins.isList element
getStyles element.child then getStylesFromList element
else else if element ? child
{ }) // { then getStyles element.child
__toString = stylesToString; else {}
}; )
// {
__toString = stylesToString;
};
getStylesFromList = elements: getStylesFromList = elements:
builtins.zipAttrsWith (name: value: builtins.elemAt value 0) builtins.zipAttrsWith (name: value: builtins.elemAt value 0)
(map getStyles elements); (map getStyles elements);
mkProps = tag: class: props: mkProps = tag: class: props:
props // { props
// {
__id = "${tag.tag}.${class}"; __id = "${tag.tag}.${class}";
__extends = tag; __extends = tag;
class = (tag.attrs.class or [ ]) ++ [ class ] ++ (props.class or [ ]); class = (tag.attrs.class or []) ++ [class] ++ (props.class or []);
}; };
stylesToString = styles: stylesToString = styles:
builtins.concatStringsSep "" builtins.concatStringsSep ""
(builtins.attrValues (builtins.mapAttrs (styleToString) styles)); (builtins.attrValues (builtins.mapAttrs styleToString styles));
in { in {
inherit getStyle getStyles stylesToString; inherit getStyle getStyles stylesToString;

View file

@ -1,83 +1,86 @@
{ it, ... }: {it, ...}: let
let
elems = import ../nixite/elems.nix; elems = import ../nixite/elems.nix;
style = import ../nixite/style.nix; style = import ../nixite/style.nix;
html = import ../nixite/html.nix; html = import ../nixite/html.nix;
in with elems; [ in
(it "makes a p tag" { with elems; [
expected = html.tag "p" { } "foobar"; (it "makes a p tag" {
actual = p { } "foobar"; expected = html.tag "p" {} "foobar";
asString = true; actual = p {} "foobar";
}) asString = true;
(it "makes a div tag" { })
expected = html.tag "div" { } "foobar"; (it "makes a div tag" {
actual = div { } "foobar"; expected = html.tag "div" {} "foobar";
asString = true; actual = div {} "foobar";
}) asString = true;
(it "makes a section tag" { })
expected = html.tag "section" { } "foobar"; (it "makes a section tag" {
actual = section { } "foobar"; expected = html.tag "section" {} "foobar";
asString = true; actual = section {} "foobar";
}) asString = true;
(it "makes a span tag" { })
expected = html.tag "span" { } "foobar"; (it "makes a span tag" {
actual = span { } "foobar"; expected = html.tag "span" {} "foobar";
asString = true; actual = span {} "foobar";
}) asString = true;
(it "makes a main tag" { })
expected = html.tag "main" { } [ "yeet" ]; (it "makes a main tag" {
actual = main { } [ "yeet" ]; expected = html.tag "main" {} ["yeet"];
asString = true; actual = main {} ["yeet"];
}) asString = true;
(it "makes a title tag" { })
expected = html.tag "title" { } "foobar"; (it "makes a title tag" {
actual = title { } "foobar"; expected = html.tag "title" {} "foobar";
asString = true; actual = title {} "foobar";
}) asString = true;
})
(it "makes an a tag" { (it "makes an a tag" {
expected = html.tag "a" { href = "https://example.com"; } "example"; expected = html.tag "a" {href = "https://example.com";} "example";
actual = a { href = "https://example.com"; } "example"; actual = a {href = "https://example.com";} "example";
asString = true; asString = true;
}) })
(it "lets the a tag drop the props" { (it "lets the a tag drop the props" {
expected = html.tag "a" { href = "https://example.com"; } "example"; expected = html.tag "a" {href = "https://example.com";} "example";
actual = a "https://example.com" "example"; actual = a "https://example.com" "example";
asString = true; asString = true;
}) })
(it "lets you extend the a tag" { (it "lets you extend the a tag" {
expected = [ "linky" ]; expected = ["linky"];
actual = ((style.component a "linky" { }) "https://example.com" actual =
"example").attrs.class; ((style.component a "linky" {}) "https://example.com"
asString = true; "example")
}) .attrs
.class;
asString = true;
})
(it "makes a stylesheet link" { (it "makes a stylesheet link" {
expected = html.tag "link" { expected = html.tag "link" {
href = "/style"; href = "/style";
rel = "stylesheet"; rel = "stylesheet";
} ""; } "";
actual = Stylesheet { href = "/style"; }; actual = Stylesheet {href = "/style";};
asString = true; asString = true;
}) })
(it "makes a list" { (it "makes a list" {
expected = html.tag "ul" { __ = ""; } [ expected = html.tag "ul" {__ = "";} [
(html.tag "li" { } "foo") (html.tag "li" {} "foo")
(html.tag "li" { } "bar") (html.tag "li" {} "bar")
(html.tag "li" { } "baz") (html.tag "li" {} "baz")
]; ];
actual = List { } [ "foo" "bar" "baz" ]; actual = List {} ["foo" "bar" "baz"];
asString = true; asString = true;
}) })
(it "makes an html doc" { (it "makes an html doc" {
expected = html.tag "html" { expected = html.tag "html" {
__child = ""; __child = "";
class = [ ]; class = [];
lang = "en"; lang = "en";
} [ (html.tag "head" { } [ "foo" ]) (html.tag "body" { } "bar") ]; } [(html.tag "head" {} ["foo"]) (html.tag "body" {} "bar")];
actual = Doc { } [ [ "foo" ] "bar" ]; actual = Doc {} [["foo"] "bar"];
asString = true; asString = true;
}) })
] ]

View file

@ -1,142 +1,149 @@
{ it, ... }: {it, ...}: let
let html = import ../nixite/html.nix; html = import ../nixite/html.nix;
in with html; [ in
with html; [
(it "keeps info in the tag" (let
p = tag "p";
in {
actual = p.tag;
expected = "p";
}))
(it "keeps info in the tag" (let p = tag "p"; (it "keeps attr info in the tag" (let
in { p = tag "p" {class = "";};
actual = p.tag; in {
expected = "p"; actual = p.attrs;
})) expected = {class = "";};
}))
(it "keeps attr info in the tag" (let p = tag "p" { class = ""; }; (it "keeps tag after setting attrs" (let
in { p = tag "p" {class = "";};
actual = p.attrs; in {
expected = { class = ""; }; actual = p.tag;
})) expected = "p";
}))
(it "keeps tag after setting attrs" (let p = tag "p" { class = ""; }; (it "makes a p tag" {
in { actual = tag "p" {} "Hello";
actual = p.tag; expected = {
expected = "p"; tag = "p";
})) attrs = {};
child = "Hello";
};
removeDunders = true;
})
(it "makes a p tag" { (it "makes element" (let
actual = tag "p" { } "Hello"; para = tag "p" {};
expected = { in {
tag = "p"; expected = "p";
attrs = { }; actual = para.tag;
child = "Hello"; }))
};
removeDunders = true;
})
(it "makes element" (let para = (tag "p" { }); (it "keeps attrs on element" (let
in { attrs = {style = {foo = "bar";};};
expected = "p"; para = tag "p" attrs;
actual = para.tag; in {
})) expected = attrs;
actual = para.attrs;
}))
(it "keeps attrs on element" (let (it "makes renderable element" (let
attrs = { style = { foo = "bar"; }; }; attrs = {style = {foo = "bar";};};
para = (tag "p" attrs); para = tag "p" attrs;
in { in {
expected = attrs; expected = "<p ></p>";
actual = para.attrs; actual = toString (para "");
})) }))
(it "makes renderable element" (let (it "keeps tag" (let
attrs = { style = { foo = "bar"; }; }; attrs = {style = {foo = "bar";};};
para = (tag "p" attrs); para = tag "p" attrs;
in { in {
expected = "<p ></p>"; expected = "p";
actual = toString (para ""); actual = (para "").tag;
})) }))
(it "keeps tag" (let (it "keeps style" (let
attrs = { style = { foo = "bar"; }; }; attrs = {style = {foo = "bar";};};
para = (tag "p" attrs); para = tag "p" attrs;
in { in {
expected = "p"; expected = {foo = "bar";};
actual = (para "").tag; actual = (para "").attrs.style;
})) }))
(it "keeps style" (let (it "needs no args to make string" (let
attrs = { style = { foo = "bar"; }; }; p = tag "p";
para = (tag "p" attrs); in {
in { actual = toString p;
expected = { foo = "bar"; }; expected = "<p ></p>";
actual = (para "").attrs.style; }))
}))
(it "needs no args to make string" (let p = tag "p"; (it "needs no content to make string" (let
in { p = tag "p";
actual = toString p; in {
expected = "<p ></p>"; actual = toString (p {class = "foobar";});
})) expected = ''<p class="foobar"></p>'';
}))
(it "needs no content to make string" (let p = tag "p"; (it "can take many sets of props" (let
in { p = tag "p";
actual = toString (p { class = "foobar"; }); in {
expected = ''<p class="foobar"></p>''; actual = toString (p {class = "foobar";} {style = "a style";});
})) expected = ''<p class="foobar" style="a style"></p>'';
}))
(it "can take many sets of props" (let p = tag "p"; (it "works recursively" (let
in { attrs = {style = {foo = "bar";};};
actual = toString (p { class = "foobar"; } { style = "a style"; }); para = tag "p" attrs;
expected = ''<p class="foobar" style="a style"></p>''; a = tag "a" {};
})) in {
expected = "<p ><a >hello</a></p>";
actual = toString (para (a "hello"));
}))
(it "works recursively" (let (it "throws with function child" {
attrs = { style = { foo = "bar"; }; }; actual = toString (tag "p" (i: ""));
para = (tag "p" attrs); throws = true;
a = (tag "a" { }); })
in {
expected = "<p ><a >hello</a></p>";
actual = toString (para (a "hello"));
}))
(it "throws with function child" ({ (it "throws with a number" {
actual = toString (tag "p" (i: "")); actual = toString (tag "p" 5);
throws = true; throws = true;
})) })
(it "throws with a number" ({ (it "throws with a bool" {
actual = toString (tag "p" 5); actual = toString (tag "p" true);
throws = true; throws = true;
})) })
(it "throws with a bool" ({ (it "throws with a null" {
actual = toString (tag "p" true); actual = toString (tag "p" null);
throws = true; throws = true;
})) })
(it "throws with a null" ({ (it "concatinates classes" {
actual = toString (tag "p" null); actual = toString (tag "p" {class = ["class1" "class2"];} "Hello");
throws = true; expected = ''<p class="class1 class2">Hello</p>'';
})) })
(it "concatinates classes" { (it "applies style" (let
actual = toString (tag "p" { class = [ "class1" "class2" ]; } "Hello"); page = tag "html" {} [(tag "head" {} ["foo"])];
expected = ''<p class="class1 class2">Hello</p>''; in {
}) actual = addToHead page ["bar"];
expected = {
tag = "html";
attrs = {};
child = [(tag "head" {} ["foo" "bar"])];
};
removeDunders = true;
}))
(it "applies style" (let page = tag "html" { } [ (tag "head" { } [ "foo" ]) ]; (it "renders on / off attrs" {
in { actual = toString (tag "p" {
actual = addToHead page [ "bar" ]; on = true;
expected = { off = false;
tag = "html"; });
attrs = { }; expected = "<p on></p>";
child = [ (tag "head" { } [ "foo" "bar" ]) ]; })
}; ]
removeDunders = true;
}))
(it "renders on / off attrs" ({
actual = toString (tag "p" {
on = true;
off = false;
});
expected = "<p on></p>";
}))
]

View file

@ -1,148 +1,148 @@
{ it, ... }: {it, ...}: let
let
md = import ../nixite/md.nix; md = import ../nixite/md.nix;
elems = import ../nixite/elems.nix; elems = import ../nixite/elems.nix;
in with md; [ in
with md; [
(it "matches a list of one element" {
actual = list ''
- something
'';
expected = {
matched = true;
block = elems.List ["something"];
};
asJSON = true;
})
(it "matches a list of one element" ({ (it "makes a list of many elements" {
actual = list '' actual = list ''
- something - something
''; - something else
expected = { '';
matched = true; expected = {
block = elems.List [ "something" ]; matched = true;
}; block = elems.List ["something" "something else"];
asJSON = true; };
})) asJSON = true;
})
(it "makes a list of many elements" ({ (it "makes a list of many checkboxes" {
actual = list '' actual = list ''
- something - [ ] something
- something else - [X] something else
''; '';
expected = { expected = {
matched = true; matched = true;
block = elems.List [ "something" "something else" ]; block = elems.List [
}; [
asJSON = true; (elems.input {
})) type = "checkbox";
disabled = true;
checked = false;
} "")
"something"
]
[
(elems.input {
type = "checkbox";
disabled = true;
checked = true;
} "")
"something else"
]
];
};
asJSON = true;
})
(it "makes a list of many checkboxes" ({ (it "matches a list with no whitespace around" {
actual = list '' actual = list ''
- [ ] something - something
- [X] something else - something else'';
''; expected = {
expected = { matched = true;
matched = true; block = elems.List ["something" "something else"];
block = elems.List [ };
[ asJSON = true;
(elems.input { })
type = "checkbox";
disabled = true; (it "doesnt match not a list" (let
checked = false; str = "blah blah";
} "") in {
"something" actual = list str;
] expected = {
[ matched = false;
(elems.input { block = str;
type = "checkbox"; };
disabled = true; }))
checked = true;
} "") (it "processes whole string with all rules" {
"something else" actual = processStr ''
] this text **may** *or may not* contain **bold** words *inside* it.
'';
expected = elems.p [
"this text"
(elems.strong "may")
(elems.em "or may not")
"contain"
(elems.strong "bold")
"words"
(elems.em "inside")
"it."
]; ];
}; asString = true;
asJSON = true; })
}))
(it "matches a list with no whitespace around" ({ (it "makes paragraphs" {
actual = list '' actual = readMd ''
- something lorem ipsum
- something else''; dolor sit
expected = {
matched = true;
block = elems.List [ "something" "something else" ];
};
asJSON = true;
}))
(it "doesnt match not a list" (let str = "blah blah"; foo bar
in { '';
actual = list str; expected = ''
expected = { <p >lorem ipsum
matched = false; dolor sit
block = str; </p><p >foo bar</p>'';
}; asString = true;
})) })
(it "processes whole string with all rules" ({ (it "can fix file appendixes" {
actual = processStr '' actual = fixAppendix "index.md";
this text **may** *or may not* contain **bold** words *inside* it. expected = "index.html";
''; })
expected = (elems.p [
"this text"
(elems.strong "may")
(elems.em "or may not")
"contain"
(elems.strong "bold")
"words"
(elems.em "inside")
"it."
]);
asString = true;
}))
(it "makes paragraphs" { (it "converts markdown to a page" {
actual = readMd '' actual = toString (mdToPage ./blog/index.md) + "\n";
lorem ipsum expected = builtins.readFile ./out/index.html;
dolor sit asString = true;
})
foo bar (it "recursively reads dir" {
''; actual = recReadMd ./blog;
expected = '' expected = {
<p >lorem ipsum "index.md" = mdToPage ./blog/index.md;
dolor sit "dir" = {"index.md" = mdToPage ./blog/dir/index.md;};
</p><p >foo bar</p>''; };
asString = true; asJSON = true;
}) })
(it "can fix file appendixes" { (it "recursively fixes filename" {
actual = fixAppendix "index.md"; actual = recFixAppendix {
expected = "index.html"; "index.md" = "something";
}) dir = {"index.md" = "something else";};
};
expected = {
"index.html" = "something";
dir = {"index.html" = "something else";};
};
})
(it "converts markdown to a page" { (it "recursively translates md to html" {
actual = toString (mdToPage ./blog/index.md) + "\n"; actual = builtins.toJSON (readDir ./blog);
expected = builtins.readFile ./out/index.html; expected = builtins.toJSON {
asString = true; "index.html" = mdToPage ./blog/index.md;
}) "dir" = {"index.html" = mdToPage ./blog/dir/index.md;};
};
(it "recursively reads dir" { })
actual = recReadMd ./blog; ]
expected = {
"index.md" = mdToPage ./blog/index.md;
"dir" = { "index.md" = mdToPage ./blog/dir/index.md; };
};
asJSON = true;
})
(it "recursively fixes filename" {
actual = recFixAppendix {
"index.md" = "something";
dir = { "index.md" = "something else"; };
};
expected = {
"index.html" = "something";
dir = { "index.html" = "something else"; };
};
})
(it "recursively translates md to html" {
actual = builtins.toJSON (readDir ./blog);
expected = builtins.toJSON {
"index.html" = mdToPage ./blog/index.md;
"dir" = { "index.html" = mdToPage ./blog/dir/index.md; };
};
})
]

View file

@ -1,315 +1,313 @@
{ it, ... }: {it, ...}: let
let
html = import ../nixite/html.nix; html = import ../nixite/html.nix;
elems = import ../nixite/elems.nix; elems = import ../nixite/elems.nix;
site = import ../nixite/site.nix; site = import ../nixite/site.nix;
style = import ../nixite/style.nix; style = import ../nixite/style.nix;
in with site; [ in
(it "applies a style" { with site; [
expected = { (it "applies a style" {
"index.html" = html.tag "html" { } [ expected = {
(html.tag "head" { } [ "index.html" = html.tag "html" {} [
(elems.title { } "foobar") (html.tag "head" {} [
(elems.Stylesheet "/style.css") (elems.title {} "foobar")
])
(elems.main { } "something")
];
blog = {
"index.html" = html.tag "html" { } [
(html.tag "head" { } [
(elems.title { } "foobar")
(elems.Stylesheet "/style.css") (elems.Stylesheet "/style.css")
]) ])
(elems.main { } "blogy blog") (elems.main {} "something")
]; ];
blog = {
"index.html" = html.tag "html" {} [
(html.tag "head" {} [
(elems.title {} "foobar")
(elems.Stylesheet "/style.css")
])
(elems.main {} "blogy blog")
];
};
"style.css" = ''
this is a stylesheet
'';
}; };
"style.css" = '' actual =
this is a stylesheet applyStyle ''
''; this is a stylesheet
}; '' {
actual = applyStyle '' "index.html" = html.tag "html" {} [
this is a stylesheet (html.tag "head" {} [(elems.title {} "foobar")])
'' { (elems.main {} "something")
"index.html" = html.tag "html" { } [ ];
(html.tag "head" { } [ (elems.title { } "foobar") ]) blog = {
(elems.main { } "something") "index.html" = html.tag "html" {} [
]; (html.tag "head" {} [(elems.title {} "foobar")])
blog = { (elems.main {} "blogy blog")
"index.html" = html.tag "html" { } [ ];
(html.tag "head" { } [ (elems.title { } "foobar") ]) };
(elems.main { } "blogy blog") };
]; asJSON = true;
}; })
};
asJSON = true;
})
(it "applies a favicon" { (it "applies a favicon" {
expected = { expected = {
"index.html" = elems.html { } [ "index.html" = elems.html {} [
(elems.head { } [ (elems.head {} [
(elems.title { } "foobar") (elems.title {} "foobar")
(elems.link {
rel = "shortcut icon";
href = ./src/favicon.png;
})
])
(elems.main { } "something")
];
blog = {
"index.html" = elems.html { } [
(elems.head { } [
(elems.title { } "foobar")
(elems.link { (elems.link {
rel = "shortcut icon"; rel = "shortcut icon";
href = ./src/favicon.png; href = ./src/favicon.png;
}) })
]) ])
(elems.main { } "something") (elems.main {} "something")
]; ];
blog = {
"index.html" = elems.html {} [
(elems.head {} [
(elems.title {} "foobar")
(elems.link {
rel = "shortcut icon";
href = ./src/favicon.png;
})
])
(elems.main {} "something")
];
};
}; };
}; actual = applyFavicon ./src/favicon.png {
actual = applyFavicon ./src/favicon.png { "index.html" = elems.html {} [
"index.html" = elems.html { } [ (elems.head {} [(elems.title {} "foobar")])
(elems.head { } [ (elems.title { } "foobar") ]) (elems.main {} "something")
(elems.main { } "something")
];
blog = {
"index.html" = elems.html { } [
(elems.head { } [ (elems.title { } "foobar") ])
(elems.main { } "something")
]; ];
blog = {
"index.html" = elems.html {} [
(elems.head {} [(elems.title {} "foobar")])
(elems.main {} "something")
];
};
}; };
}; asJSON = true;
asJSON = true; })
})
(it "gets all styles" { (it "gets all styles" {
expected = { expected = {
"p" = { }; "p" = {};
"div" = { }; "div" = {};
"p.class" = { color = "blue"; }; "p.class" = {color = "blue";};
"div.class2" = { color = "green"; }; "div.class2" = {color = "green";};
}; };
actual = getStyles (let actual = getStyles (let
p = style.component elems.p "class" { style = { color = "blue"; }; }; p = style.component elems.p "class" {style = {color = "blue";};};
g = style.component elems.div "class2" { style = { color = "green"; }; }; g = style.component elems.div "class2" {style = {color = "green";};};
in {
"index.html" = p "";
blog = {"index.html" = g "";};
});
removeDunders = true;
})
(it "gets top level paths" {
actual = getPaths {
something = "";
src = ./src/index.md;
};
expected = {"index.md" = ./src/index.md;};
})
(it "gets lower level paths" {
actual = getPaths {
something = "yes";
a-list = [
{thingy = ./src/index.md;}
[(html.tag "img" {src = ./src/favicon.png;} "")]
];
};
expected = {
"index.md" = ./src/index.md;
"favicon.png" = ./src/favicon.png;
};
})
(it "switches paths" {
actual = switchPaths {
something = "";
a-thing = {src = ./src/index.md;};
a-list = [{thingy = ./src/index.md;}];
};
expected = {
something = "";
a-thing = {src = "/static/index.md";};
a-list = [{thingy = "/static/index.md";}];
};
})
(it "extracts paths" {
actual = extractPaths {
something = "";
a-thing = {src = ./src/index.md;};
a-list = [{thingy = ./src/index.md;}];
};
expected = {
something = "";
a-thing = {src = "/static/index.md";};
a-list = [{thingy = "/static/index.md";}];
static = {"index.md" = ./src/index.md;};
};
})
(it "switches links" (let
coolPage = {
type = "link";
name = "cool-page";
content = "";
};
in { in {
"index.html" = p ""; actual = switchLinks {
blog = { "index.html" = g ""; }; something = "";
}); a-thing = {src = coolPage;};
removeDunders = true; a-list = [{thingy = coolPage;}];
}) };
expected = {
something = "";
a-thing = {src = "/static/cool-page";};
a-list = [{thingy = "/static/cool-page";}];
};
}))
(it "gets top level paths" { (it "gets links" (let
actual = getPaths { coolPage = {
something = ""; type = "link";
src = ./src/index.md; name = "cool-page";
}; content = "cool content";
expected = { "index.md" = ./src/index.md; }; };
}) otherPage = {
(it "gets lower level paths" { type = "link";
actual = getPaths { name = "page2";
something = "yes"; content = "stuff";
a-list = [ };
{ thingy = ./src/index.md; } in {
[ (html.tag "img" { src = ./src/favicon.png; } "") ] actual = getLinks {
]; something = "yes";
}; a-list = [{thingy = coolPage;} [(elems.img {src = otherPage;} "")]];
expected = { };
"index.md" = ./src/index.md; expected = {
"favicon.png" = ./src/favicon.png;
};
})
(it "switches paths" {
actual = switchPaths {
something = "";
a-thing = { src = ./src/index.md; };
a-list = [{ thingy = ./src/index.md; }];
};
expected = {
something = "";
a-thing = { src = "/static/index.md"; };
a-list = [{ thingy = "/static/index.md"; }];
};
})
(it "extracts paths" {
actual = extractPaths {
something = "";
a-thing = { src = ./src/index.md; };
a-list = [{ thingy = ./src/index.md; }];
};
expected = {
something = "";
a-thing = { src = "/static/index.md"; };
a-list = [{ thingy = "/static/index.md"; }];
static = { "index.md" = ./src/index.md; };
};
})
(it "switches links" (let
coolPage = {
type = "link";
name = "cool-page";
content = "";
};
in {
actual = switchLinks {
something = "";
a-thing = { src = coolPage; };
a-list = [{ thingy = coolPage; }];
};
expected = {
something = "";
a-thing = { src = "/static/cool-page"; };
a-list = [{ thingy = "/static/cool-page"; }];
};
}))
(it "gets links" (let
coolPage = {
type = "link";
name = "cool-page";
content = "cool content";
};
otherPage = {
type = "link";
name = "page2";
content = "stuff";
};
in {
actual = getLinks {
something = "yes";
a-list =
[ { thingy = coolPage; } [ (elems.img { src = otherPage; } "") ] ];
};
expected = {
"cool-page" = "cool content";
"page2" = "stuff";
};
}))
(it "extracts links" (let
coolPage = {
type = "link";
name = "cool-page";
content = "cool content";
};
otherPage = {
type = "link";
name = "page2";
content = "stuff";
};
in {
actual = extractLinks {
something = "";
a-thing = { src = coolPage; };
a-list = [{ thingy = otherPage; }];
};
expected = {
something = "";
a-thing = { src = "/static/cool-page"; };
a-list = [{ thingy = "/static/page2"; }];
static = {
"cool-page" = "cool content"; "cool-page" = "cool content";
"page2" = "stuff"; "page2" = "stuff";
}; };
}; }))
}))
(it "copies all the files" ({ (it "extracts links" (let
actual = copyTo "." { coolPage = {
page = "this is a page"; type = "link";
subdir = { name = "cool-page";
page = "this page is in a subdir"; content = "cool content";
link = ./src;
}; };
}; otherPage = {
expected = '' type = "link";
cp /nix/store/crirfz0n6f8dgl1si3x7pwyw7fqm0r8l-page ./page name = "page2";
mkdir -p ./subdir content = "stuff";
cp -r /nix/store/q95cn7ccixzi9w22aic4bl0ykk40ka7v-src ./subdir/link };
cp /nix/store/ic6fyy8wg8r4338a3m5kinmg11igxsyj-page ./subdir/page in {
actual = extractLinks {
something = "";
a-thing = {src = coolPage;};
a-list = [{thingy = otherPage;}];
};
expected = {
something = "";
a-thing = {src = "/static/cool-page";};
a-list = [{thingy = "/static/page2";}];
static = {
"cool-page" = "cool content";
"page2" = "stuff";
};
};
}))
''; (it "copies all the files" {
})) actual = copyTo "." {
page = "this is a page";
subdir = {
page = "this page is in a subdir";
link = ./src;
};
};
expected = ''
cp /nix/store/crirfz0n6f8dgl1si3x7pwyw7fqm0r8l-page ./page
mkdir -p ./subdir
cp -r /nix/store/q95cn7ccixzi9w22aic4bl0ykk40ka7v-src ./subdir/link
cp /nix/store/ic6fyy8wg8r4338a3m5kinmg11igxsyj-page ./subdir/page
(it "throws with a list for a page" ({ '';
actual = copyTo "." { page = [ ]; }; })
throws = true;
}))
(it "throws with null for a page" ({ (it "throws with a list for a page" {
actual = copyTo "." { page = null; }; actual = copyTo "." {page = [];};
throws = true; throws = true;
})) })
(it "throws with a bool for a page" ({ (it "throws with null for a page" {
actual = copyTo "." { page = true; }; actual = copyTo "." {page = null;};
throws = true; throws = true;
})) })
(it "throws with a number for a page" ({ (it "throws with a bool for a page" {
actual = copyTo "." { page = 5; }; actual = copyTo "." {page = true;};
throws = true; throws = true;
})) })
(it "prepares the site" ({ (it "throws with a number for a page" {
actual = prepare { favicon = ./src/favicon.png; } { actual = copyTo "." {page = 5;};
"index.html" = elems.html { } [ throws = true;
(elems.head { } [ (elems.title { } "foobar") ]) })
(elems.main { } [
(elems.a { (it "prepares the site" {
href = { actual = prepare {favicon = ./src/favicon.png;} {
type = "link"; "index.html" = elems.html {} [
name = "a-page"; (elems.head {} [(elems.title {} "foobar")])
content = "this is another page"; (elems.main {} [
}; (elems.a {
} "A Page") href = {
]) type = "link";
]; name = "a-page";
blog = { content = "this is another page";
"index.html" = elems.html { } [ };
(elems.head { } [ (elems.title { } "foobar") ]) } "A Page")
(elems.main { } "something") ])
]; ];
blog = {
"index.html" = elems.html {} [
(elems.head {} [(elems.title {} "foobar")])
(elems.main {} "something")
];
};
}; };
}; expected = {
expected = { "index.html" = elems.html {} [
"index.html" = elems.html { } [ (elems.head {} [
(elems.head { } [ (elems.title {} "foobar")
(elems.title { } "foobar")
(elems.link {
rel = "shortcut icon";
href = "/static/favicon.png";
})
(elems.Stylesheet "/style.css")
])
(elems.main { } [ (elems.a { href = "/static/a-page"; } "A Page") ])
];
blog = {
"index.html" = elems.html { } [
(elems.head { } [
(elems.title { } "foobar")
(elems.link { (elems.link {
rel = "shortcut icon"; rel = "shortcut icon";
href = "/static/favicon.png"; href = "/static/favicon.png";
}) })
(elems.Stylesheet "/style.css") (elems.Stylesheet "/style.css")
]) ])
(elems.main { } "something") (elems.main {} [(elems.a {href = "/static/a-page";} "A Page")])
]; ];
blog = {
"index.html" = elems.html {} [
(elems.head {} [
(elems.title {} "foobar")
(elems.link {
rel = "shortcut icon";
href = "/static/favicon.png";
})
(elems.Stylesheet "/style.css")
])
(elems.main {} "something")
];
};
static = {
"favicon.png" = ./src/favicon.png;
"a-page" = "this is another page";
};
"style.css" = "";
}; };
static = { asJSON = true;
"favicon.png" = ./src/favicon.png; })
"a-page" = "this is another page"; ]
};
"style.css" = "";
};
asJSON = true;
}))
]

View file

@ -1,54 +1,53 @@
{ it, ... }: {it, ...}: let
let
style = import ../nixite/style.nix; style = import ../nixite/style.nix;
elems = import ../nixite/elems.nix; elems = import ../nixite/elems.nix;
in [ in [
(it "fetches empty style" (let
(it "fetches empty style" (let para = (style.component elems.p "para" { }); para = style.component elems.p "para" {};
in { in {
expected = { expected = {
"p" = { }; "p" = {};
"p.para" = { }; "p.para" = {};
}; };
actual = style.getStyle (para ""); actual = style.getStyle (para "");
})) }))
(it "fetches style" (let (it "fetches style" (let
attrs = { style = { foo = "bar"; }; }; attrs = {style = {foo = "bar";};};
para = (style.component elems.p "para" attrs); para = style.component elems.p "para" attrs;
in { in {
expected = { expected = {
"p" = { }; "p" = {};
"p.para" = attrs.style; "p.para" = attrs.style;
}; };
actual = style.getStyle (para ""); actual = style.getStyle (para "");
})) }))
(it "appliess class" (let (it "appliess class" (let
attrs = { style = { foo = "bar"; }; }; attrs = {style = {foo = "bar";};};
para = (style.component elems.p "para" attrs); para = style.component elems.p "para" attrs;
in { in {
expected = [ "para" ]; expected = ["para"];
actual = (para "").attrs.class; actual = (para "").attrs.class;
})) }))
(it "applies classes from props" (let (it "applies classes from props" (let
attrs = { attrs = {
style = { foo = "bar"; }; style = {foo = "bar";};
class = [ "other" "class" ]; class = ["other" "class"];
}; };
para = (style.component elems.p "para" attrs); para = style.component elems.p "para" attrs;
in { in {
expected = [ "para" "other" "class" ]; expected = ["para" "other" "class"];
actual = (para "").attrs.class; actual = (para "").attrs.class;
})) }))
(it "fetches style for class" (let (it "fetches style for class" (let
s = { foo = "bar"; }; s = {foo = "bar";};
para = (style.component elems.p "para" { style = s; }); para = style.component elems.p "para" {style = s;};
in { in {
expected = { expected = {
"p" = { }; "p" = {};
"p.para" = s; "p.para" = s;
}; };
actual = style.getStyle (para ""); actual = style.getStyle (para "");
@ -56,13 +55,13 @@ in [
(it "fetches style recursively" (let (it "fetches style recursively" (let
s = { s = {
"p" = { }; "p" = {};
"div" = { }; "div" = {};
"p.para" = { foo = "bar"; }; "p.para" = {foo = "bar";};
"div.link" = { this = "that"; }; "div.link" = {this = "that";};
}; };
para = (style.component elems.p "para" { style = s."p.para"; }); para = style.component elems.p "para" {style = s."p.para";};
div = (style.component elems.div "link" { style = s."div.link"; }); div = style.component elems.div "link" {style = s."div.link";};
in { in {
expected = s; expected = s;
actual = style.getStyles (para (div "hello")); actual = style.getStyles (para (div "hello"));
@ -71,39 +70,39 @@ in [
(it "fetches style recursively through lists" (let (it "fetches style recursively through lists" (let
s = { s = {
"p" = { }; "p" = {};
"div" = { }; "div" = {};
"p.para" = { foo = "bar"; }; "p.para" = {foo = "bar";};
"div.link" = { this = "that"; }; "div.link" = {this = "that";};
}; };
para = (style.component elems.p "para" { style = s."p.para"; }); para = style.component elems.p "para" {style = s."p.para";};
a = (style.component elems.div "link" { style = s."div.link"; }); a = style.component elems.div "link" {style = s."div.link";};
in { in {
expected = s; expected = s;
actual = style.getStyles (para [ (a "hello") ]); actual = style.getStyles (para [(a "hello")]);
removeDunders = true; removeDunders = true;
})) }))
(it "fetches style recursively with repeats" (let (it "fetches style recursively with repeats" (let
s = { s = {
"p" = { }; "p" = {};
"div" = { }; "div" = {};
"p.para" = { foo = "bar"; }; "p.para" = {foo = "bar";};
"div.link" = { this = "that"; }; "div.link" = {this = "that";};
}; };
para = (style.component elems.p "para" { style = s."p.para"; }); para = style.component elems.p "para" {style = s."p.para";};
a = (style.component elems.div "link" { style = s."div.link"; }); a = style.component elems.div "link" {style = s."div.link";};
in { in {
expected = s; expected = s;
actual = style.getStyles (para [ (a "hello") (a "hello") ]); actual = style.getStyles (para [(a "hello") (a "hello")]);
removeDunders = true; removeDunders = true;
})) }))
(it "converts styles to string" (let (it "converts styles to string" (let
s = { s = {
"p" = { }; "p" = {};
"p.para" = { foo = "bar"; }; "p.para" = {foo = "bar";};
"a.link" = { this = "that"; }; "a.link" = {this = "that";};
}; };
in { in {
expected = '' expected = ''
@ -119,11 +118,11 @@ in [
(it "extends styled tags" (let (it "extends styled tags" (let
s = { s = {
"p.para" = { foo = "bar"; }; "p.para" = {foo = "bar";};
"p.oof" = { oof = "yes"; }; "p.oof" = {oof = "yes";};
}; };
para = (style.component elems.p "para" { style = s."p.para"; }); para = style.component elems.p "para" {style = s."p.para";};
para2 = (style.component para "oof" { style = s."p.oof"; }); para2 = style.component para "oof" {style = s."p.oof";};
in { in {
expected = s; expected = s;
actual = style.getStyles (para2 ""); actual = style.getStyles (para2 "");
@ -132,16 +131,15 @@ in [
(it "extends styled tags classes" (let (it "extends styled tags classes" (let
s = { s = {
"p" = { }; "p" = {};
"div" = { }; "div" = {};
"p.para" = { foo = "bar"; }; "p.para" = {foo = "bar";};
"p.para.oof" = { oof = "yes"; }; "p.para.oof" = {oof = "yes";};
}; };
para = (style.component elems.p "para" { style = s."p.para"; }); para = style.component elems.p "para" {style = s."p.para";};
para2 = (style.component para "oof" { style = s."p.para.oof"; }); para2 = style.component para "oof" {style = s."p.para.oof";};
in { in {
expected = [ "para" "oof" ]; expected = ["para" "oof"];
actual = (para2 "").attrs.class; actual = (para2 "").attrs.class;
})) }))
] ]