Source: chunker/parser.js

import { UUID } from "../utils/utils.js";

/**
 * Parses and processes a flow of content (HTML or DOM nodes) offscreen.
 * Adds unique references to elements for later retrieval or tracking.
 *
 * @class
 */
class ContentParser {
	/**
	 * Create a new ContentParser instance.
	 *
	 * @param {string | Node} content - HTML string or DOM Node to be parsed.
	 * @param {Function} [cb] - Optional callback (currently unused).
	 * @returns {DocumentFragment | Node} The parsed DOM fragment or node.
	 */
	constructor(content, cb) {
		if (content && content.nodeType) {
			// Handle DOM Node input
			this.dom = this.add(content);
		} else if (typeof content === "string") {
			// Handle HTML string input
			this.dom = this.parse(content);
		}

		return this.dom;
	}

	/**
	 * Parses an HTML string into a DocumentFragment and adds `data-ref` attributes.
	 *
	 * @param {string} markup - The HTML markup to parse.
	 * @param {string} [mime] - Optional MIME type (currently unused).
	 * @returns {DocumentFragment} A document fragment with processed nodes.
	 */
	parse(markup, mime) {
		let range = document.createRange();
		let fragment = range.createContextualFragment(markup);

		this.addRefs(fragment);

		return fragment;
	}

	/**
	 * Processes a DOM Node by cloning its structure (if needed) and adding `data-ref` attributes.
	 *
	 * @param {Node} contents - A DOM Node or DocumentFragment to process.
	 * @returns {Node} The processed content with references.
	 */
	add(contents) {
		// let fragment = document.createDocumentFragment();
		//
		// let children = [...contents.childNodes];
		// for (let child of children) {
		// 	let clone = child.cloneNode(true);
		// 	fragment.appendChild(clone);
		// }
		this.addRefs(contents);

		return contents;
	}

	/**
	 * Walks the content tree and adds a `data-ref` attribute (UUID) to each element.
	 * Also preserves original `id` attributes via `data-id`.
	 *
	 * @param {Node} content - A DOM Node or DocumentFragment to annotate.
	 */
	addRefs(content) {
		const treeWalker = document.createTreeWalker(
			content,
			NodeFilter.SHOW_ELEMENT,
			null,
			false,
		);

		let node = treeWalker.nextNode();
		while (node) {
			if (!node.hasAttribute("data-ref")) {
				const uuid = UUID();
				node.setAttribute("data-ref", uuid);
			}

			if (node.id) {
				node.setAttribute("data-id", node.id);
			}

			node = treeWalker.nextNode();
		}
	}

	/**
	 * Finds a DOM node by its reference ID (this.refs must be pre-populated externally).
	 *
	 * @param {string} ref - The `data-ref` UUID to search for.
	 * @returns {HTMLElement|undefined} The associated element, if found.
	 */
	find(ref) {
		return this.refs?.[ref];
	}

	/**
	 * Cleans up the parser's references and DOM structure.
	 */
	destroy() {
		this.refs = undefined;
		this.dom = undefined;
	}
}

export default ContentParser;