// import { parseDOM } from 'htmlparser2' import { Node, DataNode, Element, NodeWithChildren } from 'domhandler' import * as entities from 'entities' const selfCloseTagMap = new Map([ ['area', true], ['base', true], ['br', true], ['col', true], ['embed', true], ['hr', true], ['img', true], ['input', true], ['link', true], ['meta', true], ['param', true], ['source', true], ['track', true], ['wbr', true], ['command', true], ['keygen', true], ['menuitem', true], ]) export interface GeneratorOptions { disableAttrEscape?: boolean dumpEmptyAttri?: boolean beforeEach?: (node: Node, parent: NodeWithChildren | null) => Node } const defaultOptions: GeneratorOptions = { disableAttrEscape: false, dumpEmptyAttri: false, beforeEach: undefined, } function escapeAttr(attr: string, disableAttrEscape: boolean = false) { if (!attr) return attr return disableAttrEscape ? attr : entities.escape(attr) } function dfs(node: Node[] | Node, options: GeneratorOptions): string { if (Array.isArray(node)) { return node.map(n => dfs(n, options)).join('') } if (options.beforeEach) { node = options.beforeEach(node, node.parent) } const dataNode = node as DataNode const el = node as Element // console.log(node) if (el.type === 'text') { return dataNode.data } /* else if(el.type === 'doctype') { // couldn't find doctype example return '' } */ else if (el.type === 'cdata') { return `` } else if (el.type === 'directive') { return `<${dataNode.data}>` } else if (el.type === 'comment') { return `` } else if (['style', 'script', 'tag'].includes(el.type)) { let ans = '<' + el.name if (el.attribs && Object.keys(el.attribs).length) { ans += ' ' + Object.keys(el.attribs).map(key => { const val = el.attribs[key] return key + (options.dumpEmptyAttri && !val ? '' : `="${escapeAttr(val, options.disableAttrEscape)}"`) }).join(' ') } if (el.children?.length) { ans += '>' + dfs(el.children, options) + `` } else { if (selfCloseTagMap.has(el.name)) { ans += '/>' } else { ans += `>` } } return ans } return '' } function generator(dom: Node[], userOptions: GeneratorOptions = {}): string { const options: GeneratorOptions = { ...defaultOptions, ...userOptions } return dfs(dom, options) } export default generator // export default generator // console.log(generator(parseDOM("Yay!"))) // console.log(generator(parseDOM(``))) // console.log(generator(parseDOM(``))) // console.log(generator(parseDOM(`John Smith]]>`, { recognizeCDATA: true, xmlMode: true }))) // console.log(generator(parseDOM( // `The TitleHello world` // ))) // javascript:alert('Haello'); // console.log(generator(parseDOM('<__proto__>'))) // console.log(generator(parseDOM("

")))