[Archived] Archived WIP of vielle.dev
at master 4.6 kB view raw
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;