[Archived] Archived WIP of vielle.dev
1import type { Plugin } from "unified";
2import type {
3 Root,
4 Element,
5 Node,
6 ElementContent,
7 RootContent,
8 Text,
9} from "hast";
10type Options = {};
11
12/*
13 blockquote flags go in the first line
14 they are formatted as:
15 `$FLAG `, where `FLAG` is the flag name
16 there can only be one flag per blockquote
17*/
18
19function blockquote(node: Element) {
20 for (const child of node.children) {
21 if (child.type === "element" && child.children[0].type === "text") {
22 const flag = child.children[0].value.match(/(?<=^\$).*/gm);
23 if (flag?.length !== 1) continue;
24 child.children.shift();
25 // finiky to get types working bc shift mutation etc
26 if (
27 (child.children[0] as Node).type === "element" &&
28 (child.children[0] as Node as Element).tagName === "br"
29 )
30 child.children.shift();
31
32 node.properties[`data-bq-flag--${flag[0].toLowerCase()}`] = true;
33 }
34 }
35}
36
37/*
38 image flags go in alt text
39 if alt text has a pipe (|), there are flags
40 all words up to the first pipe are treated as flags in a space seperated list
41 they are added to the data tag as [data-img-flag--<name>="true"]
42*/
43
44function image(node: Element) {
45 // get alt; throw error if missing; convert to string
46 const alt = (
47 node.properties.alt ??
48 (() => {
49 throw new Error("NO ALT TEXT!!!");
50 })()
51 ).toString();
52
53 // match section before |
54 const prefixes = alt.match(/.*?(?= \|.*)/gm);
55 if (!prefixes) return;
56
57 node.properties.alt = alt.match(/(?<= \| ).*/gm);
58 const flags = prefixes[0].split(" ");
59 for (const flag of flags) {
60 node.properties[`data-img-flag--${flag}`] = true;
61 }
62}
63
64/*
65 paragraph flags go at the start
66 to use paragraph flags, include the following syntax at the start of a paragraph:
67 `::FLAG[::FLAG]*::`, where FLAG is a flag which is included as [data-para-flag--FLAG]
68 if FLAG starts with an `@`, it will be treated as a directive
69 current directives:
70 - @nest: replaces children with a p tag and moves children into it
71 - @div: replaces self with div
72*/
73
74function para(value: Text, parent: Element) {
75 const flags = value.value.match(/(?<=^::).*(?=::)/gm);
76
77 if (!flags) return;
78
79 const txt = value.value.match(/(?<=^::.*:: ).*/gm);
80 value.value =
81 !txt || txt.length !== 1 ? "Err: Parser Error (custom HTML)" : txt[0];
82
83 for (const flag of flags[0].split("::")) {
84 if (flag[0] === "@") {
85 switch (flag.slice(1)) {
86 case "nest": {
87 const prevChildren = parent.children;
88 parent.children = [
89 {
90 type: "element",
91 tagName: "p",
92 properties: {},
93 children: prevChildren,
94 } satisfies Element,
95 ];
96 break;
97 }
98 case "div": {
99 parent.tagName = "div";
100 break;
101 }
102 default: {
103 console.warn("Unknown paragraph directive:", flag);
104 }
105 }
106 } else parent.properties[`data-para-flag--${flag}`] = true;
107 }
108}
109
110const forChild = (children: ElementContent[] | RootContent[]) => {
111 for (const node of children) {
112 if (node.type !== "element") continue;
113
114 switch (node.tagName) {
115 case "blockquote": {
116 blockquote(node);
117 break;
118 }
119
120 case "img": {
121 image(node);
122 break;
123 }
124
125 case "p": {
126 if (node.children[0].type === "text") para(node.children[0], node);
127 forChild(node.children);
128 break;
129 }
130
131 case "pre": {
132 if (node.properties["tabindex"]) node.properties["tabindex"] = "-1";
133 break;
134 }
135
136 case "ul":
137 {
138 node.children.forEach((x) => {
139 if (x.type === "element" && x.tagName === "li") {
140 const contents = x.children.map((y) => {
141 if (
142 y.type === "element" &&
143 y.tagName === "input" &&
144 y.properties.type === "checkbox"
145 )
146 y.properties["aria-label"] = y.properties.checked
147 ? "Checked checkbox"
148 : "Unchecked checkbox";
149 return y;
150 });
151
152 x.children = [
153 {
154 type: "element",
155 tagName: "label",
156 properties: {},
157 children: contents,
158 },
159 ];
160 }
161 });
162 }
163
164 break;
165 }
166 }
167};
168
169const plugin: Plugin<[Options], Root> = function (options) {
170 return function (root, _) {
171 forChild(root.children);
172 };
173};
174
175export default plugin;