UNPKG

uhtml

Version:
82 lines (76 loc) • 2.58 kB
'use strict'; const { DOCUMENT_FRAGMENT_NODE } = require('domconstants/constants'); const custom = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require('custom-function/factory')); const drop = (require('./range.js')); const { empty } = require('./utils.js'); /** * @param {PersistentFragment} fragment * @returns {Node | Element} */ const remove = ({firstChild, lastChild}, preserve) => drop(firstChild, lastChild, preserve); let checkType = false; /** * @param {Node} node * @param {1 | 0 | -0 | -1} operation * @returns {Node} */ const diffFragment = (node, operation) => ( checkType && node.nodeType === DOCUMENT_FRAGMENT_NODE ? ((1 / operation) < 0 ? (operation ? remove(node, true) : node.lastChild) : (operation ? node.valueOf() : node.firstChild)) : node ); exports.diffFragment = diffFragment; const comment = value => document.createComment(value); /** @extends {DocumentFragment} */ class PersistentFragment extends custom(DocumentFragment) { #firstChild = comment('<>'); #lastChild = comment('</>'); #nodes = empty; constructor(fragment) { super(fragment); this.replaceChildren(...[ this.#firstChild, ...fragment.childNodes, this.#lastChild, ]); checkType = true; } get firstChild() { return this.#firstChild; } get lastChild() { return this.#lastChild; } get parentNode() { return this.#firstChild.parentNode; } remove() { remove(this, false); } replaceWith(node) { remove(this, true).replaceWith(node); } valueOf() { const { parentNode } = this; if (parentNode === this) { if (this.#nodes === empty) this.#nodes = [...this.childNodes]; } else { /* c8 ignore start */ // there are cases where a fragment might be just appended // out of the box without valueOf() invoke (first render). // When these are moved around and lose their parent and, // such parent is not the fragment itself, it's possible there // where changes or mutations in there to take care about. // This is a render-only specific issue but it's tested and // it's worth fixing to me to have more consistent fragments. if (parentNode) { let { firstChild, lastChild } = this; this.#nodes = [firstChild]; while (firstChild !== lastChild) this.#nodes.push((firstChild = firstChild.nextSibling)); } /* c8 ignore stop */ this.replaceChildren(...this.#nodes); } return this; } } exports.PersistentFragment = PersistentFragment