ripple
Version:
Ripple is an elegant TypeScript UI framework
60 lines (50 loc) • 1.85 kB
JavaScript
import { render } from './blocks.js';
import { HEAD_BLOCK } from './constants.js';
import { COMMENT_NODE } from '../../../constants.js';
import { create_text, get_first_child, get_next_sibling } from './operations.js';
import { hydrate_node, hydrating, set_hydrate_node, set_hydrating } from './hydration.js';
/**
* @param {string} hash
* @param {(anchor: Node) => void} render_fn
* @returns {void}
*/
export function head(hash, render_fn) {
// The head function may be called after the first hydration pass and ssr comment nodes may still be present,
// therefore we need to skip that when we detect that we're not in hydration mode.
let previous_hydrate_node = null;
let was_hydrating = hydrating;
/** @type {Comment | Text} */
var anchor;
if (hydrating) {
previous_hydrate_node = hydrate_node;
var head_anchor = get_first_child(document.head);
// There might be multiple head blocks in our app, and they could have been
// rendered in an arbitrary order — find one corresponding to this component
while (
head_anchor !== null &&
(head_anchor.nodeType !== COMMENT_NODE || /** @type {Comment} */ (head_anchor).data !== hash)
) {
head_anchor = get_next_sibling(head_anchor);
}
// If we can't find an opening hydration marker, skip hydration (this can happen
// if a framework rendered body but not head content)
if (head_anchor === null) {
set_hydrating(false);
} else {
var start = get_next_sibling(head_anchor);
/** @type {ChildNode} */ (head_anchor).remove(); // in case this component is repeated
set_hydrate_node(start);
}
}
if (!hydrating) {
anchor = document.head.appendChild(create_text());
}
try {
render(() => render_fn(anchor), null, HEAD_BLOCK);
} finally {
if (was_hydrating) {
set_hydrating(true);
set_hydrate_node(previous_hydrate_node);
}
}
}