122 lines
3.5 KiB
Plaintext
122 lines
3.5 KiB
Plaintext
|
import { parse, html as __html, walkSync, ELEMENT_NODE, DOCUMENT_NODE, TEXT_NODE, } from "ultrahtml";
|
||
|
import inlineCSS from "ultrahtml/transformers/inline";
|
||
|
const TW_NAMES = new Set([
|
||
|
/[mp](t|b|r|l|x|y)?-/,
|
||
|
`color-`,
|
||
|
`flex`,
|
||
|
`h-`,
|
||
|
`w-`,
|
||
|
`min-w-`,
|
||
|
`min-h-`,
|
||
|
`max-w-`,
|
||
|
`max-h-`,
|
||
|
`leading-`,
|
||
|
`text-`,
|
||
|
`opacity-`,
|
||
|
`font-`,
|
||
|
`aspect-`,
|
||
|
`tint-`,
|
||
|
`bg-`,
|
||
|
`opacity-`,
|
||
|
`shadow-`,
|
||
|
`rounded`,
|
||
|
`top-`,
|
||
|
`right-`,
|
||
|
`bottom-`,
|
||
|
`left-`,
|
||
|
`inset-`,
|
||
|
`border`,
|
||
|
`elevation-`,
|
||
|
`tracking-`,
|
||
|
`z-`,
|
||
|
]);
|
||
|
const inliner = inlineCSS({ useObjectSyntax: true });
|
||
|
const tw = (doc) => {
|
||
|
walkSync(doc, (node) => {
|
||
|
if (node.type !== ELEMENT_NODE)
|
||
|
return;
|
||
|
if (node.attributes.class && !node.attributes.tw) {
|
||
|
const classNames = node.attributes.class.split(/\s+/);
|
||
|
let match = false;
|
||
|
for (const name of TW_NAMES) {
|
||
|
if (match)
|
||
|
break;
|
||
|
for (const item of classNames) {
|
||
|
if (match)
|
||
|
break;
|
||
|
if (item.indexOf(":") > -1) {
|
||
|
match = true;
|
||
|
}
|
||
|
else if (typeof name === "string") {
|
||
|
match = item.startsWith(name);
|
||
|
}
|
||
|
else {
|
||
|
match = name.test(item);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (match) {
|
||
|
node.attributes.tw = node.attributes.class;
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
const camelize = (ident) => ident.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
||
|
export function html(templates, ...expressions) {
|
||
|
const result = __html.call(null, templates, ...expressions);
|
||
|
let doc = parse(result.value.trim());
|
||
|
inliner(doc);
|
||
|
tw(doc);
|
||
|
const nodeMap = new WeakMap();
|
||
|
let root = {
|
||
|
type: "div",
|
||
|
props: {
|
||
|
style: {
|
||
|
display: "flex",
|
||
|
flexDirection: "column",
|
||
|
width: "100%",
|
||
|
height: "100%",
|
||
|
},
|
||
|
children: [],
|
||
|
},
|
||
|
};
|
||
|
walkSync(doc, (node, parent, index) => {
|
||
|
let newNode = {};
|
||
|
if (node.type === DOCUMENT_NODE) {
|
||
|
nodeMap.set(node, root);
|
||
|
}
|
||
|
else if (node.type === ELEMENT_NODE) {
|
||
|
newNode.type = node.name;
|
||
|
const { style, "": _, ...props } = node.attributes;
|
||
|
if (typeof style === "object") {
|
||
|
props["style"] = {};
|
||
|
for (const [decl, value] of Object.entries(style)) {
|
||
|
props["style"][camelize(decl)] = value;
|
||
|
}
|
||
|
}
|
||
|
props.children = [];
|
||
|
Object.assign(newNode, { props });
|
||
|
nodeMap.set(node, newNode);
|
||
|
if (parent) {
|
||
|
const newParent = nodeMap.get(parent);
|
||
|
newParent.props.children[index] = newNode;
|
||
|
}
|
||
|
}
|
||
|
else if (node.type === TEXT_NODE) {
|
||
|
newNode = node.value.trim();
|
||
|
if (newNode.trim()) {
|
||
|
if (parent) {
|
||
|
const newParent = nodeMap.get(parent);
|
||
|
if (parent.children.length === 1) {
|
||
|
newParent.props.children = newNode;
|
||
|
}
|
||
|
else {
|
||
|
newParent.props.children[index] = newNode;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
return root;
|
||
|
}
|