# generated by patch-package 6.4.7 on 2021-05-12 20:36:44
#
# command:
#   npx patch-package node-html-parser
#
# declared package:
#   node-html-parser: github:taoqf/node-fast-html-parser#60ea8fee51f07fbc712b5642a0496f12748eb90f
#
# this patch will
#
#  * add source location to nodes
#
diff --git a/node_modules/node-html-parser/.eslintrc.json b/node_modules/node-html-parser/.eslintrc.json
index 118d441..a2ba748 100644
--- a/node_modules/node-html-parser/.eslintrc.json
+++ b/node_modules/node-html-parser/.eslintrc.json
@@ -11,8 +11,7 @@
 		"plugin:import/errors",
 		"plugin:import/warnings",
 		"plugin:import/typescript",
-		"prettier",
-		"prettier/@typescript-eslint"
+		"prettier"
 	],
 	"parser": "@typescript-eslint/parser",
 	"parserOptions": {
diff --git a/node_modules/node-html-parser/dist/back.d.ts b/node_modules/node-html-parser/dist/back.d.ts
new file mode 100644
index 0000000..a04d14b
--- /dev/null
+++ b/node_modules/node-html-parser/dist/back.d.ts
@@ -0,0 +1 @@
+export default function arr_back<T>(arr: T[]): T;
diff --git a/node_modules/node-html-parser/dist/back.js b/node_modules/node-html-parser/dist/back.js
new file mode 100644
index 0000000..ba0664a
--- /dev/null
+++ b/node_modules/node-html-parser/dist/back.js
@@ -0,0 +1,6 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+function arr_back(arr) {
+    return arr[arr.length - 1];
+}
+exports.default = arr_back;
diff --git a/node_modules/node-html-parser/dist/esm/back.js b/node_modules/node-html-parser/dist/esm/back.js
new file mode 100644
index 0000000..166870f
--- /dev/null
+++ b/node_modules/node-html-parser/dist/esm/back.js
@@ -0,0 +1,3 @@
+export default function arr_back(arr) {
+    return arr[arr.length - 1];
+}
diff --git a/node_modules/node-html-parser/dist/esm/index.js b/node_modules/node-html-parser/dist/esm/index.js
new file mode 100644
index 0000000..ca6abea
--- /dev/null
+++ b/node_modules/node-html-parser/dist/esm/index.js
@@ -0,0 +1,7 @@
+export { default as CommentNode } from './nodes/comment';
+export { default as HTMLElement } from './nodes/html';
+export { default as parse, default } from './parse';
+export { default as valid } from './valid';
+export { default as Node } from './nodes/node';
+export { default as TextNode } from './nodes/text';
+export { NodeType } from './nodes/type';
diff --git a/node_modules/node-html-parser/dist/esm/matcher.js b/node_modules/node-html-parser/dist/esm/matcher.js
new file mode 100644
index 0000000..c16fe4c
--- /dev/null
+++ b/node_modules/node-html-parser/dist/esm/matcher.js
@@ -0,0 +1,101 @@
+import { NodeType } from './nodes/type';
+function isTag(node) {
+    return node && node.nodeType === NodeType.ELEMENT_NODE;
+}
+function getAttributeValue(elem, name) {
+    return isTag(elem) ? elem.getAttribute(name) : undefined;
+}
+function getName(elem) {
+    return ((elem && elem.rawTagName) || '').toLowerCase();
+}
+function getChildren(node) {
+    return node && node.childNodes;
+}
+function getParent(node) {
+    return node ? node.parentNode : null;
+}
+function getText(node) {
+    return node.text;
+}
+function removeSubsets(nodes) {
+    let idx = nodes.length;
+    let node;
+    let ancestor;
+    let replace;
+    // Check if each node (or one of its ancestors) is already contained in the
+    // array.
+    while (--idx > -1) {
+        node = ancestor = nodes[idx];
+        // Temporarily remove the node under consideration
+        nodes[idx] = null;
+        replace = true;
+        while (ancestor) {
+            if (nodes.indexOf(ancestor) > -1) {
+                replace = false;
+                nodes.splice(idx, 1);
+                break;
+            }
+            ancestor = getParent(ancestor);
+        }
+        // If the node has been found to be unique, re-insert it.
+        if (replace) {
+            nodes[idx] = node;
+        }
+    }
+    return nodes;
+}
+function existsOne(test, elems) {
+    return elems.some((elem) => {
+        return isTag(elem) ? test(elem) || existsOne(test, getChildren(elem)) : false;
+    });
+}
+function getSiblings(node) {
+    const parent = getParent(node);
+    return parent && getChildren(parent);
+}
+function hasAttrib(elem, name) {
+    return getAttributeValue(elem, name) !== undefined;
+}
+function findOne(test, elems) {
+    let elem = null;
+    for (let i = 0, l = elems.length; i < l && !elem; i++) {
+        const el = elems[i];
+        if (test(el)) {
+            elem = el;
+        }
+        else {
+            const childs = getChildren(el);
+            if (childs && childs.length > 0) {
+                elem = findOne(test, childs);
+            }
+        }
+    }
+    return elem;
+}
+function findAll(test, nodes) {
+    let result = [];
+    for (let i = 0, j = nodes.length; i < j; i++) {
+        if (!isTag(nodes[i]))
+            continue;
+        if (test(nodes[i]))
+            result.push(nodes[i]);
+        const childs = getChildren(nodes[i]);
+        if (childs)
+            result = result.concat(findAll(test, childs));
+    }
+    return result;
+}
+export default {
+    isTag,
+    getAttributeValue,
+    getName,
+    getChildren,
+    getParent,
+    getText,
+    removeSubsets,
+    existsOne,
+    getSiblings,
+    hasAttrib,
+    findOne,
+    findAll
+};
diff --git a/node_modules/node-html-parser/dist/esm/nodes/comment.js b/node_modules/node-html-parser/dist/esm/nodes/comment.js
new file mode 100644
index 0000000..3f657f1
--- /dev/null
+++ b/node_modules/node-html-parser/dist/esm/nodes/comment.js
@@ -0,0 +1,23 @@
+import Node from './node';
+import { NodeType } from './type';
+export default class CommentNode extends Node {
+    constructor(rawText, parentNode, nodeOptions) {
+        super(parentNode, nodeOptions);
+        this.rawText = rawText;
+        /**
+         * Node Type declaration.
+         * @type {Number}
+         */
+        this.nodeType = NodeType.COMMENT_NODE;
+    }
+    /**
+     * Get unescaped text value of current node and its children.
+     * @return {string} text content
+     */
+    get text() {
+        return this.rawText;
+    }
+    toString() {
+        return `<!--${this.rawText}-->`;
+    }
+}
diff --git a/node_modules/node-html-parser/dist/esm/nodes/html.js b/node_modules/node-html-parser/dist/esm/nodes/html.js
new file mode 100644
index 0000000..8aee7bd
--- /dev/null
+++ b/node_modules/node-html-parser/dist/esm/nodes/html.js
@@ -0,0 +1,860 @@
+import he from 'he';
+import { selectAll, selectOne } from 'css-select';
+import Node from './node';
+import { NodeType } from './type';
+import TextNode from './text';
+import Matcher from '../matcher';
+import arr_back from '../back';
+import CommentNode from './comment';
+import parse from '../parse';
+const { decode } = he;
+const kBlockElements = new Map();
+kBlockElements.set('DIV', true);
+kBlockElements.set('div', true);
+kBlockElements.set('P', true);
+kBlockElements.set('p', true);
+// ul: true,
+// ol: true,
+kBlockElements.set('LI', true);
+kBlockElements.set('li', true);
+// table: true,
+// tr: true,
+kBlockElements.set('TD', true);
+kBlockElements.set('td', true);
+kBlockElements.set('SECTION', true);
+kBlockElements.set('section', true);
+kBlockElements.set('BR', true);
+kBlockElements.set('br', true);
+/**
+ * HTMLElement, which contains a set of children.
+ *
+ * Note: this is a minimalist implementation, no complete tree
+ *   structure provided (no parentNode, nextSibling,
+ *   previousSibling etc).
+ * @class HTMLElement
+ * @extends {Node}
+ */
+export default class HTMLElement extends Node {
+    /**
+     * Creates an instance of HTMLElement.
+     * @param keyAttrs	id and class attribute
+     * @param [rawAttrs]	attributes in string
+     *
+     * @memberof HTMLElement
+     */
+    constructor(tagName, keyAttrs, rawAttrs = '', parentNode, nodeOptions = {}) {
+        super(parentNode, nodeOptions);
+        this.rawAttrs = rawAttrs;
+        this.classNames = [];
+        /**
+         * Node Type declaration.
+         */
+        this.nodeType = NodeType.ELEMENT_NODE;
+        this.rawTagName = tagName;
+        this.rawAttrs = rawAttrs || '';
+        this.childNodes = [];
+        if (keyAttrs.id) {
+            this.id = keyAttrs.id;
+            if (!rawAttrs) {
+                this.rawAttrs = `id="${keyAttrs.id}"`;
+            }
+        }
+        if (keyAttrs.class) {
+            this.classNames = keyAttrs.class.split(/\s+/);
+            if (!rawAttrs) {
+                const cls = `class="${this.classNames.join(' ')}"`;
+                if (this.rawAttrs) {
+                    this.rawAttrs += ` ${cls}`;
+                }
+                else {
+                    this.rawAttrs = cls;
+                }
+            }
+        }
+    }
+    /**
+     * Remove current element
+     */
+    remove() {
+        if (this.parentNode) {
+            const children = this.parentNode.childNodes;
+            this.parentNode.childNodes = children.filter((child) => {
+                return this !== child;
+            });
+        }
+    }
+    /**
+     * Remove Child element from childNodes array
+     * @param {HTMLElement} node     node to remove
+     */
+    removeChild(node) {
+        this.childNodes = this.childNodes.filter((child) => {
+            return (child !== node);
+        });
+    }
+    /**
+     * Exchanges given child with new child
+     * @param {HTMLElement} oldNode     node to exchange
+     * @param {HTMLElement} newNode     new node
+     */
+    exchangeChild(oldNode, newNode) {
+        const children = this.childNodes;
+        this.childNodes = children.map((child) => {
+            if (child === oldNode) {
+                return newNode;
+            }
+            return child;
+        });
+    }
+    get tagName() {
+        return this.rawTagName ? this.rawTagName.toUpperCase() : this.rawTagName;
+    }
+    /**
+     * Get escpaed (as-it) text value of current node and its children.
+     * @return {string} text content
+     */
+    get rawText() {
+        return this.childNodes.reduce((pre, cur) => {
+            return (pre += cur.rawText);
+        }, '');
+    }
+    get textContent() {
+        return this.rawText;
+    }
+    set textContent(val) {
+        const content = [new TextNode(val, this)];
+        this.childNodes = content;
+    }
+    /**
+     * Get unescaped text value of current node and its children.
+     * @return {string} text content
+     */
+    get text() {
+        return decode(this.rawText);
+    }
+    /**
+     * Get structured Text (with '\n' etc.)
+     * @return {string} structured text
+     */
+    get structuredText() {
+        let currentBlock = [];
+        const blocks = [currentBlock];
+        function dfs(node) {
+            if (node.nodeType === NodeType.ELEMENT_NODE) {
+                if (kBlockElements.get(node.rawTagName)) {
+                    if (currentBlock.length > 0) {
+                        blocks.push(currentBlock = []);
+                    }
+                    node.childNodes.forEach(dfs);
+                    if (currentBlock.length > 0) {
+                        blocks.push(currentBlock = []);
+                    }
+                }
+                else {
+                    node.childNodes.forEach(dfs);
+                }
+            }
+            else if (node.nodeType === NodeType.TEXT_NODE) {
+                if (node.isWhitespace) {
+                    // Whitespace node, postponed output
+                    currentBlock.prependWhitespace = true;
+                }
+                else {
+                    let text = node.text;
+                    if (currentBlock.prependWhitespace) {
+                        text = ` ${text}`;
+                        currentBlock.prependWhitespace = false;
+                    }
+                    currentBlock.push(text);
+                }
+            }
+        }
+        dfs(this);
+        return blocks.map((block) => {
+            // Normalize each line's whitespace
+            return block.join('').trim().replace(/\s{2,}/g, ' ');
+        })
+            .join('\n').replace(/\s+$/, ''); // trimRight;
+    }
+    toString() {
+        const tag = this.rawTagName;
+        if (tag) {
+            const is_void = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/i.test(tag);
+            const attrs = this.rawAttrs ? ` ${this.rawAttrs}` : '';
+            if (is_void) {
+                return `<${tag}${attrs}>`;
+            }
+            return `<${tag}${attrs}>${this.innerHTML}</${tag}>`;
+        }
+        return this.innerHTML;
+    }
+    get innerHTML() {
+        return this.childNodes.map((child) => {
+            return child.toString();
+        }).join('');
+    }
+    set_content(content, options = {}) {
+        if (content instanceof Node) {
+            content = [content];
+        }
+        else if (typeof content == 'string') {
+            const r = parse(content, options);
+            content = r.childNodes.length ? r.childNodes : [new TextNode(content, this)];
+        }
+        this.childNodes = content;
+    }
+    get outerHTML() {
+        return this.toString();
+    }
+    /**
+     * Trim element from right (in block) after seeing pattern in a TextNode.
+     * @param  {RegExp} pattern pattern to find
+     * @return {HTMLElement}    reference to current node
+     */
+    trimRight(pattern) {
+        for (let i = 0; i < this.childNodes.length; i++) {
+            const childNode = this.childNodes[i];
+            if (childNode.nodeType === NodeType.ELEMENT_NODE) {
+                childNode.trimRight(pattern);
+            }
+            else {
+                const index = childNode.rawText.search(pattern);
+                if (index > -1) {
+                    childNode.rawText = childNode.rawText.substr(0, index);
+                    // trim all following nodes.
+                    this.childNodes.length = i + 1;
+                }
+            }
+        }
+        return this;
+    }
+    /**
+     * Get DOM structure
+     * @return {string} strucutre
+     */
+    get structure() {
+        const res = [];
+        let indention = 0;
+        function write(str) {
+            res.push('  '.repeat(indention) + str);
+        }
+        function dfs(node) {
+            const idStr = node.id ? (`#${node.id}`) : '';
+            const classStr = node.classNames.length ? (`.${node.classNames.join('.')}`) : '';
+            write(`${node.rawTagName}${idStr}${classStr}`);
+            indention++;
+            node.childNodes.forEach((childNode) => {
+                if (childNode.nodeType === NodeType.ELEMENT_NODE) {
+                    dfs(childNode);
+                }
+                else if (childNode.nodeType === NodeType.TEXT_NODE) {
+                    if (!childNode.isWhitespace) {
+                        write('#text');
+                    }
+                }
+            });
+            indention--;
+        }
+        dfs(this);
+        return res.join('\n');
+    }
+    /**
+     * Remove whitespaces in this sub tree.
+     * @return {HTMLElement} pointer to this
+     */
+    removeWhitespace() {
+        let o = 0;
+        this.childNodes.forEach((node) => {
+            if (node.nodeType === NodeType.TEXT_NODE) {
+                if (node.isWhitespace) {
+                    return;
+                }
+                node.rawText = node.rawText.trim();
+            }
+            else if (node.nodeType === NodeType.ELEMENT_NODE) {
+                node.removeWhitespace();
+            }
+            this.childNodes[o++] = node;
+        });
+        this.childNodes.length = o;
+        return this;
+    }
+    /**
+     * Query CSS selector to find matching nodes.
+     * @param  {string}         selector Simplified CSS selector
+     * @return {HTMLElement[]}  matching elements
+     */
+    querySelectorAll(selector) {
+        return selectAll(selector, this, {
+            xmlMode: true,
+            adapter: Matcher
+        });
+        // let matcher: Matcher;
+        // if (selector instanceof Matcher) {
+        // 	matcher = selector;
+        // 	matcher.reset();
+        // } else {
+        // 	if (selector.includes(',')) {
+        // 		const selectors = selector.split(',');
+        // 		return Array.from(selectors.reduce((pre, cur) => {
+        // 			const result = this.querySelectorAll(cur.trim());
+        // 			return result.reduce((p, c) => {
+        // 				return p.add(c);
+        // 			}, pre);
+        // 		}, new Set<HTMLElement>()));
+        // 	}
+        // 	matcher = new Matcher(selector);
+        // }
+        // interface IStack {
+        // 	0: Node;	// node
+        // 	1: number;	// children
+        // 	2: boolean;	// found flag
+        // }
+        // const stack = [] as IStack[];
+        // return this.childNodes.reduce((res, cur) => {
+        // 	stack.push([cur, 0, false]);
+        // 	while (stack.length) {
+        // 		const state = arr_back(stack);	// get last element
+        // 		const el = state[0];
+        // 		if (state[1] === 0) {
+        // 			// Seen for first time.
+        // 			if (el.nodeType !== NodeType.ELEMENT_NODE) {
+        // 				stack.pop();
+        // 				continue;
+        // 			}
+        // 			const html_el = el as HTMLElement;
+        // 			state[2] = matcher.advance(html_el);
+        // 			if (state[2]) {
+        // 				if (matcher.matched) {
+        // 					res.push(html_el);
+        // 					res.push(...(html_el.querySelectorAll(selector)));
+        // 					// no need to go further.
+        // 					matcher.rewind();
+        // 					stack.pop();
+        // 					continue;
+        // 				}
+        // 			}
+        // 		}
+        // 		if (state[1] < el.childNodes.length) {
+        // 			stack.push([el.childNodes[state[1]++], 0, false]);
+        // 		} else {
+        // 			if (state[2]) {
+        // 				matcher.rewind();
+        // 			}
+        // 			stack.pop();
+        // 		}
+        // 	}
+        // 	return res;
+        // }, [] as HTMLElement[]);
+    }
+    /**
+     * Query CSS Selector to find matching node.
+     * @param  {string}         selector Simplified CSS selector
+     * @return {HTMLElement}    matching node
+     */
+    querySelector(selector) {
+        return selectOne(selector, this, {
+            xmlMode: true,
+            adapter: Matcher
+        });
+        // let matcher: Matcher;
+        // if (selector instanceof Matcher) {
+        // 	matcher = selector;
+        // 	matcher.reset();
+        // } else {
+        // 	matcher = new Matcher(selector);
+        // }
+        // const stack = [] as { 0: Node; 1: 0 | 1; 2: boolean }[];
+        // for (const node of this.childNodes) {
+        // 	stack.push([node, 0, false]);
+        // 	while (stack.length) {
+        // 		const state = arr_back(stack);
+        // 		const el = state[0];
+        // 		if (state[1] === 0) {
+        // 			// Seen for first time.
+        // 			if (el.nodeType !== NodeType.ELEMENT_NODE) {
+        // 				stack.pop();
+        // 				continue;
+        // 			}
+        // 			state[2] = matcher.advance(el as HTMLElement);
+        // 			if (state[2]) {
+        // 				if (matcher.matched) {
+        // 					return el as HTMLElement;
+        // 				}
+        // 			}
+        // 		}
+        // 		if (state[1] < el.childNodes.length) {
+        // 			stack.push([el.childNodes[state[1]++], 0, false]);
+        // 		} else {
+        // 			if (state[2]) {
+        // 				matcher.rewind();
+        // 			}
+        // 			stack.pop();
+        // 		}
+        // 	}
+        // }
+        // return null;
+    }
+    /**
+     * Append a child node to childNodes
+     * @param  {Node} node node to append
+     * @return {Node}      node appended
+     */
+    appendChild(node) {
+        // node.parentNode = this;
+        this.childNodes.push(node);
+        node.parentNode = this;
+        return node;
+    }
+    /**
+     * Get first child node
+     * @return {Node} first child node
+     */
+    get firstChild() {
+        return this.childNodes[0];
+    }
+    /**
+     * Get last child node
+     * @return {Node} last child node
+     */
+    get lastChild() {
+        return arr_back(this.childNodes);
+    }
+    /**
+     * Get attributes
+     * @access private
+     * @return {Object} parsed and unescaped attributes
+     */
+    get attrs() {
+        if (this._attrs) {
+            return this._attrs;
+        }
+        this._attrs = {};
+        const attrs = this.rawAttributes;
+        for (const key in attrs) {
+            const val = attrs[key] || '';
+            this._attrs[key.toLowerCase()] = decode(val);
+        }
+        return this._attrs;
+    }
+    get attributes() {
+        const ret_attrs = {};
+        const attrs = this.rawAttributes;
+        for (const key in attrs) {
+            const val = attrs[key] || '';
+            ret_attrs[key] = decode(val);
+        }
+        return ret_attrs;
+    }
+    /**
+     * Get escaped (as-it) attributes
+     * @return {Object} parsed attributes
+     */
+    get rawAttributes() {
+        if (this._rawAttrs) {
+            return this._rawAttrs;
+        }
+        const attrs = {};
+        if (this.rawAttrs) {
+            const re = /\b([a-z][a-z0-9-_]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/ig;
+            let match;
+            while ((match = re.exec(this.rawAttrs))) {
+                attrs[match[1]] = match[2] || match[3] || match[4] || null;
+            }
+        }
+        this._rawAttrs = attrs;
+        return attrs;
+    }
+    removeAttribute(key) {
+        const attrs = this.rawAttributes;
+        delete attrs[key];
+        // Update this.attribute
+        if (this._attrs) {
+            delete this._attrs[key];
+        }
+        // Update rawString
+        this.rawAttrs = Object.keys(attrs).map((name) => {
+            const val = JSON.stringify(attrs[name]);
+            if (val === undefined || val === 'null') {
+                return name;
+            }
+            return `${name}=${val}`;
+        }).join(' ');
+    }
+    hasAttribute(key) {
+        return key.toLowerCase() in this.attrs;
+    }
+    /**
+     * Get an attribute
+     * @return {string} value of the attribute
+     */
+    getAttribute(key) {
+        return this.attrs[key.toLowerCase()];
+    }
+    /**
+     * Set an attribute value to the HTMLElement
+     * @param {string} key The attribute name
+     * @param {string} value The value to set, or null / undefined to remove an attribute
+     */
+    setAttribute(key, value) {
+        if (arguments.length < 2) {
+            throw new Error('Failed to execute \'setAttribute\' on \'Element\'');
+        }
+        const k2 = key.toLowerCase();
+        const attrs = this.rawAttributes;
+        for (const k in attrs) {
+            if (k.toLowerCase() === k2) {
+                key = k;
+                break;
+            }
+        }
+        attrs[key] = String(value);
+        // update this.attrs
+        if (this._attrs) {
+            this._attrs[k2] = decode(attrs[key]);
+        }
+        // Update rawString
+        this.rawAttrs = Object.keys(attrs).map((name) => {
+            const val = JSON.stringify(attrs[name]);
+            if (val === 'null' || val === '""') {
+                return name;
+            }
+            return `${name}=${val}`;
+        }).join(' ');
+    }
+    /**
+     * Replace all the attributes of the HTMLElement by the provided attributes
+     * @param {Attributes} attributes the new attribute set
+     */
+    setAttributes(attributes) {
+        // Invalidate current this.attributes
+        if (this._attrs) {
+            delete this._attrs;
+        }
+        // Invalidate current this.rawAttributes
+        if (this._rawAttrs) {
+            delete this._rawAttrs;
+        }
+        // Update rawString
+        this.rawAttrs = Object.keys(attributes).map((name) => {
+            const val = attributes[name];
+            if (val === 'null' || val === '""') {
+                return name;
+            }
+            return `${name}=${JSON.stringify(String(val))}`;
+        }).join(' ');
+    }
+    insertAdjacentHTML(where, html) {
+        if (arguments.length < 2) {
+            throw new Error('2 arguments required');
+        }
+        const p = parse(html);
+        if (where === 'afterend') {
+            const idx = this.parentNode.childNodes.findIndex((child) => {
+                return child === this;
+            });
+            this.parentNode.childNodes.splice(idx + 1, 0, ...p.childNodes);
+            p.childNodes.forEach((n) => {
+                if (n instanceof HTMLElement) {
+                    n.parentNode = this.parentNode;
+                }
+            });
+        }
+        else if (where === 'afterbegin') {
+            this.childNodes.unshift(...p.childNodes);
+        }
+        else if (where === 'beforeend') {
+            p.childNodes.forEach((n) => {
+                this.appendChild(n);
+            });
+        }
+        else if (where === 'beforebegin') {
+            const idx = this.parentNode.childNodes.findIndex((child) => {
+                return child === this;
+            });
+            this.parentNode.childNodes.splice(idx, 0, ...p.childNodes);
+            p.childNodes.forEach((n) => {
+                if (n instanceof HTMLElement) {
+                    n.parentNode = this.parentNode;
+                }
+            });
+        }
+        else {
+            throw new Error(`The value provided ('${where}') is not one of 'beforebegin', 'afterbegin', 'beforeend', or 'afterend'`);
+        }
+        // if (!where || html === undefined || html === null) {
+        // 	return;
+        // }
+    }
+    get nextSibling() {
+        if (this.parentNode) {
+            const children = this.parentNode.childNodes;
+            let i = 0;
+            while (i < children.length) {
+                const child = children[i++];
+                if (this === child) {
+                    return children[i] || null;
+                }
+            }
+            return null;
+        }
+    }
+    get nextElementSibling() {
+        if (this.parentNode) {
+            const children = this.parentNode.childNodes;
+            let i = 0;
+            let find = false;
+            while (i < children.length) {
+                const child = children[i++];
+                if (find) {
+                    if (child instanceof HTMLElement) {
+                        return child || null;
+                    }
+                }
+                else if (this === child) {
+                    find = true;
+                }
+            }
+            return null;
+        }
+    }
+}
+// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
+const kMarkupPattern = /<!--[^]*?(?=-->)-->|<(\/?)([a-z][-.:0-9_a-z]*)\s*([^>]*?)(\/?)>/ig;
+// <(?<tag>[^\s]*)(.*)>(.*)</\k<tag>>
+// <([a-z][-.:0-9_a-z]*)\s*\/>
+// <(area|base|br|col|hr|img|input|link|meta|source)\s*(.*)\/?>
+// <(area|base|br|col|hr|img|input|link|meta|source)\s*(.*)\/?>|<(?<tag>[^\s]*)(.*)>(.*)</\k<tag>>
+const kAttributePattern = /(^|\s)(id|class)\s*=\s*("([^"]*)"|'([^']*)'|(\S+))/ig;
+const kSelfClosingElements = {
+    area: true,
+    AREA: true,
+    base: true,
+    BASE: true,
+    br: true,
+    BR: true,
+    col: true,
+    COL: true,
+    hr: true,
+    HR: true,
+    img: true,
+    IMG: true,
+    input: true,
+    INPUT: true,
+    link: true,
+    LINK: true,
+    meta: true,
+    META: true,
+    source: true,
+    SOURCE: true,
+    embed: true,
+    EMBED: true,
+    param: true,
+    PARAM: true,
+    track: true,
+    TRACK: true,
+    wbr: true,
+    WBR: true
+};
+const kElementsClosedByOpening = {
+    li: { li: true, LI: true },
+    LI: { li: true, LI: true },
+    p: { p: true, div: true, P: true, DIV: true },
+    P: { p: true, div: true, P: true, DIV: true },
+    b: { div: true, DIV: true },
+    B: { div: true, DIV: true },
+    td: { td: true, th: true, TD: true, TH: true },
+    TD: { td: true, th: true, TD: true, TH: true },
+    th: { td: true, th: true, TD: true, TH: true },
+    TH: { td: true, th: true, TD: true, TH: true },
+    h1: { h1: true, H1: true },
+    H1: { h1: true, H1: true },
+    h2: { h2: true, H2: true },
+    H2: { h2: true, H2: true },
+    h3: { h3: true, H3: true },
+    H3: { h3: true, H3: true },
+    h4: { h4: true, H4: true },
+    H4: { h4: true, H4: true },
+    h5: { h5: true, H5: true },
+    H5: { h5: true, H5: true },
+    h6: { h6: true, H6: true },
+    H6: { h6: true, H6: true }
+};
+const kElementsClosedByClosing = {
+    li: { ul: true, ol: true, UL: true, OL: true },
+    LI: { ul: true, ol: true, UL: true, OL: true },
+    a: { div: true, DIV: true },
+    A: { div: true, DIV: true },
+    b: { div: true, DIV: true },
+    B: { div: true, DIV: true },
+    i: { div: true, DIV: true },
+    I: { div: true, DIV: true },
+    p: { div: true, DIV: true },
+    P: { div: true, DIV: true },
+    td: { tr: true, table: true, TR: true, TABLE: true },
+    TD: { tr: true, table: true, TR: true, TABLE: true },
+    th: { tr: true, table: true, TR: true, TABLE: true },
+    TH: { tr: true, table: true, TR: true, TABLE: true }
+};
+const frameflag = 'documentfragmentcontainer';
+/**
+ * Parses HTML and returns a root element
+ * Parse a chuck of HTML source.
+ * @param  {string} data      html
+ * @return {HTMLElement}      root element
+ */
+export function base_parse(data, options = { lowerCaseTagName: false, comment: false }) {
+    const elements = options.blockTextElements || {
+        script: true,
+        noscript: true,
+        style: true,
+        pre: true
+    };
+    const element_names = Object.keys(elements);
+    const kBlockTextElements = element_names.map((it) => {
+        return new RegExp(it, 'i');
+    });
+    const kIgnoreElements = element_names.filter((it) => {
+        return elements[it];
+    }).map((it) => {
+        return new RegExp(it, 'i');
+    });
+    function element_should_be_ignore(tag) {
+        return kIgnoreElements.some((it) => {
+            return it.test(tag);
+        });
+    }
+    function is_block_text_element(tag) {
+        return kBlockTextElements.some((it) => {
+            return it.test(tag);
+        });
+    }
+    const root = new HTMLElement(null, {}, '', null);
+    let currentParent = root;
+    const stack = [root];
+    let lastTextPos = -1;
+    let match;
+    // https://github.com/taoqf/node-html-parser/issues/38
+    data = `<${frameflag}>${data}</${frameflag}>`;
+    const dataOffset = `<${frameflag}>`.length;
+    const dataLastIndex = data.length - `</${frameflag}>`.length - 1;
+    function nodeArgs(start, end) {
+        const text = data.substring(start, end);
+        const nodeOptions = {
+            start: start - dataOffset,
+            end: end - dataOffset,
+        };
+        return { text, nodeOptions };
+    }
+    while ((match = kMarkupPattern.exec(data))) {
+        // match[0] is open or close tag, without content
+        //console.dir({ ...match, input: undefined }); // debug
+        if (lastTextPos > -1) {
+            if (lastTextPos + match[0].length < kMarkupPattern.lastIndex) {
+                // if has content
+                const { text, nodeOptions } = nodeArgs(lastTextPos, match.index);
+                currentParent.appendChild(new TextNode(text, currentParent, nodeOptions));
+            }
+        }
+        lastTextPos = kMarkupPattern.lastIndex;
+        if (match[2] === frameflag) {
+            continue;
+        }
+        if (match[0][1] === '!') {
+            // this is a comment
+            if (options.comment) {
+                // Only keep what is in between <!-- and -->
+                const { text, nodeOptions } = nodeArgs(lastTextPos - 3, lastTextPos - match[0].length + 4);
+                currentParent.appendChild(new CommentNode(text, currentParent, nodeOptions));
+            }
+            continue;
+        }
+        if (options.lowerCaseTagName) {
+            match[2] = match[2].toLowerCase();
+        }
+        if (!match[1]) {
+            // open tag
+            const attrs = {};
+            if (match[3]) {
+                for (let attMatch; (attMatch = kAttributePattern.exec(match[3]));) {
+                    attrs[attMatch[2].toLowerCase()] = attMatch[4] || attMatch[5] || attMatch[6];
+                }
+            }
+            const tagName = currentParent.rawTagName;
+            if (!match[4] && kElementsClosedByOpening[tagName]) {
+                if (kElementsClosedByOpening[tagName][match[2]]) {
+                    stack.pop();
+                    currentParent = arr_back(stack);
+                }
+            }
+            const nodeOptions = {
+                start: match.index - dataOffset,
+                //end: -1, // end is unknown here
+            };
+            // ignore container tag we add above
+            // https://github.com/taoqf/node-html-parser/issues/38
+            //console.dir({ new_HTMLElement: { name: match[2], attrs, attrRaw: match[3], parent: null, nodeOptions } }); // debug
+            currentParent = currentParent.appendChild(new HTMLElement(match[2], attrs, match[3], null, nodeOptions));
+            stack.push(currentParent);
+            if (is_block_text_element(match[2])) {
+                // a little test to find next </script> or </style> ...
+                const closeMarkup = `</${match[2]}>`;
+                const index = (() => {
+                    if (options.lowerCaseTagName) {
+                        return data.toLocaleLowerCase().indexOf(closeMarkup, kMarkupPattern.lastIndex);
+                    }
+                    return data.indexOf(closeMarkup, kMarkupPattern.lastIndex);
+                })();
+                if (element_should_be_ignore(match[2])) {
+                    let text;
+                    const nodeOptions = {
+                        start: kMarkupPattern.lastIndex - dataOffset,
+                        end: dataLastIndex, // TODO verify
+                    };
+                    if (index === -1) {
+                        // there is no matching ending for the text element.
+                        text = data.substr(kMarkupPattern.lastIndex);
+                    }
+                    else {
+                        text = data.substring(kMarkupPattern.lastIndex, index);
+                        nodeOptions.end = index - dataOffset;
+                    }
+                    if (text.length > 0) {
+                        currentParent.appendChild(new TextNode(text, currentParent, nodeOptions));
+                    }
+                }
+                if (index === -1) {
+                    lastTextPos = kMarkupPattern.lastIndex = data.length + 1;
+                }
+                else {
+                    lastTextPos = kMarkupPattern.lastIndex = index + closeMarkup.length;
+                    match[1] = 'true';
+                }
+            }
+        }
+        if (match[1] || match[4] || kSelfClosingElements[match[2]]) {
+            // </ or /> or <br> etc.
+            while (true) {
+                if (currentParent.rawTagName === match[2]) {
+                    stack.pop();
+                    currentParent = arr_back(stack);
+                    break;
+                }
+                else {
+                    const tagName = currentParent.tagName;
+                    // Trying to close current tag, and move on
+                    if (kElementsClosedByClosing[tagName]) {
+                        if (kElementsClosedByClosing[tagName][match[2]]) {
+                            stack.pop();
+                            currentParent = arr_back(stack);
+                            continue;
+                        }
+                    }
+                    // Use aggressive strategy to handle unmatching markups.
+                    break;
+                }
+            }
+        }
+    }
+    return stack;
+}
diff --git a/node_modules/node-html-parser/dist/esm/nodes/node.js b/node_modules/node-html-parser/dist/esm/nodes/node.js
new file mode 100644
index 0000000..dde5263
--- /dev/null
+++ b/node_modules/node-html-parser/dist/esm/nodes/node.js
@@ -0,0 +1,22 @@
+/**
+ * Node Class as base class for TextNode and HTMLElement.
+ */
+export default class Node {
+    constructor(parentNode = null, nodeOptions = {}) {
+        this.parentNode = parentNode;
+        this.childNodes = [];
+        this._source = {
+            start: nodeOptions.start,
+            end: nodeOptions.end,
+        };
+    }
+    get innerText() {
+        return this.rawText;
+    }
+    get textContent() {
+        return this.rawText;
+    }
+    set textContent(val) {
+        this.rawText = val;
+    }
+}
diff --git a/node_modules/node-html-parser/dist/esm/nodes/text.js b/node_modules/node-html-parser/dist/esm/nodes/text.js
new file mode 100644
index 0000000..d73f126
--- /dev/null
+++ b/node_modules/node-html-parser/dist/esm/nodes/text.js
@@ -0,0 +1,34 @@
+import { NodeType } from './type';
+import Node from './node';
+/**
+ * TextNode to contain a text element in DOM tree.
+ * @param {string} value [description]
+ */
+export default class TextNode extends Node {
+    constructor(rawText, parentNode, nodeOptions = {}) {
+        super(parentNode, nodeOptions);
+        this.rawText = rawText;
+        /**
+         * Node Type declaration.
+         * @type {Number}
+         */
+        this.nodeType = NodeType.TEXT_NODE;
+    }
+    /**
+     * Get unescaped text value of current node and its children.
+     * @return {string} text content
+     */
+    get text() {
+        return this.rawText;
+    }
+    /**
+     * Detect if the node contains only white space.
+     * @return {bool}
+     */
+    get isWhitespace() {
+        return /^(\s|&nbsp;)*$/.test(this.rawText);
+    }
+    toString() {
+        return this.text;
+    }
+}
diff --git a/node_modules/node-html-parser/dist/esm/nodes/type.js b/node_modules/node-html-parser/dist/esm/nodes/type.js
new file mode 100644
index 0000000..4ea7a3e
--- /dev/null
+++ b/node_modules/node-html-parser/dist/esm/nodes/type.js
@@ -0,0 +1,6 @@
+export var NodeType;
+(function (NodeType) {
+    NodeType[NodeType["ELEMENT_NODE"] = 1] = "ELEMENT_NODE";
+    NodeType[NodeType["TEXT_NODE"] = 3] = "TEXT_NODE";
+    NodeType[NodeType["COMMENT_NODE"] = 8] = "COMMENT_NODE";
+})(NodeType || (NodeType = {}));
diff --git a/node_modules/node-html-parser/dist/esm/parse.js b/node_modules/node-html-parser/dist/esm/parse.js
new file mode 100644
index 0000000..b4a58e9
--- /dev/null
+++ b/node_modules/node-html-parser/dist/esm/parse.js
@@ -0,0 +1,41 @@
+import arr_back from './back';
+import { base_parse } from './nodes/html';
+/**
+ * Parses HTML and returns a root element
+ * Parse a chuck of HTML source.
+ */
+export default function parse(data, options = { lowerCaseTagName: false, comment: false }) {
+    const stack = base_parse(data, options);
+    const [root] = stack;
+    while (stack.length > 1) {
+        // Handle each error elements.
+        const last = stack.pop();
+        const oneBefore = arr_back(stack);
+        if (last.parentNode && last.parentNode.parentNode) {
+            if (last.parentNode === oneBefore && last.tagName === oneBefore.tagName) {
+                // Pair error case <h3> <h3> handle : Fixes to <h3> </h3>
+                oneBefore.removeChild(last);
+                last.childNodes.forEach((child) => {
+                    oneBefore.parentNode.appendChild(child);
+                });
+                stack.pop();
+            }
+            else {
+                // Single error  <div> <h3> </div> handle: Just removes <h3>
+                oneBefore.removeChild(last);
+                last.childNodes.forEach((child) => {
+                    oneBefore.appendChild(child);
+                });
+            }
+        }
+        else {
+            // If it's final element just skip.
+        }
+    }
+    // response.childNodes.forEach((node) => {
+    // 	if (node instanceof HTMLElement) {
+    // 		node.parentNode = null;
+    // 	}
+    // });
+    return root;
+}
diff --git a/node_modules/node-html-parser/dist/esm/valid.js b/node_modules/node-html-parser/dist/esm/valid.js
new file mode 100644
index 0000000..39d66ae
--- /dev/null
+++ b/node_modules/node-html-parser/dist/esm/valid.js
@@ -0,0 +1,9 @@
+import { base_parse } from './nodes/html';
+/**
+ * Parses HTML and returns a root element
+ * Parse a chuck of HTML source.
+ */
+export default function valid(data, options = { lowerCaseTagName: false, comment: false }) {
+    const stack = base_parse(data, options);
+    return Boolean(stack.length === 1);
+}
diff --git a/node_modules/node-html-parser/dist/index.d.ts b/node_modules/node-html-parser/dist/index.d.ts
new file mode 100644
index 0000000..f3c4c24
--- /dev/null
+++ b/node_modules/node-html-parser/dist/index.d.ts
@@ -0,0 +1,7 @@
+export { default as CommentNode } from './nodes/comment';
+export { default as HTMLElement, Options } from './nodes/html';
+export { default as parse, default } from './parse';
+export { default as valid } from './valid';
+export { default as Node } from './nodes/node';
+export { default as TextNode } from './nodes/text';
+export { NodeType, SourceLocation } from './nodes/type';
diff --git a/node_modules/node-html-parser/dist/index.js b/node_modules/node-html-parser/dist/index.js
new file mode 100644
index 0000000..6ca1b8f
--- /dev/null
+++ b/node_modules/node-html-parser/dist/index.js
@@ -0,0 +1,21 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.NodeType = exports.TextNode = exports.Node = exports.valid = exports.default = exports.parse = exports.HTMLElement = exports.CommentNode = void 0;
+var comment_1 = require("./nodes/comment");
+Object.defineProperty(exports, "CommentNode", { enumerable: true, get: function () { return __importDefault(comment_1).default; } });
+var html_1 = require("./nodes/html");
+Object.defineProperty(exports, "HTMLElement", { enumerable: true, get: function () { return __importDefault(html_1).default; } });
+var parse_1 = require("./parse");
+Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return __importDefault(parse_1).default; } });
+Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(parse_1).default; } });
+var valid_1 = require("./valid");
+Object.defineProperty(exports, "valid", { enumerable: true, get: function () { return __importDefault(valid_1).default; } });
+var node_1 = require("./nodes/node");
+Object.defineProperty(exports, "Node", { enumerable: true, get: function () { return __importDefault(node_1).default; } });
+var text_1 = require("./nodes/text");
+Object.defineProperty(exports, "TextNode", { enumerable: true, get: function () { return __importDefault(text_1).default; } });
+var type_1 = require("./nodes/type");
+Object.defineProperty(exports, "NodeType", { enumerable: true, get: function () { return type_1.NodeType; } });
diff --git a/node_modules/node-html-parser/dist/main.js b/node_modules/node-html-parser/dist/main.js
new file mode 100644
index 0000000..a81fc3e
--- /dev/null
+++ b/node_modules/node-html-parser/dist/main.js
@@ -0,0 +1,1283 @@
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = function (d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+    return function (d, b) {
+        if (typeof b !== "function" && b !== null)
+            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+var __spreadArray = (this && this.__spreadArray) || function (to, from) {
+    for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
+        to[j] = from[i];
+    return to;
+};
+define("back", ["require", "exports"], function (require, exports) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    function arr_back(arr) {
+        return arr[arr.length - 1];
+    }
+    exports.default = arr_back;
+});
+define("nodes/type", ["require", "exports"], function (require, exports) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.NodeType = void 0;
+    var NodeType;
+    (function (NodeType) {
+        NodeType[NodeType["ELEMENT_NODE"] = 1] = "ELEMENT_NODE";
+        NodeType[NodeType["TEXT_NODE"] = 3] = "TEXT_NODE";
+        NodeType[NodeType["COMMENT_NODE"] = 8] = "COMMENT_NODE";
+    })(NodeType = exports.NodeType || (exports.NodeType = {}));
+});
+define("nodes/text", ["require", "exports", "nodes/type", "nodes/node"], function (require, exports, type_1, node_1) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    node_1 = __importDefault(node_1);
+    /**
+     * TextNode to contain a text element in DOM tree.
+     * @param {string} value [description]
+     */
+    var TextNode = /** @class */ (function (_super) {
+        __extends(TextNode, _super);
+        function TextNode(rawText, parentNode, nodeOptions) {
+            if (nodeOptions === void 0) { nodeOptions = {}; }
+            var _this = _super.call(this, parentNode, nodeOptions) || this;
+            _this.rawText = rawText;
+            /**
+             * Node Type declaration.
+             * @type {Number}
+             */
+            _this.nodeType = type_1.NodeType.TEXT_NODE;
+            return _this;
+        }
+        Object.defineProperty(TextNode.prototype, "text", {
+            /**
+             * Get unescaped text value of current node and its children.
+             * @return {string} text content
+             */
+            get: function () {
+                return this.rawText;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(TextNode.prototype, "isWhitespace", {
+            /**
+             * Detect if the node contains only white space.
+             * @return {bool}
+             */
+            get: function () {
+                return /^(\s|&nbsp;)*$/.test(this.rawText);
+            },
+            enumerable: false,
+            configurable: true
+        });
+        TextNode.prototype.toString = function () {
+            return this.text;
+        };
+        return TextNode;
+    }(node_1.default));
+    exports.default = TextNode;
+});
+define("matcher", ["require", "exports", "nodes/type"], function (require, exports, type_2) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    function isTag(node) {
+        return node && node.nodeType === type_2.NodeType.ELEMENT_NODE;
+    }
+    function getAttributeValue(elem, name) {
+        return isTag(elem) ? elem.getAttribute(name) : undefined;
+    }
+    function getName(elem) {
+        return ((elem && elem.rawTagName) || '').toLowerCase();
+    }
+    function getChildren(node) {
+        return node && node.childNodes;
+    }
+    function getParent(node) {
+        return node ? node.parentNode : null;
+    }
+    function getText(node) {
+        return node.text;
+    }
+    function removeSubsets(nodes) {
+        var idx = nodes.length;
+        var node;
+        var ancestor;
+        var replace;
+        // Check if each node (or one of its ancestors) is already contained in the
+        // array.
+        while (--idx > -1) {
+            node = ancestor = nodes[idx];
+            // Temporarily remove the node under consideration
+            nodes[idx] = null;
+            replace = true;
+            while (ancestor) {
+                if (nodes.indexOf(ancestor) > -1) {
+                    replace = false;
+                    nodes.splice(idx, 1);
+                    break;
+                }
+                ancestor = getParent(ancestor);
+            }
+            // If the node has been found to be unique, re-insert it.
+            if (replace) {
+                nodes[idx] = node;
+            }
+        }
+        return nodes;
+    }
+    function existsOne(test, elems) {
+        return elems.some(function (elem) {
+            return isTag(elem) ? test(elem) || existsOne(test, getChildren(elem)) : false;
+        });
+    }
+    function getSiblings(node) {
+        var parent = getParent(node);
+        return parent && getChildren(parent);
+    }
+    function hasAttrib(elem, name) {
+        return getAttributeValue(elem, name) !== undefined;
+    }
+    function findOne(test, elems) {
+        var elem = null;
+        for (var i = 0, l = elems.length; i < l && !elem; i++) {
+            var el = elems[i];
+            if (test(el)) {
+                elem = el;
+            }
+            else {
+                var childs = getChildren(el);
+                if (childs && childs.length > 0) {
+                    elem = findOne(test, childs);
+                }
+            }
+        }
+        return elem;
+    }
+    function findAll(test, nodes) {
+        var result = [];
+        for (var i = 0, j = nodes.length; i < j; i++) {
+            if (!isTag(nodes[i]))
+                continue;
+            if (test(nodes[i]))
+                result.push(nodes[i]);
+            var childs = getChildren(nodes[i]);
+            if (childs)
+                result = result.concat(findAll(test, childs));
+        }
+        return result;
+    }
+    exports.default = {
+        isTag: isTag,
+        getAttributeValue: getAttributeValue,
+        getName: getName,
+        getChildren: getChildren,
+        getParent: getParent,
+        getText: getText,
+        removeSubsets: removeSubsets,
+        existsOne: existsOne,
+        getSiblings: getSiblings,
+        hasAttrib: hasAttrib,
+        findOne: findOne,
+        findAll: findAll
+    };
+});
+define("parse", ["require", "exports", "back", "nodes/html"], function (require, exports, back_1, html_1) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    back_1 = __importDefault(back_1);
+    /**
+     * Parses HTML and returns a root element
+     * Parse a chuck of HTML source.
+     */
+    function parse(data, options) {
+        if (options === void 0) { options = { lowerCaseTagName: false, comment: false }; }
+        var stack = (0, html_1.base_parse)(data, options);
+        var root = stack[0];
+        var _loop_1 = function () {
+            // Handle each error elements.
+            var last = stack.pop();
+            var oneBefore = (0, back_1.default)(stack);
+            if (last.parentNode && last.parentNode.parentNode) {
+                if (last.parentNode === oneBefore && last.tagName === oneBefore.tagName) {
+                    // Pair error case <h3> <h3> handle : Fixes to <h3> </h3>
+                    oneBefore.removeChild(last);
+                    last.childNodes.forEach(function (child) {
+                        oneBefore.parentNode.appendChild(child);
+                    });
+                    stack.pop();
+                }
+                else {
+                    // Single error  <div> <h3> </div> handle: Just removes <h3>
+                    oneBefore.removeChild(last);
+                    last.childNodes.forEach(function (child) {
+                        oneBefore.appendChild(child);
+                    });
+                }
+            }
+            else {
+                // If it's final element just skip.
+            }
+        };
+        while (stack.length > 1) {
+            _loop_1();
+        }
+        // response.childNodes.forEach((node) => {
+        // 	if (node instanceof HTMLElement) {
+        // 		node.parentNode = null;
+        // 	}
+        // });
+        return root;
+    }
+    exports.default = parse;
+});
+define("nodes/html", ["require", "exports", "he", "css-select", "nodes/node", "nodes/type", "nodes/text", "matcher", "back", "nodes/comment", "parse"], function (require, exports, he_1, css_select_1, node_2, type_3, text_1, matcher_1, back_2, comment_1, parse_1) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.base_parse = void 0;
+    he_1 = __importDefault(he_1);
+    node_2 = __importDefault(node_2);
+    text_1 = __importDefault(text_1);
+    matcher_1 = __importDefault(matcher_1);
+    back_2 = __importDefault(back_2);
+    comment_1 = __importDefault(comment_1);
+    parse_1 = __importDefault(parse_1);
+    var decode = he_1.default.decode;
+    var kBlockElements = new Map();
+    kBlockElements.set('DIV', true);
+    kBlockElements.set('div', true);
+    kBlockElements.set('P', true);
+    kBlockElements.set('p', true);
+    // ul: true,
+    // ol: true,
+    kBlockElements.set('LI', true);
+    kBlockElements.set('li', true);
+    // table: true,
+    // tr: true,
+    kBlockElements.set('TD', true);
+    kBlockElements.set('td', true);
+    kBlockElements.set('SECTION', true);
+    kBlockElements.set('section', true);
+    kBlockElements.set('BR', true);
+    kBlockElements.set('br', true);
+    /**
+     * HTMLElement, which contains a set of children.
+     *
+     * Note: this is a minimalist implementation, no complete tree
+     *   structure provided (no parentNode, nextSibling,
+     *   previousSibling etc).
+     * @class HTMLElement
+     * @extends {Node}
+     */
+    var HTMLElement = /** @class */ (function (_super) {
+        __extends(HTMLElement, _super);
+        /**
+         * Creates an instance of HTMLElement.
+         * @param keyAttrs	id and class attribute
+         * @param [rawAttrs]	attributes in string
+         *
+         * @memberof HTMLElement
+         */
+        function HTMLElement(tagName, keyAttrs, rawAttrs, parentNode, nodeOptions) {
+            if (rawAttrs === void 0) { rawAttrs = ''; }
+            if (nodeOptions === void 0) { nodeOptions = {}; }
+            var _this = _super.call(this, parentNode, nodeOptions) || this;
+            _this.rawAttrs = rawAttrs;
+            _this.classNames = [];
+            /**
+             * Node Type declaration.
+             */
+            _this.nodeType = type_3.NodeType.ELEMENT_NODE;
+            _this.rawTagName = tagName;
+            _this.rawAttrs = rawAttrs || '';
+            _this.childNodes = [];
+            if (keyAttrs.id) {
+                _this.id = keyAttrs.id;
+                if (!rawAttrs) {
+                    _this.rawAttrs = "id=\"" + keyAttrs.id + "\"";
+                }
+            }
+            if (keyAttrs.class) {
+                _this.classNames = keyAttrs.class.split(/\s+/);
+                if (!rawAttrs) {
+                    var cls = "class=\"" + _this.classNames.join(' ') + "\"";
+                    if (_this.rawAttrs) {
+                        _this.rawAttrs += " " + cls;
+                    }
+                    else {
+                        _this.rawAttrs = cls;
+                    }
+                }
+            }
+            return _this;
+        }
+        /**
+         * Remove current element
+         */
+        HTMLElement.prototype.remove = function () {
+            var _this = this;
+            if (this.parentNode) {
+                var children = this.parentNode.childNodes;
+                this.parentNode.childNodes = children.filter(function (child) {
+                    return _this !== child;
+                });
+            }
+        };
+        /**
+         * Remove Child element from childNodes array
+         * @param {HTMLElement} node     node to remove
+         */
+        HTMLElement.prototype.removeChild = function (node) {
+            this.childNodes = this.childNodes.filter(function (child) {
+                return (child !== node);
+            });
+        };
+        /**
+         * Exchanges given child with new child
+         * @param {HTMLElement} oldNode     node to exchange
+         * @param {HTMLElement} newNode     new node
+         */
+        HTMLElement.prototype.exchangeChild = function (oldNode, newNode) {
+            var children = this.childNodes;
+            this.childNodes = children.map(function (child) {
+                if (child === oldNode) {
+                    return newNode;
+                }
+                return child;
+            });
+        };
+        Object.defineProperty(HTMLElement.prototype, "tagName", {
+            get: function () {
+                return this.rawTagName ? this.rawTagName.toUpperCase() : this.rawTagName;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(HTMLElement.prototype, "rawText", {
+            /**
+             * Get escpaed (as-it) text value of current node and its children.
+             * @return {string} text content
+             */
+            get: function () {
+                return this.childNodes.reduce(function (pre, cur) {
+                    return (pre += cur.rawText);
+                }, '');
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(HTMLElement.prototype, "textContent", {
+            get: function () {
+                return this.rawText;
+            },
+            set: function (val) {
+                var content = [new text_1.default(val, this)];
+                this.childNodes = content;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(HTMLElement.prototype, "text", {
+            /**
+             * Get unescaped text value of current node and its children.
+             * @return {string} text content
+             */
+            get: function () {
+                return decode(this.rawText);
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(HTMLElement.prototype, "structuredText", {
+            /**
+             * Get structured Text (with '\n' etc.)
+             * @return {string} structured text
+             */
+            get: function () {
+                var currentBlock = [];
+                var blocks = [currentBlock];
+                function dfs(node) {
+                    if (node.nodeType === type_3.NodeType.ELEMENT_NODE) {
+                        if (kBlockElements.get(node.rawTagName)) {
+                            if (currentBlock.length > 0) {
+                                blocks.push(currentBlock = []);
+                            }
+                            node.childNodes.forEach(dfs);
+                            if (currentBlock.length > 0) {
+                                blocks.push(currentBlock = []);
+                            }
+                        }
+                        else {
+                            node.childNodes.forEach(dfs);
+                        }
+                    }
+                    else if (node.nodeType === type_3.NodeType.TEXT_NODE) {
+                        if (node.isWhitespace) {
+                            // Whitespace node, postponed output
+                            currentBlock.prependWhitespace = true;
+                        }
+                        else {
+                            var text = node.text;
+                            if (currentBlock.prependWhitespace) {
+                                text = " " + text;
+                                currentBlock.prependWhitespace = false;
+                            }
+                            currentBlock.push(text);
+                        }
+                    }
+                }
+                dfs(this);
+                return blocks.map(function (block) {
+                    // Normalize each line's whitespace
+                    return block.join('').trim().replace(/\s{2,}/g, ' ');
+                })
+                    .join('\n').replace(/\s+$/, ''); // trimRight;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        HTMLElement.prototype.toString = function () {
+            var tag = this.rawTagName;
+            if (tag) {
+                var is_void = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/i.test(tag);
+                var attrs = this.rawAttrs ? " " + this.rawAttrs : '';
+                if (is_void) {
+                    return "<" + tag + attrs + ">";
+                }
+                return "<" + tag + attrs + ">" + this.innerHTML + "</" + tag + ">";
+            }
+            return this.innerHTML;
+        };
+        Object.defineProperty(HTMLElement.prototype, "innerHTML", {
+            get: function () {
+                return this.childNodes.map(function (child) {
+                    return child.toString();
+                }).join('');
+            },
+            enumerable: false,
+            configurable: true
+        });
+        HTMLElement.prototype.set_content = function (content, options) {
+            if (options === void 0) { options = {}; }
+            if (content instanceof node_2.default) {
+                content = [content];
+            }
+            else if (typeof content == 'string') {
+                var r = (0, parse_1.default)(content, options);
+                content = r.childNodes.length ? r.childNodes : [new text_1.default(content, this)];
+            }
+            this.childNodes = content;
+        };
+        Object.defineProperty(HTMLElement.prototype, "outerHTML", {
+            get: function () {
+                return this.toString();
+            },
+            enumerable: false,
+            configurable: true
+        });
+        /**
+         * Trim element from right (in block) after seeing pattern in a TextNode.
+         * @param  {RegExp} pattern pattern to find
+         * @return {HTMLElement}    reference to current node
+         */
+        HTMLElement.prototype.trimRight = function (pattern) {
+            for (var i = 0; i < this.childNodes.length; i++) {
+                var childNode = this.childNodes[i];
+                if (childNode.nodeType === type_3.NodeType.ELEMENT_NODE) {
+                    childNode.trimRight(pattern);
+                }
+                else {
+                    var index = childNode.rawText.search(pattern);
+                    if (index > -1) {
+                        childNode.rawText = childNode.rawText.substr(0, index);
+                        // trim all following nodes.
+                        this.childNodes.length = i + 1;
+                    }
+                }
+            }
+            return this;
+        };
+        Object.defineProperty(HTMLElement.prototype, "structure", {
+            /**
+             * Get DOM structure
+             * @return {string} strucutre
+             */
+            get: function () {
+                var res = [];
+                var indention = 0;
+                function write(str) {
+                    res.push('  '.repeat(indention) + str);
+                }
+                function dfs(node) {
+                    var idStr = node.id ? ("#" + node.id) : '';
+                    var classStr = node.classNames.length ? ("." + node.classNames.join('.')) : '';
+                    write("" + node.rawTagName + idStr + classStr);
+                    indention++;
+                    node.childNodes.forEach(function (childNode) {
+                        if (childNode.nodeType === type_3.NodeType.ELEMENT_NODE) {
+                            dfs(childNode);
+                        }
+                        else if (childNode.nodeType === type_3.NodeType.TEXT_NODE) {
+                            if (!childNode.isWhitespace) {
+                                write('#text');
+                            }
+                        }
+                    });
+                    indention--;
+                }
+                dfs(this);
+                return res.join('\n');
+            },
+            enumerable: false,
+            configurable: true
+        });
+        /**
+         * Remove whitespaces in this sub tree.
+         * @return {HTMLElement} pointer to this
+         */
+        HTMLElement.prototype.removeWhitespace = function () {
+            var _this = this;
+            var o = 0;
+            this.childNodes.forEach(function (node) {
+                if (node.nodeType === type_3.NodeType.TEXT_NODE) {
+                    if (node.isWhitespace) {
+                        return;
+                    }
+                    node.rawText = node.rawText.trim();
+                }
+                else if (node.nodeType === type_3.NodeType.ELEMENT_NODE) {
+                    node.removeWhitespace();
+                }
+                _this.childNodes[o++] = node;
+            });
+            this.childNodes.length = o;
+            return this;
+        };
+        /**
+         * Query CSS selector to find matching nodes.
+         * @param  {string}         selector Simplified CSS selector
+         * @return {HTMLElement[]}  matching elements
+         */
+        HTMLElement.prototype.querySelectorAll = function (selector) {
+            return (0, css_select_1.selectAll)(selector, this, {
+                xmlMode: true,
+                adapter: matcher_1.default
+            });
+            // let matcher: Matcher;
+            // if (selector instanceof Matcher) {
+            // 	matcher = selector;
+            // 	matcher.reset();
+            // } else {
+            // 	if (selector.includes(',')) {
+            // 		const selectors = selector.split(',');
+            // 		return Array.from(selectors.reduce((pre, cur) => {
+            // 			const result = this.querySelectorAll(cur.trim());
+            // 			return result.reduce((p, c) => {
+            // 				return p.add(c);
+            // 			}, pre);
+            // 		}, new Set<HTMLElement>()));
+            // 	}
+            // 	matcher = new Matcher(selector);
+            // }
+            // interface IStack {
+            // 	0: Node;	// node
+            // 	1: number;	// children
+            // 	2: boolean;	// found flag
+            // }
+            // const stack = [] as IStack[];
+            // return this.childNodes.reduce((res, cur) => {
+            // 	stack.push([cur, 0, false]);
+            // 	while (stack.length) {
+            // 		const state = arr_back(stack);	// get last element
+            // 		const el = state[0];
+            // 		if (state[1] === 0) {
+            // 			// Seen for first time.
+            // 			if (el.nodeType !== NodeType.ELEMENT_NODE) {
+            // 				stack.pop();
+            // 				continue;
+            // 			}
+            // 			const html_el = el as HTMLElement;
+            // 			state[2] = matcher.advance(html_el);
+            // 			if (state[2]) {
+            // 				if (matcher.matched) {
+            // 					res.push(html_el);
+            // 					res.push(...(html_el.querySelectorAll(selector)));
+            // 					// no need to go further.
+            // 					matcher.rewind();
+            // 					stack.pop();
+            // 					continue;
+            // 				}
+            // 			}
+            // 		}
+            // 		if (state[1] < el.childNodes.length) {
+            // 			stack.push([el.childNodes[state[1]++], 0, false]);
+            // 		} else {
+            // 			if (state[2]) {
+            // 				matcher.rewind();
+            // 			}
+            // 			stack.pop();
+            // 		}
+            // 	}
+            // 	return res;
+            // }, [] as HTMLElement[]);
+        };
+        /**
+         * Query CSS Selector to find matching node.
+         * @param  {string}         selector Simplified CSS selector
+         * @return {HTMLElement}    matching node
+         */
+        HTMLElement.prototype.querySelector = function (selector) {
+            return (0, css_select_1.selectOne)(selector, this, {
+                xmlMode: true,
+                adapter: matcher_1.default
+            });
+            // let matcher: Matcher;
+            // if (selector instanceof Matcher) {
+            // 	matcher = selector;
+            // 	matcher.reset();
+            // } else {
+            // 	matcher = new Matcher(selector);
+            // }
+            // const stack = [] as { 0: Node; 1: 0 | 1; 2: boolean }[];
+            // for (const node of this.childNodes) {
+            // 	stack.push([node, 0, false]);
+            // 	while (stack.length) {
+            // 		const state = arr_back(stack);
+            // 		const el = state[0];
+            // 		if (state[1] === 0) {
+            // 			// Seen for first time.
+            // 			if (el.nodeType !== NodeType.ELEMENT_NODE) {
+            // 				stack.pop();
+            // 				continue;
+            // 			}
+            // 			state[2] = matcher.advance(el as HTMLElement);
+            // 			if (state[2]) {
+            // 				if (matcher.matched) {
+            // 					return el as HTMLElement;
+            // 				}
+            // 			}
+            // 		}
+            // 		if (state[1] < el.childNodes.length) {
+            // 			stack.push([el.childNodes[state[1]++], 0, false]);
+            // 		} else {
+            // 			if (state[2]) {
+            // 				matcher.rewind();
+            // 			}
+            // 			stack.pop();
+            // 		}
+            // 	}
+            // }
+            // return null;
+        };
+        /**
+         * Append a child node to childNodes
+         * @param  {Node} node node to append
+         * @return {Node}      node appended
+         */
+        HTMLElement.prototype.appendChild = function (node) {
+            // node.parentNode = this;
+            this.childNodes.push(node);
+            node.parentNode = this;
+            return node;
+        };
+        Object.defineProperty(HTMLElement.prototype, "firstChild", {
+            /**
+             * Get first child node
+             * @return {Node} first child node
+             */
+            get: function () {
+                return this.childNodes[0];
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(HTMLElement.prototype, "lastChild", {
+            /**
+             * Get last child node
+             * @return {Node} last child node
+             */
+            get: function () {
+                return (0, back_2.default)(this.childNodes);
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(HTMLElement.prototype, "attrs", {
+            /**
+             * Get attributes
+             * @access private
+             * @return {Object} parsed and unescaped attributes
+             */
+            get: function () {
+                if (this._attrs) {
+                    return this._attrs;
+                }
+                this._attrs = {};
+                var attrs = this.rawAttributes;
+                for (var key in attrs) {
+                    var val = attrs[key] || '';
+                    this._attrs[key.toLowerCase()] = decode(val);
+                }
+                return this._attrs;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(HTMLElement.prototype, "attributes", {
+            get: function () {
+                var ret_attrs = {};
+                var attrs = this.rawAttributes;
+                for (var key in attrs) {
+                    var val = attrs[key] || '';
+                    ret_attrs[key] = decode(val);
+                }
+                return ret_attrs;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(HTMLElement.prototype, "rawAttributes", {
+            /**
+             * Get escaped (as-it) attributes
+             * @return {Object} parsed attributes
+             */
+            get: function () {
+                if (this._rawAttrs) {
+                    return this._rawAttrs;
+                }
+                var attrs = {};
+                if (this.rawAttrs) {
+                    var re = /\b([a-z][a-z0-9-_]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/ig;
+                    var match = void 0;
+                    while ((match = re.exec(this.rawAttrs))) {
+                        attrs[match[1]] = match[2] || match[3] || match[4] || null;
+                    }
+                }
+                this._rawAttrs = attrs;
+                return attrs;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        HTMLElement.prototype.removeAttribute = function (key) {
+            var attrs = this.rawAttributes;
+            delete attrs[key];
+            // Update this.attribute
+            if (this._attrs) {
+                delete this._attrs[key];
+            }
+            // Update rawString
+            this.rawAttrs = Object.keys(attrs).map(function (name) {
+                var val = JSON.stringify(attrs[name]);
+                if (val === undefined || val === 'null') {
+                    return name;
+                }
+                return name + "=" + val;
+            }).join(' ');
+        };
+        HTMLElement.prototype.hasAttribute = function (key) {
+            return key.toLowerCase() in this.attrs;
+        };
+        /**
+         * Get an attribute
+         * @return {string} value of the attribute
+         */
+        HTMLElement.prototype.getAttribute = function (key) {
+            return this.attrs[key.toLowerCase()];
+        };
+        /**
+         * Set an attribute value to the HTMLElement
+         * @param {string} key The attribute name
+         * @param {string} value The value to set, or null / undefined to remove an attribute
+         */
+        HTMLElement.prototype.setAttribute = function (key, value) {
+            if (arguments.length < 2) {
+                throw new Error('Failed to execute \'setAttribute\' on \'Element\'');
+            }
+            var k2 = key.toLowerCase();
+            var attrs = this.rawAttributes;
+            for (var k in attrs) {
+                if (k.toLowerCase() === k2) {
+                    key = k;
+                    break;
+                }
+            }
+            attrs[key] = String(value);
+            // update this.attrs
+            if (this._attrs) {
+                this._attrs[k2] = decode(attrs[key]);
+            }
+            // Update rawString
+            this.rawAttrs = Object.keys(attrs).map(function (name) {
+                var val = JSON.stringify(attrs[name]);
+                if (val === 'null' || val === '""') {
+                    return name;
+                }
+                return name + "=" + val;
+            }).join(' ');
+        };
+        /**
+         * Replace all the attributes of the HTMLElement by the provided attributes
+         * @param {Attributes} attributes the new attribute set
+         */
+        HTMLElement.prototype.setAttributes = function (attributes) {
+            // Invalidate current this.attributes
+            if (this._attrs) {
+                delete this._attrs;
+            }
+            // Invalidate current this.rawAttributes
+            if (this._rawAttrs) {
+                delete this._rawAttrs;
+            }
+            // Update rawString
+            this.rawAttrs = Object.keys(attributes).map(function (name) {
+                var val = attributes[name];
+                if (val === 'null' || val === '""') {
+                    return name;
+                }
+                return name + "=" + JSON.stringify(String(val));
+            }).join(' ');
+        };
+        HTMLElement.prototype.insertAdjacentHTML = function (where, html) {
+            var _a, _b, _c;
+            var _this = this;
+            if (arguments.length < 2) {
+                throw new Error('2 arguments required');
+            }
+            var p = (0, parse_1.default)(html);
+            if (where === 'afterend') {
+                var idx = this.parentNode.childNodes.findIndex(function (child) {
+                    return child === _this;
+                });
+                (_a = this.parentNode.childNodes).splice.apply(_a, __spreadArray([idx + 1, 0], p.childNodes));
+                p.childNodes.forEach(function (n) {
+                    if (n instanceof HTMLElement) {
+                        n.parentNode = _this.parentNode;
+                    }
+                });
+            }
+            else if (where === 'afterbegin') {
+                (_b = this.childNodes).unshift.apply(_b, p.childNodes);
+            }
+            else if (where === 'beforeend') {
+                p.childNodes.forEach(function (n) {
+                    _this.appendChild(n);
+                });
+            }
+            else if (where === 'beforebegin') {
+                var idx = this.parentNode.childNodes.findIndex(function (child) {
+                    return child === _this;
+                });
+                (_c = this.parentNode.childNodes).splice.apply(_c, __spreadArray([idx, 0], p.childNodes));
+                p.childNodes.forEach(function (n) {
+                    if (n instanceof HTMLElement) {
+                        n.parentNode = _this.parentNode;
+                    }
+                });
+            }
+            else {
+                throw new Error("The value provided ('" + where + "') is not one of 'beforebegin', 'afterbegin', 'beforeend', or 'afterend'");
+            }
+            // if (!where || html === undefined || html === null) {
+            // 	return;
+            // }
+        };
+        Object.defineProperty(HTMLElement.prototype, "nextSibling", {
+            get: function () {
+                if (this.parentNode) {
+                    var children = this.parentNode.childNodes;
+                    var i = 0;
+                    while (i < children.length) {
+                        var child = children[i++];
+                        if (this === child) {
+                            return children[i] || null;
+                        }
+                    }
+                    return null;
+                }
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(HTMLElement.prototype, "nextElementSibling", {
+            get: function () {
+                if (this.parentNode) {
+                    var children = this.parentNode.childNodes;
+                    var i = 0;
+                    var find = false;
+                    while (i < children.length) {
+                        var child = children[i++];
+                        if (find) {
+                            if (child instanceof HTMLElement) {
+                                return child || null;
+                            }
+                        }
+                        else if (this === child) {
+                            find = true;
+                        }
+                    }
+                    return null;
+                }
+            },
+            enumerable: false,
+            configurable: true
+        });
+        return HTMLElement;
+    }(node_2.default));
+    exports.default = HTMLElement;
+    // https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
+    var kMarkupPattern = /<!--[^]*?(?=-->)-->|<(\/?)([a-z][-.:0-9_a-z]*)\s*([^>]*?)(\/?)>/ig;
+    // <(?<tag>[^\s]*)(.*)>(.*)</\k<tag>>
+    // <([a-z][-.:0-9_a-z]*)\s*\/>
+    // <(area|base|br|col|hr|img|input|link|meta|source)\s*(.*)\/?>
+    // <(area|base|br|col|hr|img|input|link|meta|source)\s*(.*)\/?>|<(?<tag>[^\s]*)(.*)>(.*)</\k<tag>>
+    var kAttributePattern = /(^|\s)(id|class)\s*=\s*("([^"]*)"|'([^']*)'|(\S+))/ig;
+    var kSelfClosingElements = {
+        area: true,
+        AREA: true,
+        base: true,
+        BASE: true,
+        br: true,
+        BR: true,
+        col: true,
+        COL: true,
+        hr: true,
+        HR: true,
+        img: true,
+        IMG: true,
+        input: true,
+        INPUT: true,
+        link: true,
+        LINK: true,
+        meta: true,
+        META: true,
+        source: true,
+        SOURCE: true,
+        embed: true,
+        EMBED: true,
+        param: true,
+        PARAM: true,
+        track: true,
+        TRACK: true,
+        wbr: true,
+        WBR: true
+    };
+    var kElementsClosedByOpening = {
+        li: { li: true, LI: true },
+        LI: { li: true, LI: true },
+        p: { p: true, div: true, P: true, DIV: true },
+        P: { p: true, div: true, P: true, DIV: true },
+        b: { div: true, DIV: true },
+        B: { div: true, DIV: true },
+        td: { td: true, th: true, TD: true, TH: true },
+        TD: { td: true, th: true, TD: true, TH: true },
+        th: { td: true, th: true, TD: true, TH: true },
+        TH: { td: true, th: true, TD: true, TH: true },
+        h1: { h1: true, H1: true },
+        H1: { h1: true, H1: true },
+        h2: { h2: true, H2: true },
+        H2: { h2: true, H2: true },
+        h3: { h3: true, H3: true },
+        H3: { h3: true, H3: true },
+        h4: { h4: true, H4: true },
+        H4: { h4: true, H4: true },
+        h5: { h5: true, H5: true },
+        H5: { h5: true, H5: true },
+        h6: { h6: true, H6: true },
+        H6: { h6: true, H6: true }
+    };
+    var kElementsClosedByClosing = {
+        li: { ul: true, ol: true, UL: true, OL: true },
+        LI: { ul: true, ol: true, UL: true, OL: true },
+        a: { div: true, DIV: true },
+        A: { div: true, DIV: true },
+        b: { div: true, DIV: true },
+        B: { div: true, DIV: true },
+        i: { div: true, DIV: true },
+        I: { div: true, DIV: true },
+        p: { div: true, DIV: true },
+        P: { div: true, DIV: true },
+        td: { tr: true, table: true, TR: true, TABLE: true },
+        TD: { tr: true, table: true, TR: true, TABLE: true },
+        th: { tr: true, table: true, TR: true, TABLE: true },
+        TH: { tr: true, table: true, TR: true, TABLE: true }
+    };
+    var frameflag = 'documentfragmentcontainer';
+    /**
+     * Parses HTML and returns a root element
+     * Parse a chuck of HTML source.
+     * @param  {string} data      html
+     * @return {HTMLElement}      root element
+     */
+    function base_parse(data, options) {
+        if (options === void 0) { options = { lowerCaseTagName: false, comment: false }; }
+        var elements = options.blockTextElements || {
+            script: true,
+            noscript: true,
+            style: true,
+            pre: true
+        };
+        var element_names = Object.keys(elements);
+        var kBlockTextElements = element_names.map(function (it) {
+            return new RegExp(it, 'i');
+        });
+        var kIgnoreElements = element_names.filter(function (it) {
+            return elements[it];
+        }).map(function (it) {
+            return new RegExp(it, 'i');
+        });
+        function element_should_be_ignore(tag) {
+            return kIgnoreElements.some(function (it) {
+                return it.test(tag);
+            });
+        }
+        function is_block_text_element(tag) {
+            return kBlockTextElements.some(function (it) {
+                return it.test(tag);
+            });
+        }
+        var root = new HTMLElement(null, {}, '', null);
+        var currentParent = root;
+        var stack = [root];
+        var lastTextPos = -1;
+        var match;
+        // https://github.com/taoqf/node-html-parser/issues/38
+        data = "<" + frameflag + ">" + data + "</" + frameflag + ">";
+        var dataOffset = ("<" + frameflag + ">").length;
+        var dataLastIndex = data.length - ("</" + frameflag + ">").length - 1;
+        function nodeArgs(start, end) {
+            var text = data.substring(start, end);
+            var nodeOptions = {
+                start: start - dataOffset,
+                end: end - dataOffset,
+            };
+            return { text: text, nodeOptions: nodeOptions };
+        }
+        var _loop_2 = function () {
+            // match[0] is open or close tag, without content
+            //console.dir({ ...match, input: undefined }); // debug
+            if (lastTextPos > -1) {
+                if (lastTextPos + match[0].length < kMarkupPattern.lastIndex) {
+                    // if has content
+                    var _a = nodeArgs(lastTextPos, match.index), text = _a.text, nodeOptions = _a.nodeOptions;
+                    currentParent.appendChild(new text_1.default(text, currentParent, nodeOptions));
+                }
+            }
+            lastTextPos = kMarkupPattern.lastIndex;
+            if (match[2] === frameflag) {
+                return "continue";
+            }
+            if (match[0][1] === '!') {
+                // this is a comment
+                if (options.comment) {
+                    // Only keep what is in between <!-- and -->
+                    var _b = nodeArgs(lastTextPos - 3, lastTextPos - match[0].length + 4), text = _b.text, nodeOptions = _b.nodeOptions;
+                    currentParent.appendChild(new comment_1.default(text, currentParent, nodeOptions));
+                }
+                return "continue";
+            }
+            if (options.lowerCaseTagName) {
+                match[2] = match[2].toLowerCase();
+            }
+            if (!match[1]) {
+                // open tag
+                var attrs = {};
+                if (match[3]) {
+                    for (var attMatch = void 0; (attMatch = kAttributePattern.exec(match[3]));) {
+                        attrs[attMatch[2].toLowerCase()] = attMatch[4] || attMatch[5] || attMatch[6];
+                    }
+                }
+                var tagName = currentParent.rawTagName;
+                if (!match[4] && kElementsClosedByOpening[tagName]) {
+                    if (kElementsClosedByOpening[tagName][match[2]]) {
+                        stack.pop();
+                        currentParent = (0, back_2.default)(stack);
+                    }
+                }
+                var nodeOptions = {
+                    start: match.index - dataOffset,
+                    //end: -1, // end is unknown here
+                };
+                // ignore container tag we add above
+                // https://github.com/taoqf/node-html-parser/issues/38
+                //console.dir({ new_HTMLElement: { name: match[2], attrs, attrRaw: match[3], parent: null, nodeOptions } }); // debug
+                currentParent = currentParent.appendChild(new HTMLElement(match[2], attrs, match[3], null, nodeOptions));
+                stack.push(currentParent);
+                if (is_block_text_element(match[2])) {
+                    // a little test to find next </script> or </style> ...
+                    var closeMarkup_1 = "</" + match[2] + ">";
+                    var index = (function () {
+                        if (options.lowerCaseTagName) {
+                            return data.toLocaleLowerCase().indexOf(closeMarkup_1, kMarkupPattern.lastIndex);
+                        }
+                        return data.indexOf(closeMarkup_1, kMarkupPattern.lastIndex);
+                    })();
+                    if (element_should_be_ignore(match[2])) {
+                        var text = void 0;
+                        var nodeOptions_1 = {
+                            start: kMarkupPattern.lastIndex - dataOffset,
+                            end: dataLastIndex, // TODO verify
+                        };
+                        if (index === -1) {
+                            // there is no matching ending for the text element.
+                            text = data.substr(kMarkupPattern.lastIndex);
+                        }
+                        else {
+                            text = data.substring(kMarkupPattern.lastIndex, index);
+                            nodeOptions_1.end = index - dataOffset;
+                        }
+                        if (text.length > 0) {
+                            currentParent.appendChild(new text_1.default(text, currentParent, nodeOptions_1));
+                        }
+                    }
+                    if (index === -1) {
+                        lastTextPos = kMarkupPattern.lastIndex = data.length + 1;
+                    }
+                    else {
+                        lastTextPos = kMarkupPattern.lastIndex = index + closeMarkup_1.length;
+                        match[1] = 'true';
+                    }
+                }
+            }
+            if (match[1] || match[4] || kSelfClosingElements[match[2]]) {
+                // </ or /> or <br> etc.
+                while (true) {
+                    if (currentParent.rawTagName === match[2]) {
+                        stack.pop();
+                        currentParent = (0, back_2.default)(stack);
+                        break;
+                    }
+                    else {
+                        var tagName = currentParent.tagName;
+                        // Trying to close current tag, and move on
+                        if (kElementsClosedByClosing[tagName]) {
+                            if (kElementsClosedByClosing[tagName][match[2]]) {
+                                stack.pop();
+                                currentParent = (0, back_2.default)(stack);
+                                continue;
+                            }
+                        }
+                        // Use aggressive strategy to handle unmatching markups.
+                        break;
+                    }
+                }
+            }
+        };
+        while ((match = kMarkupPattern.exec(data))) {
+            _loop_2();
+        }
+        return stack;
+    }
+    exports.base_parse = base_parse;
+});
+define("nodes/node", ["require", "exports"], function (require, exports) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    /**
+     * Node Class as base class for TextNode and HTMLElement.
+     */
+    var Node = /** @class */ (function () {
+        function Node(parentNode, nodeOptions) {
+            if (parentNode === void 0) { parentNode = null; }
+            if (nodeOptions === void 0) { nodeOptions = {}; }
+            this.parentNode = parentNode;
+            this.childNodes = [];
+            this._source = {
+                start: nodeOptions.start,
+                end: nodeOptions.end,
+            };
+        }
+        Object.defineProperty(Node.prototype, "innerText", {
+            get: function () {
+                return this.rawText;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(Node.prototype, "textContent", {
+            get: function () {
+                return this.rawText;
+            },
+            set: function (val) {
+                this.rawText = val;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        return Node;
+    }());
+    exports.default = Node;
+});
+define("nodes/comment", ["require", "exports", "nodes/node", "nodes/type"], function (require, exports, node_3, type_4) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    node_3 = __importDefault(node_3);
+    var CommentNode = /** @class */ (function (_super) {
+        __extends(CommentNode, _super);
+        function CommentNode(rawText, parentNode, nodeOptions) {
+            var _this = _super.call(this, parentNode, nodeOptions) || this;
+            _this.rawText = rawText;
+            /**
+             * Node Type declaration.
+             * @type {Number}
+             */
+            _this.nodeType = type_4.NodeType.COMMENT_NODE;
+            return _this;
+        }
+        Object.defineProperty(CommentNode.prototype, "text", {
+            /**
+             * Get unescaped text value of current node and its children.
+             * @return {string} text content
+             */
+            get: function () {
+                return this.rawText;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        CommentNode.prototype.toString = function () {
+            return "<!--" + this.rawText + "-->";
+        };
+        return CommentNode;
+    }(node_3.default));
+    exports.default = CommentNode;
+});
+define("valid", ["require", "exports", "nodes/html"], function (require, exports, html_2) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    /**
+     * Parses HTML and returns a root element
+     * Parse a chuck of HTML source.
+     */
+    function valid(data, options) {
+        if (options === void 0) { options = { lowerCaseTagName: false, comment: false }; }
+        var stack = (0, html_2.base_parse)(data, options);
+        return Boolean(stack.length === 1);
+    }
+    exports.default = valid;
+});
+define("index", ["require", "exports", "nodes/comment", "nodes/html", "parse", "valid", "nodes/node", "nodes/text", "nodes/type"], function (require, exports, comment_2, html_3, parse_2, valid_1, node_4, text_2, type_5) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.NodeType = exports.TextNode = exports.Node = exports.valid = exports.default = exports.parse = exports.HTMLElement = exports.CommentNode = void 0;
+    Object.defineProperty(exports, "CommentNode", { enumerable: true, get: function () { return __importDefault(comment_2).default; } });
+    Object.defineProperty(exports, "HTMLElement", { enumerable: true, get: function () { return __importDefault(html_3).default; } });
+    Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return __importDefault(parse_2).default; } });
+    Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(parse_2).default; } });
+    Object.defineProperty(exports, "valid", { enumerable: true, get: function () { return __importDefault(valid_1).default; } });
+    Object.defineProperty(exports, "Node", { enumerable: true, get: function () { return __importDefault(node_4).default; } });
+    Object.defineProperty(exports, "TextNode", { enumerable: true, get: function () { return __importDefault(text_2).default; } });
+    Object.defineProperty(exports, "NodeType", { enumerable: true, get: function () { return type_5.NodeType; } });
+});
diff --git a/node_modules/node-html-parser/dist/matcher.d.ts b/node_modules/node-html-parser/dist/matcher.d.ts
new file mode 100644
index 0000000..6eb4d7d
--- /dev/null
+++ b/node_modules/node-html-parser/dist/matcher.d.ts
@@ -0,0 +1,6 @@
+import { Adapter } from 'css-select/lib/types';
+import HTMLElement from './nodes/html';
+import Node from './nodes/node';
+export declare type Predicate = (node: Node) => node is HTMLElement;
+declare const _default: Adapter<Node, HTMLElement>;
+export default _default;
diff --git a/node_modules/node-html-parser/dist/matcher.js b/node_modules/node-html-parser/dist/matcher.js
new file mode 100644
index 0000000..959a27a
--- /dev/null
+++ b/node_modules/node-html-parser/dist/matcher.js
@@ -0,0 +1,103 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var type_1 = require("./nodes/type");
+function isTag(node) {
+    return node && node.nodeType === type_1.NodeType.ELEMENT_NODE;
+}
+function getAttributeValue(elem, name) {
+    return isTag(elem) ? elem.getAttribute(name) : undefined;
+}
+function getName(elem) {
+    return ((elem && elem.rawTagName) || '').toLowerCase();
+}
+function getChildren(node) {
+    return node && node.childNodes;
+}
+function getParent(node) {
+    return node ? node.parentNode : null;
+}
+function getText(node) {
+    return node.text;
+}
+function removeSubsets(nodes) {
+    var idx = nodes.length;
+    var node;
+    var ancestor;
+    var replace;
+    // Check if each node (or one of its ancestors) is already contained in the
+    // array.
+    while (--idx > -1) {
+        node = ancestor = nodes[idx];
+        // Temporarily remove the node under consideration
+        nodes[idx] = null;
+        replace = true;
+        while (ancestor) {
+            if (nodes.indexOf(ancestor) > -1) {
+                replace = false;
+                nodes.splice(idx, 1);
+                break;
+            }
+            ancestor = getParent(ancestor);
+        }
+        // If the node has been found to be unique, re-insert it.
+        if (replace) {
+            nodes[idx] = node;
+        }
+    }
+    return nodes;
+}
+function existsOne(test, elems) {
+    return elems.some(function (elem) {
+        return isTag(elem) ? test(elem) || existsOne(test, getChildren(elem)) : false;
+    });
+}
+function getSiblings(node) {
+    var parent = getParent(node);
+    return parent && getChildren(parent);
+}
+function hasAttrib(elem, name) {
+    return getAttributeValue(elem, name) !== undefined;
+}
+function findOne(test, elems) {
+    var elem = null;
+    for (var i = 0, l = elems.length; i < l && !elem; i++) {
+        var el = elems[i];
+        if (test(el)) {
+            elem = el;
+        }
+        else {
+            var childs = getChildren(el);
+            if (childs && childs.length > 0) {
+                elem = findOne(test, childs);
+            }
+        }
+    }
+    return elem;
+}
+function findAll(test, nodes) {
+    var result = [];
+    for (var i = 0, j = nodes.length; i < j; i++) {
+        if (!isTag(nodes[i]))
+            continue;
+        if (test(nodes[i]))
+            result.push(nodes[i]);
+        var childs = getChildren(nodes[i]);
+        if (childs)
+            result = result.concat(findAll(test, childs));
+    }
+    return result;
+}
+exports.default = {
+    isTag: isTag,
+    getAttributeValue: getAttributeValue,
+    getName: getName,
+    getChildren: getChildren,
+    getParent: getParent,
+    getText: getText,
+    removeSubsets: removeSubsets,
+    existsOne: existsOne,
+    getSiblings: getSiblings,
+    hasAttrib: hasAttrib,
+    findOne: findOne,
+    findAll: findAll
+};
diff --git a/node_modules/node-html-parser/dist/nodes/comment.d.ts b/node_modules/node-html-parser/dist/nodes/comment.d.ts
new file mode 100644
index 0000000..1c7bce2
--- /dev/null
+++ b/node_modules/node-html-parser/dist/nodes/comment.d.ts
@@ -0,0 +1,18 @@
+import Node from './node';
+import { NodeOptions, NodeType } from './type';
+import HTMLElement from './html';
+export default class CommentNode extends Node {
+    rawText: string;
+    constructor(rawText: string, parentNode: HTMLElement, nodeOptions: NodeOptions);
+    /**
+     * Node Type declaration.
+     * @type {Number}
+     */
+    nodeType: NodeType;
+    /**
+     * Get unescaped text value of current node and its children.
+     * @return {string} text content
+     */
+    get text(): string;
+    toString(): string;
+}
diff --git a/node_modules/node-html-parser/dist/nodes/comment.js b/node_modules/node-html-parser/dist/nodes/comment.js
new file mode 100644
index 0000000..5865252
--- /dev/null
+++ b/node_modules/node-html-parser/dist/nodes/comment.js
@@ -0,0 +1,51 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = function (d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+    return function (d, b) {
+        if (typeof b !== "function" && b !== null)
+            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+var node_1 = __importDefault(require("./node"));
+var type_1 = require("./type");
+var CommentNode = /** @class */ (function (_super) {
+    __extends(CommentNode, _super);
+    function CommentNode(rawText, parentNode, nodeOptions) {
+        var _this = _super.call(this, parentNode, nodeOptions) || this;
+        _this.rawText = rawText;
+        /**
+         * Node Type declaration.
+         * @type {Number}
+         */
+        _this.nodeType = type_1.NodeType.COMMENT_NODE;
+        return _this;
+    }
+    Object.defineProperty(CommentNode.prototype, "text", {
+        /**
+         * Get unescaped text value of current node and its children.
+         * @return {string} text content
+         */
+        get: function () {
+            return this.rawText;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    CommentNode.prototype.toString = function () {
+        return "<!--" + this.rawText + "-->";
+    };
+    return CommentNode;
+}(node_1.default));
+exports.default = CommentNode;
diff --git a/node_modules/node-html-parser/dist/nodes/html.d.ts b/node_modules/node-html-parser/dist/nodes/html.d.ts
new file mode 100644
index 0000000..f73484f
--- /dev/null
+++ b/node_modules/node-html-parser/dist/nodes/html.d.ts
@@ -0,0 +1,170 @@
+import Node from './node';
+import { NodeOptions, NodeType } from './type';
+export interface KeyAttributes {
+    id?: string;
+    class?: string;
+}
+export interface Attributes {
+    [key: string]: string;
+}
+export interface RawAttributes {
+    [key: string]: string;
+}
+export declare type InsertPosition = 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend';
+/**
+ * HTMLElement, which contains a set of children.
+ *
+ * Note: this is a minimalist implementation, no complete tree
+ *   structure provided (no parentNode, nextSibling,
+ *   previousSibling etc).
+ * @class HTMLElement
+ * @extends {Node}
+ */
+export default class HTMLElement extends Node {
+    private rawAttrs;
+    private _attrs;
+    private _rawAttrs;
+    rawTagName: string;
+    id: string;
+    classNames: string[];
+    /**
+     * Node Type declaration.
+     */
+    nodeType: NodeType;
+    /**
+     * Creates an instance of HTMLElement.
+     * @param keyAttrs	id and class attribute
+     * @param [rawAttrs]	attributes in string
+     *
+     * @memberof HTMLElement
+     */
+    constructor(tagName: string, keyAttrs: KeyAttributes, rawAttrs: string, parentNode: HTMLElement | null, nodeOptions?: NodeOptions);
+    /**
+     * Remove current element
+     */
+    remove(): void;
+    /**
+     * Remove Child element from childNodes array
+     * @param {HTMLElement} node     node to remove
+     */
+    removeChild(node: Node): void;
+    /**
+     * Exchanges given child with new child
+     * @param {HTMLElement} oldNode     node to exchange
+     * @param {HTMLElement} newNode     new node
+     */
+    exchangeChild(oldNode: Node, newNode: Node): void;
+    get tagName(): string;
+    /**
+     * Get escpaed (as-it) text value of current node and its children.
+     * @return {string} text content
+     */
+    get rawText(): string;
+    get textContent(): string;
+    set textContent(val: string);
+    /**
+     * Get unescaped text value of current node and its children.
+     * @return {string} text content
+     */
+    get text(): string;
+    /**
+     * Get structured Text (with '\n' etc.)
+     * @return {string} structured text
+     */
+    get structuredText(): string;
+    toString(): string;
+    get innerHTML(): string;
+    set_content(content: string | Node | Node[], options?: Options): void;
+    get outerHTML(): string;
+    /**
+     * Trim element from right (in block) after seeing pattern in a TextNode.
+     * @param  {RegExp} pattern pattern to find
+     * @return {HTMLElement}    reference to current node
+     */
+    trimRight(pattern: RegExp): this;
+    /**
+     * Get DOM structure
+     * @return {string} strucutre
+     */
+    get structure(): string;
+    /**
+     * Remove whitespaces in this sub tree.
+     * @return {HTMLElement} pointer to this
+     */
+    removeWhitespace(): this;
+    /**
+     * Query CSS selector to find matching nodes.
+     * @param  {string}         selector Simplified CSS selector
+     * @return {HTMLElement[]}  matching elements
+     */
+    querySelectorAll(selector: string): HTMLElement[];
+    /**
+     * Query CSS Selector to find matching node.
+     * @param  {string}         selector Simplified CSS selector
+     * @return {HTMLElement}    matching node
+     */
+    querySelector(selector: string): HTMLElement;
+    /**
+     * Append a child node to childNodes
+     * @param  {Node} node node to append
+     * @return {Node}      node appended
+     */
+    appendChild<T extends Node = Node>(node: T): T;
+    /**
+     * Get first child node
+     * @return {Node} first child node
+     */
+    get firstChild(): Node;
+    /**
+     * Get last child node
+     * @return {Node} last child node
+     */
+    get lastChild(): Node;
+    /**
+     * Get attributes
+     * @access private
+     * @return {Object} parsed and unescaped attributes
+     */
+    get attrs(): Attributes;
+    get attributes(): Record<string, string>;
+    /**
+     * Get escaped (as-it) attributes
+     * @return {Object} parsed attributes
+     */
+    get rawAttributes(): RawAttributes;
+    removeAttribute(key: string): void;
+    hasAttribute(key: string): boolean;
+    /**
+     * Get an attribute
+     * @return {string} value of the attribute
+     */
+    getAttribute(key: string): string | undefined;
+    /**
+     * Set an attribute value to the HTMLElement
+     * @param {string} key The attribute name
+     * @param {string} value The value to set, or null / undefined to remove an attribute
+     */
+    setAttribute(key: string, value: string): void;
+    /**
+     * Replace all the attributes of the HTMLElement by the provided attributes
+     * @param {Attributes} attributes the new attribute set
+     */
+    setAttributes(attributes: Attributes): void;
+    insertAdjacentHTML(where: InsertPosition, html: string): void;
+    get nextSibling(): Node;
+    get nextElementSibling(): HTMLElement | null;
+}
+export interface Options {
+    lowerCaseTagName: boolean;
+    comment: boolean;
+    blockTextElements: {
+        [tag: string]: boolean;
+    };
+}
+/**
+ * Parses HTML and returns a root element
+ * Parse a chuck of HTML source.
+ * @param  {string} data      html
+ * @return {HTMLElement}      root element
+ */
+export declare function base_parse(data: string, options?: Partial<Options>): HTMLElement[];
diff --git a/node_modules/node-html-parser/dist/nodes/html.js b/node_modules/node-html-parser/dist/nodes/html.js
new file mode 100644
index 0000000..0e6bd12
--- /dev/null
+++ b/node_modules/node-html-parser/dist/nodes/html.js
@@ -0,0 +1,962 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = function (d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+    return function (d, b) {
+        if (typeof b !== "function" && b !== null)
+            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var __spreadArray = (this && this.__spreadArray) || function (to, from) {
+    for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
+        to[j] = from[i];
+    return to;
+};
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.base_parse = void 0;
+var he_1 = __importDefault(require("he"));
+var css_select_1 = require("css-select");
+var node_1 = __importDefault(require("./node"));
+var type_1 = require("./type");
+var text_1 = __importDefault(require("./text"));
+var matcher_1 = __importDefault(require("../matcher"));
+var back_1 = __importDefault(require("../back"));
+var comment_1 = __importDefault(require("./comment"));
+var parse_1 = __importDefault(require("../parse"));
+var decode = he_1.default.decode;
+var kBlockElements = new Map();
+kBlockElements.set('DIV', true);
+kBlockElements.set('div', true);
+kBlockElements.set('P', true);
+kBlockElements.set('p', true);
+// ul: true,
+// ol: true,
+kBlockElements.set('LI', true);
+kBlockElements.set('li', true);
+// table: true,
+// tr: true,
+kBlockElements.set('TD', true);
+kBlockElements.set('td', true);
+kBlockElements.set('SECTION', true);
+kBlockElements.set('section', true);
+kBlockElements.set('BR', true);
+kBlockElements.set('br', true);
+/**
+ * HTMLElement, which contains a set of children.
+ *
+ * Note: this is a minimalist implementation, no complete tree
+ *   structure provided (no parentNode, nextSibling,
+ *   previousSibling etc).
+ * @class HTMLElement
+ * @extends {Node}
+ */
+var HTMLElement = /** @class */ (function (_super) {
+    __extends(HTMLElement, _super);
+    /**
+     * Creates an instance of HTMLElement.
+     * @param keyAttrs	id and class attribute
+     * @param [rawAttrs]	attributes in string
+     *
+     * @memberof HTMLElement
+     */
+    function HTMLElement(tagName, keyAttrs, rawAttrs, parentNode, nodeOptions) {
+        if (rawAttrs === void 0) { rawAttrs = ''; }
+        if (nodeOptions === void 0) { nodeOptions = {}; }
+        var _this = _super.call(this, parentNode, nodeOptions) || this;
+        _this.rawAttrs = rawAttrs;
+        _this.classNames = [];
+        /**
+         * Node Type declaration.
+         */
+        _this.nodeType = type_1.NodeType.ELEMENT_NODE;
+        _this.rawTagName = tagName;
+        _this.rawAttrs = rawAttrs || '';
+        _this.childNodes = [];
+        if (keyAttrs.id) {
+            _this.id = keyAttrs.id;
+            if (!rawAttrs) {
+                _this.rawAttrs = "id=\"" + keyAttrs.id + "\"";
+            }
+        }
+        if (keyAttrs.class) {
+            _this.classNames = keyAttrs.class.split(/\s+/);
+            if (!rawAttrs) {
+                var cls = "class=\"" + _this.classNames.join(' ') + "\"";
+                if (_this.rawAttrs) {
+                    _this.rawAttrs += " " + cls;
+                }
+                else {
+                    _this.rawAttrs = cls;
+                }
+            }
+        }
+        return _this;
+    }
+    /**
+     * Remove current element
+     */
+    HTMLElement.prototype.remove = function () {
+        var _this = this;
+        if (this.parentNode) {
+            var children = this.parentNode.childNodes;
+            this.parentNode.childNodes = children.filter(function (child) {
+                return _this !== child;
+            });
+        }
+    };
+    /**
+     * Remove Child element from childNodes array
+     * @param {HTMLElement} node     node to remove
+     */
+    HTMLElement.prototype.removeChild = function (node) {
+        this.childNodes = this.childNodes.filter(function (child) {
+            return (child !== node);
+        });
+    };
+    /**
+     * Exchanges given child with new child
+     * @param {HTMLElement} oldNode     node to exchange
+     * @param {HTMLElement} newNode     new node
+     */
+    HTMLElement.prototype.exchangeChild = function (oldNode, newNode) {
+        var children = this.childNodes;
+        this.childNodes = children.map(function (child) {
+            if (child === oldNode) {
+                return newNode;
+            }
+            return child;
+        });
+    };
+    Object.defineProperty(HTMLElement.prototype, "tagName", {
+        get: function () {
+            return this.rawTagName ? this.rawTagName.toUpperCase() : this.rawTagName;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(HTMLElement.prototype, "rawText", {
+        /**
+         * Get escpaed (as-it) text value of current node and its children.
+         * @return {string} text content
+         */
+        get: function () {
+            return this.childNodes.reduce(function (pre, cur) {
+                return (pre += cur.rawText);
+            }, '');
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(HTMLElement.prototype, "textContent", {
+        get: function () {
+            return this.rawText;
+        },
+        set: function (val) {
+            var content = [new text_1.default(val, this)];
+            this.childNodes = content;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(HTMLElement.prototype, "text", {
+        /**
+         * Get unescaped text value of current node and its children.
+         * @return {string} text content
+         */
+        get: function () {
+            return decode(this.rawText);
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(HTMLElement.prototype, "structuredText", {
+        /**
+         * Get structured Text (with '\n' etc.)
+         * @return {string} structured text
+         */
+        get: function () {
+            var currentBlock = [];
+            var blocks = [currentBlock];
+            function dfs(node) {
+                if (node.nodeType === type_1.NodeType.ELEMENT_NODE) {
+                    if (kBlockElements.get(node.rawTagName)) {
+                        if (currentBlock.length > 0) {
+                            blocks.push(currentBlock = []);
+                        }
+                        node.childNodes.forEach(dfs);
+                        if (currentBlock.length > 0) {
+                            blocks.push(currentBlock = []);
+                        }
+                    }
+                    else {
+                        node.childNodes.forEach(dfs);
+                    }
+                }
+                else if (node.nodeType === type_1.NodeType.TEXT_NODE) {
+                    if (node.isWhitespace) {
+                        // Whitespace node, postponed output
+                        currentBlock.prependWhitespace = true;
+                    }
+                    else {
+                        var text = node.text;
+                        if (currentBlock.prependWhitespace) {
+                            text = " " + text;
+                            currentBlock.prependWhitespace = false;
+                        }
+                        currentBlock.push(text);
+                    }
+                }
+            }
+            dfs(this);
+            return blocks.map(function (block) {
+                // Normalize each line's whitespace
+                return block.join('').trim().replace(/\s{2,}/g, ' ');
+            })
+                .join('\n').replace(/\s+$/, ''); // trimRight;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    HTMLElement.prototype.toString = function () {
+        var tag = this.rawTagName;
+        if (tag) {
+            var is_void = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/i.test(tag);
+            var attrs = this.rawAttrs ? " " + this.rawAttrs : '';
+            if (is_void) {
+                return "<" + tag + attrs + ">";
+            }
+            return "<" + tag + attrs + ">" + this.innerHTML + "</" + tag + ">";
+        }
+        return this.innerHTML;
+    };
+    Object.defineProperty(HTMLElement.prototype, "innerHTML", {
+        get: function () {
+            return this.childNodes.map(function (child) {
+                return child.toString();
+            }).join('');
+        },
+        enumerable: false,
+        configurable: true
+    });
+    HTMLElement.prototype.set_content = function (content, options) {
+        if (options === void 0) { options = {}; }
+        if (content instanceof node_1.default) {
+            content = [content];
+        }
+        else if (typeof content == 'string') {
+            var r = (0, parse_1.default)(content, options);
+            content = r.childNodes.length ? r.childNodes : [new text_1.default(content, this)];
+        }
+        this.childNodes = content;
+    };
+    Object.defineProperty(HTMLElement.prototype, "outerHTML", {
+        get: function () {
+            return this.toString();
+        },
+        enumerable: false,
+        configurable: true
+    });
+    /**
+     * Trim element from right (in block) after seeing pattern in a TextNode.
+     * @param  {RegExp} pattern pattern to find
+     * @return {HTMLElement}    reference to current node
+     */
+    HTMLElement.prototype.trimRight = function (pattern) {
+        for (var i = 0; i < this.childNodes.length; i++) {
+            var childNode = this.childNodes[i];
+            if (childNode.nodeType === type_1.NodeType.ELEMENT_NODE) {
+                childNode.trimRight(pattern);
+            }
+            else {
+                var index = childNode.rawText.search(pattern);
+                if (index > -1) {
+                    childNode.rawText = childNode.rawText.substr(0, index);
+                    // trim all following nodes.
+                    this.childNodes.length = i + 1;
+                }
+            }
+        }
+        return this;
+    };
+    Object.defineProperty(HTMLElement.prototype, "structure", {
+        /**
+         * Get DOM structure
+         * @return {string} strucutre
+         */
+        get: function () {
+            var res = [];
+            var indention = 0;
+            function write(str) {
+                res.push('  '.repeat(indention) + str);
+            }
+            function dfs(node) {
+                var idStr = node.id ? ("#" + node.id) : '';
+                var classStr = node.classNames.length ? ("." + node.classNames.join('.')) : '';
+                write("" + node.rawTagName + idStr + classStr);
+                indention++;
+                node.childNodes.forEach(function (childNode) {
+                    if (childNode.nodeType === type_1.NodeType.ELEMENT_NODE) {
+                        dfs(childNode);
+                    }
+                    else if (childNode.nodeType === type_1.NodeType.TEXT_NODE) {
+                        if (!childNode.isWhitespace) {
+                            write('#text');
+                        }
+                    }
+                });
+                indention--;
+            }
+            dfs(this);
+            return res.join('\n');
+        },
+        enumerable: false,
+        configurable: true
+    });
+    /**
+     * Remove whitespaces in this sub tree.
+     * @return {HTMLElement} pointer to this
+     */
+    HTMLElement.prototype.removeWhitespace = function () {
+        var _this = this;
+        var o = 0;
+        this.childNodes.forEach(function (node) {
+            if (node.nodeType === type_1.NodeType.TEXT_NODE) {
+                if (node.isWhitespace) {
+                    return;
+                }
+                node.rawText = node.rawText.trim();
+            }
+            else if (node.nodeType === type_1.NodeType.ELEMENT_NODE) {
+                node.removeWhitespace();
+            }
+            _this.childNodes[o++] = node;
+        });
+        this.childNodes.length = o;
+        return this;
+    };
+    /**
+     * Query CSS selector to find matching nodes.
+     * @param  {string}         selector Simplified CSS selector
+     * @return {HTMLElement[]}  matching elements
+     */
+    HTMLElement.prototype.querySelectorAll = function (selector) {
+        return (0, css_select_1.selectAll)(selector, this, {
+            xmlMode: true,
+            adapter: matcher_1.default
+        });
+        // let matcher: Matcher;
+        // if (selector instanceof Matcher) {
+        // 	matcher = selector;
+        // 	matcher.reset();
+        // } else {
+        // 	if (selector.includes(',')) {
+        // 		const selectors = selector.split(',');
+        // 		return Array.from(selectors.reduce((pre, cur) => {
+        // 			const result = this.querySelectorAll(cur.trim());
+        // 			return result.reduce((p, c) => {
+        // 				return p.add(c);
+        // 			}, pre);
+        // 		}, new Set<HTMLElement>()));
+        // 	}
+        // 	matcher = new Matcher(selector);
+        // }
+        // interface IStack {
+        // 	0: Node;	// node
+        // 	1: number;	// children
+        // 	2: boolean;	// found flag
+        // }
+        // const stack = [] as IStack[];
+        // return this.childNodes.reduce((res, cur) => {
+        // 	stack.push([cur, 0, false]);
+        // 	while (stack.length) {
+        // 		const state = arr_back(stack);	// get last element
+        // 		const el = state[0];
+        // 		if (state[1] === 0) {
+        // 			// Seen for first time.
+        // 			if (el.nodeType !== NodeType.ELEMENT_NODE) {
+        // 				stack.pop();
+        // 				continue;
+        // 			}
+        // 			const html_el = el as HTMLElement;
+        // 			state[2] = matcher.advance(html_el);
+        // 			if (state[2]) {
+        // 				if (matcher.matched) {
+        // 					res.push(html_el);
+        // 					res.push(...(html_el.querySelectorAll(selector)));
+        // 					// no need to go further.
+        // 					matcher.rewind();
+        // 					stack.pop();
+        // 					continue;
+        // 				}
+        // 			}
+        // 		}
+        // 		if (state[1] < el.childNodes.length) {
+        // 			stack.push([el.childNodes[state[1]++], 0, false]);
+        // 		} else {
+        // 			if (state[2]) {
+        // 				matcher.rewind();
+        // 			}
+        // 			stack.pop();
+        // 		}
+        // 	}
+        // 	return res;
+        // }, [] as HTMLElement[]);
+    };
+    /**
+     * Query CSS Selector to find matching node.
+     * @param  {string}         selector Simplified CSS selector
+     * @return {HTMLElement}    matching node
+     */
+    HTMLElement.prototype.querySelector = function (selector) {
+        return (0, css_select_1.selectOne)(selector, this, {
+            xmlMode: true,
+            adapter: matcher_1.default
+        });
+        // let matcher: Matcher;
+        // if (selector instanceof Matcher) {
+        // 	matcher = selector;
+        // 	matcher.reset();
+        // } else {
+        // 	matcher = new Matcher(selector);
+        // }
+        // const stack = [] as { 0: Node; 1: 0 | 1; 2: boolean }[];
+        // for (const node of this.childNodes) {
+        // 	stack.push([node, 0, false]);
+        // 	while (stack.length) {
+        // 		const state = arr_back(stack);
+        // 		const el = state[0];
+        // 		if (state[1] === 0) {
+        // 			// Seen for first time.
+        // 			if (el.nodeType !== NodeType.ELEMENT_NODE) {
+        // 				stack.pop();
+        // 				continue;
+        // 			}
+        // 			state[2] = matcher.advance(el as HTMLElement);
+        // 			if (state[2]) {
+        // 				if (matcher.matched) {
+        // 					return el as HTMLElement;
+        // 				}
+        // 			}
+        // 		}
+        // 		if (state[1] < el.childNodes.length) {
+        // 			stack.push([el.childNodes[state[1]++], 0, false]);
+        // 		} else {
+        // 			if (state[2]) {
+        // 				matcher.rewind();
+        // 			}
+        // 			stack.pop();
+        // 		}
+        // 	}
+        // }
+        // return null;
+    };
+    /**
+     * Append a child node to childNodes
+     * @param  {Node} node node to append
+     * @return {Node}      node appended
+     */
+    HTMLElement.prototype.appendChild = function (node) {
+        // node.parentNode = this;
+        this.childNodes.push(node);
+        node.parentNode = this;
+        return node;
+    };
+    Object.defineProperty(HTMLElement.prototype, "firstChild", {
+        /**
+         * Get first child node
+         * @return {Node} first child node
+         */
+        get: function () {
+            return this.childNodes[0];
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(HTMLElement.prototype, "lastChild", {
+        /**
+         * Get last child node
+         * @return {Node} last child node
+         */
+        get: function () {
+            return (0, back_1.default)(this.childNodes);
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(HTMLElement.prototype, "attrs", {
+        /**
+         * Get attributes
+         * @access private
+         * @return {Object} parsed and unescaped attributes
+         */
+        get: function () {
+            if (this._attrs) {
+                return this._attrs;
+            }
+            this._attrs = {};
+            var attrs = this.rawAttributes;
+            for (var key in attrs) {
+                var val = attrs[key] || '';
+                this._attrs[key.toLowerCase()] = decode(val);
+            }
+            return this._attrs;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(HTMLElement.prototype, "attributes", {
+        get: function () {
+            var ret_attrs = {};
+            var attrs = this.rawAttributes;
+            for (var key in attrs) {
+                var val = attrs[key] || '';
+                ret_attrs[key] = decode(val);
+            }
+            return ret_attrs;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(HTMLElement.prototype, "rawAttributes", {
+        /**
+         * Get escaped (as-it) attributes
+         * @return {Object} parsed attributes
+         */
+        get: function () {
+            if (this._rawAttrs) {
+                return this._rawAttrs;
+            }
+            var attrs = {};
+            if (this.rawAttrs) {
+                var re = /\b([a-z][a-z0-9-_]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/ig;
+                var match = void 0;
+                while ((match = re.exec(this.rawAttrs))) {
+                    attrs[match[1]] = match[2] || match[3] || match[4] || null;
+                }
+            }
+            this._rawAttrs = attrs;
+            return attrs;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    HTMLElement.prototype.removeAttribute = function (key) {
+        var attrs = this.rawAttributes;
+        delete attrs[key];
+        // Update this.attribute
+        if (this._attrs) {
+            delete this._attrs[key];
+        }
+        // Update rawString
+        this.rawAttrs = Object.keys(attrs).map(function (name) {
+            var val = JSON.stringify(attrs[name]);
+            if (val === undefined || val === 'null') {
+                return name;
+            }
+            return name + "=" + val;
+        }).join(' ');
+    };
+    HTMLElement.prototype.hasAttribute = function (key) {
+        return key.toLowerCase() in this.attrs;
+    };
+    /**
+     * Get an attribute
+     * @return {string} value of the attribute
+     */
+    HTMLElement.prototype.getAttribute = function (key) {
+        return this.attrs[key.toLowerCase()];
+    };
+    /**
+     * Set an attribute value to the HTMLElement
+     * @param {string} key The attribute name
+     * @param {string} value The value to set, or null / undefined to remove an attribute
+     */
+    HTMLElement.prototype.setAttribute = function (key, value) {
+        if (arguments.length < 2) {
+            throw new Error('Failed to execute \'setAttribute\' on \'Element\'');
+        }
+        var k2 = key.toLowerCase();
+        var attrs = this.rawAttributes;
+        for (var k in attrs) {
+            if (k.toLowerCase() === k2) {
+                key = k;
+                break;
+            }
+        }
+        attrs[key] = String(value);
+        // update this.attrs
+        if (this._attrs) {
+            this._attrs[k2] = decode(attrs[key]);
+        }
+        // Update rawString
+        this.rawAttrs = Object.keys(attrs).map(function (name) {
+            var val = JSON.stringify(attrs[name]);
+            if (val === 'null' || val === '""') {
+                return name;
+            }
+            return name + "=" + val;
+        }).join(' ');
+    };
+    /**
+     * Replace all the attributes of the HTMLElement by the provided attributes
+     * @param {Attributes} attributes the new attribute set
+     */
+    HTMLElement.prototype.setAttributes = function (attributes) {
+        // Invalidate current this.attributes
+        if (this._attrs) {
+            delete this._attrs;
+        }
+        // Invalidate current this.rawAttributes
+        if (this._rawAttrs) {
+            delete this._rawAttrs;
+        }
+        // Update rawString
+        this.rawAttrs = Object.keys(attributes).map(function (name) {
+            var val = attributes[name];
+            if (val === 'null' || val === '""') {
+                return name;
+            }
+            return name + "=" + JSON.stringify(String(val));
+        }).join(' ');
+    };
+    HTMLElement.prototype.insertAdjacentHTML = function (where, html) {
+        var _a, _b, _c;
+        var _this = this;
+        if (arguments.length < 2) {
+            throw new Error('2 arguments required');
+        }
+        var p = (0, parse_1.default)(html);
+        if (where === 'afterend') {
+            var idx = this.parentNode.childNodes.findIndex(function (child) {
+                return child === _this;
+            });
+            (_a = this.parentNode.childNodes).splice.apply(_a, __spreadArray([idx + 1, 0], p.childNodes));
+            p.childNodes.forEach(function (n) {
+                if (n instanceof HTMLElement) {
+                    n.parentNode = _this.parentNode;
+                }
+            });
+        }
+        else if (where === 'afterbegin') {
+            (_b = this.childNodes).unshift.apply(_b, p.childNodes);
+        }
+        else if (where === 'beforeend') {
+            p.childNodes.forEach(function (n) {
+                _this.appendChild(n);
+            });
+        }
+        else if (where === 'beforebegin') {
+            var idx = this.parentNode.childNodes.findIndex(function (child) {
+                return child === _this;
+            });
+            (_c = this.parentNode.childNodes).splice.apply(_c, __spreadArray([idx, 0], p.childNodes));
+            p.childNodes.forEach(function (n) {
+                if (n instanceof HTMLElement) {
+                    n.parentNode = _this.parentNode;
+                }
+            });
+        }
+        else {
+            throw new Error("The value provided ('" + where + "') is not one of 'beforebegin', 'afterbegin', 'beforeend', or 'afterend'");
+        }
+        // if (!where || html === undefined || html === null) {
+        // 	return;
+        // }
+    };
+    Object.defineProperty(HTMLElement.prototype, "nextSibling", {
+        get: function () {
+            if (this.parentNode) {
+                var children = this.parentNode.childNodes;
+                var i = 0;
+                while (i < children.length) {
+                    var child = children[i++];
+                    if (this === child) {
+                        return children[i] || null;
+                    }
+                }
+                return null;
+            }
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(HTMLElement.prototype, "nextElementSibling", {
+        get: function () {
+            if (this.parentNode) {
+                var children = this.parentNode.childNodes;
+                var i = 0;
+                var find = false;
+                while (i < children.length) {
+                    var child = children[i++];
+                    if (find) {
+                        if (child instanceof HTMLElement) {
+                            return child || null;
+                        }
+                    }
+                    else if (this === child) {
+                        find = true;
+                    }
+                }
+                return null;
+            }
+        },
+        enumerable: false,
+        configurable: true
+    });
+    return HTMLElement;
+}(node_1.default));
+exports.default = HTMLElement;
+// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
+var kMarkupPattern = /<!--[^]*?(?=-->)-->|<(\/?)([a-z][-.:0-9_a-z]*)\s*([^>]*?)(\/?)>/ig;
+// <(?<tag>[^\s]*)(.*)>(.*)</\k<tag>>
+// <([a-z][-.:0-9_a-z]*)\s*\/>
+// <(area|base|br|col|hr|img|input|link|meta|source)\s*(.*)\/?>
+// <(area|base|br|col|hr|img|input|link|meta|source)\s*(.*)\/?>|<(?<tag>[^\s]*)(.*)>(.*)</\k<tag>>
+var kAttributePattern = /(^|\s)(id|class)\s*=\s*("([^"]*)"|'([^']*)'|(\S+))/ig;
+var kSelfClosingElements = {
+    area: true,
+    AREA: true,
+    base: true,
+    BASE: true,
+    br: true,
+    BR: true,
+    col: true,
+    COL: true,
+    hr: true,
+    HR: true,
+    img: true,
+    IMG: true,
+    input: true,
+    INPUT: true,
+    link: true,
+    LINK: true,
+    meta: true,
+    META: true,
+    source: true,
+    SOURCE: true,
+    embed: true,
+    EMBED: true,
+    param: true,
+    PARAM: true,
+    track: true,
+    TRACK: true,
+    wbr: true,
+    WBR: true
+};
+var kElementsClosedByOpening = {
+    li: { li: true, LI: true },
+    LI: { li: true, LI: true },
+    p: { p: true, div: true, P: true, DIV: true },
+    P: { p: true, div: true, P: true, DIV: true },
+    b: { div: true, DIV: true },
+    B: { div: true, DIV: true },
+    td: { td: true, th: true, TD: true, TH: true },
+    TD: { td: true, th: true, TD: true, TH: true },
+    th: { td: true, th: true, TD: true, TH: true },
+    TH: { td: true, th: true, TD: true, TH: true },
+    h1: { h1: true, H1: true },
+    H1: { h1: true, H1: true },
+    h2: { h2: true, H2: true },
+    H2: { h2: true, H2: true },
+    h3: { h3: true, H3: true },
+    H3: { h3: true, H3: true },
+    h4: { h4: true, H4: true },
+    H4: { h4: true, H4: true },
+    h5: { h5: true, H5: true },
+    H5: { h5: true, H5: true },
+    h6: { h6: true, H6: true },
+    H6: { h6: true, H6: true }
+};
+var kElementsClosedByClosing = {
+    li: { ul: true, ol: true, UL: true, OL: true },
+    LI: { ul: true, ol: true, UL: true, OL: true },
+    a: { div: true, DIV: true },
+    A: { div: true, DIV: true },
+    b: { div: true, DIV: true },
+    B: { div: true, DIV: true },
+    i: { div: true, DIV: true },
+    I: { div: true, DIV: true },
+    p: { div: true, DIV: true },
+    P: { div: true, DIV: true },
+    td: { tr: true, table: true, TR: true, TABLE: true },
+    TD: { tr: true, table: true, TR: true, TABLE: true },
+    th: { tr: true, table: true, TR: true, TABLE: true },
+    TH: { tr: true, table: true, TR: true, TABLE: true }
+};
+var frameflag = 'documentfragmentcontainer';
+/**
+ * Parses HTML and returns a root element
+ * Parse a chuck of HTML source.
+ * @param  {string} data      html
+ * @return {HTMLElement}      root element
+ */
+function base_parse(data, options) {
+    if (options === void 0) { options = { lowerCaseTagName: false, comment: false }; }
+    var elements = options.blockTextElements || {
+        script: true,
+        noscript: true,
+        style: true,
+        pre: true
+    };
+    var element_names = Object.keys(elements);
+    var kBlockTextElements = element_names.map(function (it) {
+        return new RegExp(it, 'i');
+    });
+    var kIgnoreElements = element_names.filter(function (it) {
+        return elements[it];
+    }).map(function (it) {
+        return new RegExp(it, 'i');
+    });
+    function element_should_be_ignore(tag) {
+        return kIgnoreElements.some(function (it) {
+            return it.test(tag);
+        });
+    }
+    function is_block_text_element(tag) {
+        return kBlockTextElements.some(function (it) {
+            return it.test(tag);
+        });
+    }
+    var root = new HTMLElement(null, {}, '', null);
+    var currentParent = root;
+    var stack = [root];
+    var lastTextPos = -1;
+    var match;
+    // https://github.com/taoqf/node-html-parser/issues/38
+    data = "<" + frameflag + ">" + data + "</" + frameflag + ">";
+    var dataOffset = ("<" + frameflag + ">").length;
+    var dataLastIndex = data.length - ("</" + frameflag + ">").length - 1;
+    function nodeArgs(start, end) {
+        var text = data.substring(start, end);
+        var nodeOptions = {
+            start: start - dataOffset,
+            end: end - dataOffset,
+        };
+        return { text: text, nodeOptions: nodeOptions };
+    }
+    var _loop_1 = function () {
+        // match[0] is open or close tag, without content
+        //console.dir({ ...match, input: undefined }); // debug
+        if (lastTextPos > -1) {
+            if (lastTextPos + match[0].length < kMarkupPattern.lastIndex) {
+                // if has content
+                var _a = nodeArgs(lastTextPos, match.index), text = _a.text, nodeOptions = _a.nodeOptions;
+                currentParent.appendChild(new text_1.default(text, currentParent, nodeOptions));
+            }
+        }
+        lastTextPos = kMarkupPattern.lastIndex;
+        if (match[2] === frameflag) {
+            return "continue";
+        }
+        if (match[0][1] === '!') {
+            // this is a comment
+            if (options.comment) {
+                // Only keep what is in between <!-- and -->
+                var _b = nodeArgs(lastTextPos - 3, lastTextPos - match[0].length + 4), text = _b.text, nodeOptions = _b.nodeOptions;
+                currentParent.appendChild(new comment_1.default(text, currentParent, nodeOptions));
+            }
+            return "continue";
+        }
+        if (options.lowerCaseTagName) {
+            match[2] = match[2].toLowerCase();
+        }
+        if (!match[1]) {
+            // open tag
+            var attrs = {};
+            if (match[3]) {
+                for (var attMatch = void 0; (attMatch = kAttributePattern.exec(match[3]));) {
+                    attrs[attMatch[2].toLowerCase()] = attMatch[4] || attMatch[5] || attMatch[6];
+                }
+            }
+            var tagName = currentParent.rawTagName;
+            if (!match[4] && kElementsClosedByOpening[tagName]) {
+                if (kElementsClosedByOpening[tagName][match[2]]) {
+                    stack.pop();
+                    currentParent = (0, back_1.default)(stack);
+                }
+            }
+            var nodeOptions = {
+                start: match.index - dataOffset,
+                //end: -1, // end is unknown here
+            };
+            // ignore container tag we add above
+            // https://github.com/taoqf/node-html-parser/issues/38
+            //console.dir({ new_HTMLElement: { name: match[2], attrs, attrRaw: match[3], parent: null, nodeOptions } }); // debug
+            currentParent = currentParent.appendChild(new HTMLElement(match[2], attrs, match[3], null, nodeOptions));
+            stack.push(currentParent);
+            if (is_block_text_element(match[2])) {
+                // a little test to find next </script> or </style> ...
+                var closeMarkup_1 = "</" + match[2] + ">";
+                var index = (function () {
+                    if (options.lowerCaseTagName) {
+                        return data.toLocaleLowerCase().indexOf(closeMarkup_1, kMarkupPattern.lastIndex);
+                    }
+                    return data.indexOf(closeMarkup_1, kMarkupPattern.lastIndex);
+                })();
+                if (element_should_be_ignore(match[2])) {
+                    var text = void 0;
+                    var nodeOptions_1 = {
+                        start: kMarkupPattern.lastIndex - dataOffset,
+                        end: dataLastIndex, // TODO verify
+                    };
+                    if (index === -1) {
+                        // there is no matching ending for the text element.
+                        text = data.substr(kMarkupPattern.lastIndex);
+                    }
+                    else {
+                        text = data.substring(kMarkupPattern.lastIndex, index);
+                        nodeOptions_1.end = index - dataOffset;
+                    }
+                    if (text.length > 0) {
+                        currentParent.appendChild(new text_1.default(text, currentParent, nodeOptions_1));
+                    }
+                }
+                if (index === -1) {
+                    lastTextPos = kMarkupPattern.lastIndex = data.length + 1;
+                }
+                else {
+                    lastTextPos = kMarkupPattern.lastIndex = index + closeMarkup_1.length;
+                    match[1] = 'true';
+                }
+            }
+        }
+        if (match[1] || match[4] || kSelfClosingElements[match[2]]) {
+            // </ or /> or <br> etc.
+            while (true) {
+                if (currentParent.rawTagName === match[2]) {
+                    stack.pop();
+                    currentParent = (0, back_1.default)(stack);
+                    break;
+                }
+                else {
+                    var tagName = currentParent.tagName;
+                    // Trying to close current tag, and move on
+                    if (kElementsClosedByClosing[tagName]) {
+                        if (kElementsClosedByClosing[tagName][match[2]]) {
+                            stack.pop();
+                            currentParent = (0, back_1.default)(stack);
+                            continue;
+                        }
+                    }
+                    // Use aggressive strategy to handle unmatching markups.
+                    break;
+                }
+            }
+        }
+    };
+    while ((match = kMarkupPattern.exec(data))) {
+        _loop_1();
+    }
+    return stack;
+}
+exports.base_parse = base_parse;
diff --git a/node_modules/node-html-parser/dist/nodes/node.d.ts b/node_modules/node-html-parser/dist/nodes/node.d.ts
new file mode 100644
index 0000000..079810b
--- /dev/null
+++ b/node_modules/node-html-parser/dist/nodes/node.d.ts
@@ -0,0 +1,18 @@
+import { NodeOptions, NodeType, SourceLocation } from './type';
+import HTMLElement from './html';
+/**
+ * Node Class as base class for TextNode and HTMLElement.
+ */
+export default abstract class Node {
+    parentNode: HTMLElement;
+    abstract nodeType: NodeType;
+    childNodes: Node[];
+    _source: SourceLocation;
+    abstract text: string;
+    abstract rawText: string;
+    abstract toString(): string;
+    constructor(parentNode?: HTMLElement, nodeOptions?: NodeOptions);
+    get innerText(): string;
+    get textContent(): string;
+    set textContent(val: string);
+}
diff --git a/node_modules/node-html-parser/dist/nodes/node.js b/node_modules/node-html-parser/dist/nodes/node.js
new file mode 100644
index 0000000..d24aad7
--- /dev/null
+++ b/node_modules/node-html-parser/dist/nodes/node.js
@@ -0,0 +1,36 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+/**
+ * Node Class as base class for TextNode and HTMLElement.
+ */
+var Node = /** @class */ (function () {
+    function Node(parentNode, nodeOptions) {
+        if (parentNode === void 0) { parentNode = null; }
+        if (nodeOptions === void 0) { nodeOptions = {}; }
+        this.parentNode = parentNode;
+        this.childNodes = [];
+        this._source = {
+            start: nodeOptions.start,
+            end: nodeOptions.end,
+        };
+    }
+    Object.defineProperty(Node.prototype, "innerText", {
+        get: function () {
+            return this.rawText;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(Node.prototype, "textContent", {
+        get: function () {
+            return this.rawText;
+        },
+        set: function (val) {
+            this.rawText = val;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    return Node;
+}());
+exports.default = Node;
diff --git a/node_modules/node-html-parser/dist/nodes/text.d.ts b/node_modules/node-html-parser/dist/nodes/text.d.ts
new file mode 100644
index 0000000..c16a5bc
--- /dev/null
+++ b/node_modules/node-html-parser/dist/nodes/text.d.ts
@@ -0,0 +1,27 @@
+import { NodeOptions, NodeType } from './type';
+import Node from './node';
+import HTMLElement from './html';
+/**
+ * TextNode to contain a text element in DOM tree.
+ * @param {string} value [description]
+ */
+export default class TextNode extends Node {
+    rawText: string;
+    constructor(rawText: string, parentNode: HTMLElement, nodeOptions?: NodeOptions);
+    /**
+     * Node Type declaration.
+     * @type {Number}
+     */
+    nodeType: NodeType;
+    /**
+     * Get unescaped text value of current node and its children.
+     * @return {string} text content
+     */
+    get text(): string;
+    /**
+     * Detect if the node contains only white space.
+     * @return {bool}
+     */
+    get isWhitespace(): boolean;
+    toString(): string;
+}
diff --git a/node_modules/node-html-parser/dist/nodes/text.js b/node_modules/node-html-parser/dist/nodes/text.js
new file mode 100644
index 0000000..7daf0eb
--- /dev/null
+++ b/node_modules/node-html-parser/dist/nodes/text.js
@@ -0,0 +1,67 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = function (d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+    return function (d, b) {
+        if (typeof b !== "function" && b !== null)
+            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+var type_1 = require("./type");
+var node_1 = __importDefault(require("./node"));
+/**
+ * TextNode to contain a text element in DOM tree.
+ * @param {string} value [description]
+ */
+var TextNode = /** @class */ (function (_super) {
+    __extends(TextNode, _super);
+    function TextNode(rawText, parentNode, nodeOptions) {
+        if (nodeOptions === void 0) { nodeOptions = {}; }
+        var _this = _super.call(this, parentNode, nodeOptions) || this;
+        _this.rawText = rawText;
+        /**
+         * Node Type declaration.
+         * @type {Number}
+         */
+        _this.nodeType = type_1.NodeType.TEXT_NODE;
+        return _this;
+    }
+    Object.defineProperty(TextNode.prototype, "text", {
+        /**
+         * Get unescaped text value of current node and its children.
+         * @return {string} text content
+         */
+        get: function () {
+            return this.rawText;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(TextNode.prototype, "isWhitespace", {
+        /**
+         * Detect if the node contains only white space.
+         * @return {bool}
+         */
+        get: function () {
+            return /^(\s|&nbsp;)*$/.test(this.rawText);
+        },
+        enumerable: false,
+        configurable: true
+    });
+    TextNode.prototype.toString = function () {
+        return this.text;
+    };
+    return TextNode;
+}(node_1.default));
+exports.default = TextNode;
diff --git a/node_modules/node-html-parser/dist/nodes/type.d.ts b/node_modules/node-html-parser/dist/nodes/type.d.ts
new file mode 100644
index 0000000..b0a96db
--- /dev/null
+++ b/node_modules/node-html-parser/dist/nodes/type.d.ts
@@ -0,0 +1,13 @@
+export declare enum NodeType {
+    ELEMENT_NODE = 1,
+    TEXT_NODE = 3,
+    COMMENT_NODE = 8
+}
+export declare type SourceLocation = {
+    start: number;
+    end?: number;
+};
+export declare type NodeOptions = {
+    start?: number;
+    end?: number;
+};
diff --git a/node_modules/node-html-parser/dist/nodes/type.js b/node_modules/node-html-parser/dist/nodes/type.js
new file mode 100644
index 0000000..3a4ddad
--- /dev/null
+++ b/node_modules/node-html-parser/dist/nodes/type.js
@@ -0,0 +1,9 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.NodeType = void 0;
+var NodeType;
+(function (NodeType) {
+    NodeType[NodeType["ELEMENT_NODE"] = 1] = "ELEMENT_NODE";
+    NodeType[NodeType["TEXT_NODE"] = 3] = "TEXT_NODE";
+    NodeType[NodeType["COMMENT_NODE"] = 8] = "COMMENT_NODE";
+})(NodeType = exports.NodeType || (exports.NodeType = {}));
diff --git a/node_modules/node-html-parser/dist/parse.d.ts b/node_modules/node-html-parser/dist/parse.d.ts
new file mode 100644
index 0000000..e182e1b
--- /dev/null
+++ b/node_modules/node-html-parser/dist/parse.d.ts
@@ -0,0 +1,6 @@
+import { Options } from './nodes/html';
+/**
+ * Parses HTML and returns a root element
+ * Parse a chuck of HTML source.
+ */
+export default function parse(data: string, options?: Partial<Options>): import("./nodes/html").default;
diff --git a/node_modules/node-html-parser/dist/parse.js b/node_modules/node-html-parser/dist/parse.js
new file mode 100644
index 0000000..02f36a2
--- /dev/null
+++ b/node_modules/node-html-parser/dist/parse.js
@@ -0,0 +1,51 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+var back_1 = __importDefault(require("./back"));
+var html_1 = require("./nodes/html");
+/**
+ * Parses HTML and returns a root element
+ * Parse a chuck of HTML source.
+ */
+function parse(data, options) {
+    if (options === void 0) { options = { lowerCaseTagName: false, comment: false }; }
+    var stack = (0, html_1.base_parse)(data, options);
+    var root = stack[0];
+    var _loop_1 = function () {
+        // Handle each error elements.
+        var last = stack.pop();
+        var oneBefore = (0, back_1.default)(stack);
+        if (last.parentNode && last.parentNode.parentNode) {
+            if (last.parentNode === oneBefore && last.tagName === oneBefore.tagName) {
+                // Pair error case <h3> <h3> handle : Fixes to <h3> </h3>
+                oneBefore.removeChild(last);
+                last.childNodes.forEach(function (child) {
+                    oneBefore.parentNode.appendChild(child);
+                });
+                stack.pop();
+            }
+            else {
+                // Single error  <div> <h3> </div> handle: Just removes <h3>
+                oneBefore.removeChild(last);
+                last.childNodes.forEach(function (child) {
+                    oneBefore.appendChild(child);
+                });
+            }
+        }
+        else {
+            // If it's final element just skip.
+        }
+    };
+    while (stack.length > 1) {
+        _loop_1();
+    }
+    // response.childNodes.forEach((node) => {
+    // 	if (node instanceof HTMLElement) {
+    // 		node.parentNode = null;
+    // 	}
+    // });
+    return root;
+}
+exports.default = parse;
diff --git a/node_modules/node-html-parser/dist/valid.d.ts b/node_modules/node-html-parser/dist/valid.d.ts
new file mode 100644
index 0000000..c834dd9
--- /dev/null
+++ b/node_modules/node-html-parser/dist/valid.d.ts
@@ -0,0 +1,6 @@
+import { Options } from './nodes/html';
+/**
+ * Parses HTML and returns a root element
+ * Parse a chuck of HTML source.
+ */
+export default function valid(data: string, options?: Partial<Options>): boolean;
diff --git a/node_modules/node-html-parser/dist/valid.js b/node_modules/node-html-parser/dist/valid.js
new file mode 100644
index 0000000..4c38157
--- /dev/null
+++ b/node_modules/node-html-parser/dist/valid.js
@@ -0,0 +1,13 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var html_1 = require("./nodes/html");
+/**
+ * Parses HTML and returns a root element
+ * Parse a chuck of HTML source.
+ */
+function valid(data, options) {
+    if (options === void 0) { options = { lowerCaseTagName: false, comment: false }; }
+    var stack = (0, html_1.base_parse)(data, options);
+    return Boolean(stack.length === 1);
+}
+exports.default = valid;
diff --git a/node_modules/node-html-parser/src/index.ts b/node_modules/node-html-parser/src/index.ts
index 2f965e2..f3c4c24 100755
--- a/node_modules/node-html-parser/src/index.ts
+++ b/node_modules/node-html-parser/src/index.ts
@@ -4,4 +4,4 @@ export { default as parse, default } from './parse';
 export { default as valid } from './valid';
 export { default as Node } from './nodes/node';
 export { default as TextNode } from './nodes/text';
-export { default as NodeType } from './nodes/type';
+export { NodeType, SourceLocation } from './nodes/type';
diff --git a/node_modules/node-html-parser/src/matcher.ts b/node_modules/node-html-parser/src/matcher.ts
index 93fd6a3..721de8a 100644
--- a/node_modules/node-html-parser/src/matcher.ts
+++ b/node_modules/node-html-parser/src/matcher.ts
@@ -1,7 +1,7 @@
 import { Adapter/*, Predicate*/ } from 'css-select/lib/types';
 import HTMLElement from './nodes/html';
 import Node from './nodes/node';
-import NodeType from './nodes/type';
+import { NodeType } from './nodes/type';
 
 export declare type Predicate = (node: Node) => node is HTMLElement;
 
diff --git a/node_modules/node-html-parser/src/nodes/comment.ts b/node_modules/node-html-parser/src/nodes/comment.ts
index c9df5eb..bae63f2 100644
--- a/node_modules/node-html-parser/src/nodes/comment.ts
+++ b/node_modules/node-html-parser/src/nodes/comment.ts
@@ -1,10 +1,10 @@
 import Node from './node';
-import NodeType from './type';
+import { NodeOptions, NodeType } from './type';
 import HTMLElement from './html';
 
 export default class CommentNode extends Node {
-	public constructor(public rawText: string, parentNode: HTMLElement) {
-		super(parentNode);
+	public constructor(public rawText: string, parentNode: HTMLElement, nodeOptions: NodeOptions) {
+		super(parentNode, nodeOptions);
 	}
 
 	/**
diff --git a/node_modules/node-html-parser/src/nodes/html.ts b/node_modules/node-html-parser/src/nodes/html.ts
index 964e256..42fc956 100644
--- a/node_modules/node-html-parser/src/nodes/html.ts
+++ b/node_modules/node-html-parser/src/nodes/html.ts
@@ -1,7 +1,7 @@
 import he from 'he';
 import { selectAll, selectOne } from 'css-select';
 import Node from './node';
-import NodeType from './type';
+import { NodeOptions, NodeType } from './type';
 import TextNode from './text';
 import Matcher from '../matcher';
 import arr_back from '../back';
@@ -70,8 +70,8 @@ export default class HTMLElement extends Node {
 	 *
 	 * @memberof HTMLElement
 	 */
-	public constructor(tagName: string, keyAttrs: KeyAttributes, private rawAttrs = '', parentNode: HTMLElement | null) {
-		super(parentNode);
+	public constructor(tagName: string, keyAttrs: KeyAttributes, private rawAttrs = '', parentNode: HTMLElement | null, nodeOptions: NodeOptions = {}) {
+		super(parentNode, nodeOptions);
 		this.rawTagName = tagName;
 		this.rawAttrs = rawAttrs || '';
 		this.childNodes = [];
@@ -638,7 +638,7 @@ export default class HTMLElement extends Node {
 		}
 	}
 
-	public get nextElementSibling() {
+	public get nextElementSibling() : HTMLElement | null {
 		if (this.parentNode) {
 			const children = this.parentNode.childNodes;
 			let i = 0;
@@ -785,12 +785,26 @@ export function base_parse(data: string, options = { lowerCaseTagName: false, co
 	let match: RegExpExecArray;
 	// https://github.com/taoqf/node-html-parser/issues/38
 	data = `<${frameflag}>${data}</${frameflag}>`;
+	const dataOffset = `<${frameflag}>`.length;
+	const dataLastIndex = data.length - `</${frameflag}>`.length - 1;
+
+	function nodeArgs(start: number, end: number) {
+		const text = data.substring(start, end);
+		const nodeOptions = {
+			start: start - dataOffset,
+			end: end - dataOffset,
+		};
+		return { text, nodeOptions };
+	}
+
 	while ((match = kMarkupPattern.exec(data))) {
+		// match[0] is open or close tag, without content
+		//console.dir({ ...match, input: undefined }); // debug
 		if (lastTextPos > -1) {
 			if (lastTextPos + match[0].length < kMarkupPattern.lastIndex) {
 				// if has content
-				const text = data.substring(lastTextPos, kMarkupPattern.lastIndex - match[0].length);
-				currentParent.appendChild(new TextNode(text, currentParent));
+				const { text, nodeOptions } = nodeArgs(lastTextPos, match.index);
+				currentParent.appendChild(new TextNode(text, currentParent, nodeOptions));
 			}
 		}
 		lastTextPos = kMarkupPattern.lastIndex;
@@ -801,8 +815,11 @@ export function base_parse(data: string, options = { lowerCaseTagName: false, co
 			// this is a comment
 			if (options.comment) {
 				// Only keep what is in between <!-- and -->
-				const text = data.substring(lastTextPos - 3, lastTextPos - match[0].length + 4);
-				currentParent.appendChild(new CommentNode(text, currentParent));
+				const { text, nodeOptions } = nodeArgs(
+					lastTextPos - 3,
+					lastTextPos - match[0].length + 4
+				);
+				currentParent.appendChild(new CommentNode(text, currentParent, nodeOptions));
 			}
 			continue;
 		}
@@ -810,10 +827,12 @@ export function base_parse(data: string, options = { lowerCaseTagName: false, co
 			match[2] = match[2].toLowerCase();
 		}
 		if (!match[1]) {
-			// not </ tags
+			// open tag
 			const attrs = {};
-			for (let attMatch; (attMatch = kAttributePattern.exec(match[3]));) {
-				attrs[attMatch[2].toLowerCase()] = attMatch[4] || attMatch[5] || attMatch[6];
+			if (match[3]) {
+				for (let attMatch; (attMatch = kAttributePattern.exec(match[3]));) {
+					attrs[attMatch[2].toLowerCase()] = attMatch[4] || attMatch[5] || attMatch[6];
+				}
 			}
 
 			const tagName = currentParent.rawTagName as 'LI' | 'P' | 'B' | 'TD' | 'TH' | 'H1' | 'H2' | 'H3' | 'H4' | 'H5' | 'H6' | 'li' | 'p' | 'b' | 'td' | 'th' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
@@ -823,9 +842,14 @@ export function base_parse(data: string, options = { lowerCaseTagName: false, co
 					currentParent = arr_back(stack);
 				}
 			}
+			const nodeOptions = {
+				start: match.index - dataOffset,
+				//end: -1, // end is unknown here
+			};
 			// ignore container tag we add above
 			// https://github.com/taoqf/node-html-parser/issues/38
-			currentParent = currentParent.appendChild(new HTMLElement(match[2], attrs, match[3], null));
+			//console.dir({ new_HTMLElement: { name: match[2], attrs, attrRaw: match[3], parent: null, nodeOptions } }); // debug
+			currentParent = currentParent.appendChild(new HTMLElement(match[2], attrs, match[3], null, nodeOptions));
 			stack.push(currentParent);
 			if (is_block_text_element(match[2])) {
 				// a little test to find next </script> or </style> ...
@@ -838,14 +862,19 @@ export function base_parse(data: string, options = { lowerCaseTagName: false, co
 				})();
 				if (element_should_be_ignore(match[2])) {
 					let text: string;
+					const nodeOptions = {
+						start: kMarkupPattern.lastIndex - dataOffset,
+						end: dataLastIndex, // TODO verify
+					};
 					if (index === -1) {
 						// there is no matching ending for the text element.
 						text = data.substr(kMarkupPattern.lastIndex);
 					} else {
 						text = data.substring(kMarkupPattern.lastIndex, index);
+						nodeOptions.end = index - dataOffset;
 					}
 					if (text.length > 0) {
-						currentParent.appendChild(new TextNode(text, currentParent));
+						currentParent.appendChild(new TextNode(text, currentParent, nodeOptions));
 					}
 				}
 				if (index === -1) {
diff --git a/node_modules/node-html-parser/src/nodes/node.ts b/node_modules/node-html-parser/src/nodes/node.ts
index 574dbc7..b25587b 100644
--- a/node_modules/node-html-parser/src/nodes/node.ts
+++ b/node_modules/node-html-parser/src/nodes/node.ts
@@ -1,4 +1,4 @@
-import NodeType from './type';
+import { NodeOptions, NodeType, SourceLocation } from './type';
 import HTMLElement from './html';
 
 /**
@@ -7,11 +7,16 @@ import HTMLElement from './html';
 export default abstract class Node {
 	abstract nodeType: NodeType;
 	public childNodes = [] as Node[];
+	public _source: SourceLocation;
 	abstract text: string;
 	abstract rawText: string;
 	// abstract get rawText(): string;
 	abstract toString(): string;
-	public constructor(public parentNode = null as HTMLElement | null) {
+	public constructor(public parentNode = null as HTMLElement | null, nodeOptions: NodeOptions = {}) {
+		this._source = {
+			start: nodeOptions.start,
+			end: nodeOptions.end,
+		};
 	}
 	public get innerText() {
 		return this.rawText;
diff --git a/node_modules/node-html-parser/src/nodes/text.ts b/node_modules/node-html-parser/src/nodes/text.ts
index 068666a..5051da0 100644
--- a/node_modules/node-html-parser/src/nodes/text.ts
+++ b/node_modules/node-html-parser/src/nodes/text.ts
@@ -1,4 +1,4 @@
-import NodeType from './type';
+import { NodeOptions, NodeType } from './type';
 import Node from './node';
 import HTMLElement from './html';
 
@@ -7,8 +7,8 @@ import HTMLElement from './html';
  * @param {string} value [description]
  */
 export default class TextNode extends Node {
-	public constructor(public rawText: string, parentNode: HTMLElement) {
-		super(parentNode);
+	public constructor(public rawText: string, parentNode: HTMLElement, nodeOptions: NodeOptions = {}) {
+		super(parentNode, nodeOptions);
 	}
 
 	/**
diff --git a/node_modules/node-html-parser/src/nodes/type.ts b/node_modules/node-html-parser/src/nodes/type.ts
index 8863b6c..9101d9d 100644
--- a/node_modules/node-html-parser/src/nodes/type.ts
+++ b/node_modules/node-html-parser/src/nodes/type.ts
@@ -1,7 +1,15 @@
-enum NodeType {
+export enum NodeType {
 	ELEMENT_NODE = 1,
 	TEXT_NODE = 3,
 	COMMENT_NODE = 8
 }
 
-export default NodeType;
+export type SourceLocation = {
+	start: number,
+	end?: number,
+}
+
+export type NodeOptions = {
+	start?: number,
+	end?: number,
+}
