-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathflake.nix
109 lines (86 loc) · 3.77 KB
/
flake.nix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
{
description = "Write composeable HTML with Nix!";
inputs.nixpkgslib.url = "github:nix-community/nixpkgs.lib";
outputs = { self, nixpkgslib }: let
inherit (nixpkgslib) lib;
first = n: lib.substring 0 n;
dropFirst = n: string: lib.substring n (lib.stringLength string - n) string;
last = n: string: lib.substring (lib.stringLength string - n) n string;
dropLast = n: string: lib.substring 0 (lib.stringLength string - n) string;
escapix = import ./escape.nix lib;
inherit (escapix) escape;
attrsetToHtmlAttrs = attrs:
lib.concatStringsSep " "
(lib.mapAttrsToList (k: v: ''${k}="${escape (toString v)}"'') attrs);
dottedNameToTag = name:
if first 1 name == "."
then "</${dropFirst 1 name}>"
else if last 1 name == "."
then "<${dropLast 1 name}/>"
else "<${name}>";
# When doing <name>, these won't return HTML tags.
propagatedFindFiles = [ "nixpkgs" ];
in {
inherit (escapix) raw;
inherit lib;
call = builtins.scopedImport self;
DOCTYPE = self.__findFile __nixPath "!DOCTYPE html";
result = self.call /${builtins.getEnv "TARGET_FILE"};
__findFile = nixPath: name: if builtins.elem name propagatedFindFiles then __findFile nixPath name else {
outPath = dottedNameToTag name;
__functor = this: next:
# Is a list. Consume each item. Treat it as if it was passed in one by one.
if lib.isList next
then lib.foldl' (this: this) (this (lib.head next)) (lib.tail next)
# We are passed in a functor/function that doesn't have an outPath meaning
# it is not a HTML tag. This means the user forgot to call it.
else if lib.isFunction next && !(next ? outPath)
then throw ''
You probably didn't mean to pass a function into the tag,
and forgot some parenthesis to actually call the function.
This is a common mistake, which usually looks like this:
<p>raw "Foo Bar Baz"<.p>
You probably meant to write something like this:
<p>(raw "Foo Bar Baz")<.p>
''
# Not an attrset, list or a function.
# Just add it onto the HTML after stringifying it.
else if !lib.isAttrs next
then this // {
outPath = (toString this) + escape (toString next);
}
# An attrset. But not a tag. This means it must be HTML attributes.
# We need to insert it right before the '>' or '/>' at the end of our string
# and error if it doesn't end with a tag.
#
# Due to how it is implemented, passing multiple attrsets to a single
# tag to combine them works. Here is an example:
#
# <foo>{bar="baz";}{fizz="fuzz";}
#
# This will output the following HTML:
#
# <foo bar="baz" fizz="fuzz">
else if lib.isAttrs next && !(next ? outPath)
then let
lastElementIsTag = last 1 (toString this) == ">";
lastElementIsSelfClosing = last 2 (toString this) == "/>";
in this // {
outPath = let
attrs = attrsetToHtmlAttrs next;
in if !lastElementIsTag then
throw "Attributes must come right after a tag: '${if attrs != "" then attrs else "<empty attrs>"}'"
else
(dropLast (if lastElementIsSelfClosing then 2 else 1) (toString this))
+ (if attrs != "" then " " else "") # Keep it pretty.
+ attrs
+ (if lastElementIsSelfClosing then "/>" else ">");
}
# The next element is a tag with the `outPath` attribute which means it's a
# start, closing or self closing tag. Just append it onto our string.
else this // {
outPath = "${this}${next}";
};
};
};
}