1import { html, type TreeAdapter, type TreeAdapterTypeMap, type Token } from 'parse5';
2
3function getSerializedTreeIndent(indent: number): string {
4    return '|'.padEnd(indent + 2, ' ');
5}
6
7function getElementSerializedNamespaceURI<T extends TreeAdapterTypeMap>(
8    element: T['element'],
9    treeAdapter: TreeAdapter<T>
10): string {
11    switch (treeAdapter.getNamespaceURI(element)) {
12        case html.NS.SVG: {
13            return 'svg ';
14        }
15        case html.NS.MATHML: {
16            return 'math ';
17        }
18        default: {
19            return '';
20        }
21    }
22}
23
24function serializeNodeList<T extends TreeAdapterTypeMap>(
25    nodes: T['node'][],
26    indent: number,
27    treeAdapter: TreeAdapter<T>
28): string {
29    let str = '';
30
31    for (let node of nodes) {
32        str += getSerializedTreeIndent(indent);
33
34        if (treeAdapter.isCommentNode(node)) {
35            str += `<!-- ${treeAdapter.getCommentNodeContent(node)} -->\n`;
36        } else if (treeAdapter.isTextNode(node)) {
37            str += `"${treeAdapter.getTextNodeContent(node)}"\n`;
38        } else if (treeAdapter.isDocumentTypeNode(node)) {
39            const publicId = treeAdapter.getDocumentTypeNodePublicId(node);
40            const systemId = treeAdapter.getDocumentTypeNodeSystemId(node);
41
42            str += `<!DOCTYPE ${treeAdapter.getDocumentTypeNodeName(node) || ''}`;
43
44            if (publicId || systemId) {
45                str += ` "${publicId}" "${systemId}"`;
46            }
47
48            str += '>\n';
49        } else {
50            const tn = treeAdapter.getTagName(node);
51
52            str += `<${getElementSerializedNamespaceURI(node, treeAdapter) + tn}>\n`;
53
54            let childrenIndent = indent + 2;
55            const serializedAttrs = treeAdapter.getAttrList(node).map((attr: Token.Attribute) => {
56                let attrStr = getSerializedTreeIndent(childrenIndent);
57
58                if (attr.prefix) {
59                    attrStr += `${attr.prefix} `;
60                }
61
62                attrStr += `${attr.name}="${attr.value}"\n`;
63
64                return attrStr;
65            });
66
67            str += serializedAttrs.sort().join('');
68
69            if (tn === html.TAG_NAMES.TEMPLATE && treeAdapter.getNamespaceURI(node) === html.NS.HTML) {
70                str += `${getSerializedTreeIndent(childrenIndent)}content\n`;
71                childrenIndent += 2;
72                node = treeAdapter.getTemplateContent(node);
73            }
74
75            str += serializeNodeList(treeAdapter.getChildNodes(node), childrenIndent, treeAdapter);
76        }
77    }
78
79    return str;
80}
81
82export function serializeToDatFileFormat<T extends TreeAdapterTypeMap>(
83    rootNode: T['parentNode'],
84    treeAdapter: TreeAdapter<T>
85): string {
86    return serializeNodeList(treeAdapter.getChildNodes(rootNode), 0, treeAdapter);
87}
88