UNPKG

microsite

Version:
166 lines (165 loc) 5.6 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { h, hydrate as rehydrate, render } from "preact"; const win = window; if (!("requestIdleCallback" in window)) { win.requestIdleCallback = function (cb) { return setTimeout(function () { var start = Date.now(); cb({ didTimeout: false, timeRemaining: function () { return Math.max(0, 50 - (Date.now() - start)); }, }); }, 1); }; win.cancelIdleCallback = function (id) { clearTimeout(id); }; } const createObserver = (hydrate) => { if (!("IntersectionObserver" in window)) return null; const io = new IntersectionObserver((entries) => { entries.forEach((entry) => { const isIntersecting = entry.isIntersecting || entry.intersectionRatio > 0; if (!isIntersecting) return; hydrate(); io.disconnect(); }); }); return io; }; function attach(fragment, data, { name, source }, cb) { const _a = data.p, _b = _a === void 0 ? {} : _a, { children = null } = _b, props = __rest(_b, ["children"]), { m: method = "idle", f: flush } = data; const hydrate = async () => { if (win.__MICROSITE_DEBUG) console.log(`[Hydrate] <${name} /> hydrated via "${method}"`); const { [name]: Component } = await import(source); if (flush) { render(h(Component, props, children), fragment); } else { rehydrate(h(Component, props, children), fragment); } if (cb) cb(); }; switch (method) { case "idle": { if (!("requestAnimationFrame" in window)) return setTimeout(hydrate, 0); win.requestIdleCallback(() => { requestAnimationFrame(hydrate); }, { timeout: 500 }); break; } case "visible": { if (!("IntersectionObserver" in window)) return hydrate(); const observer = createObserver(hydrate); const childElements = fragment.childNodes.filter((node) => node.nodeType === node.ELEMENT_NODE); for (const child of childElements) { observer.observe(child); } break; } } } function createPersistentFragment(parentNode, childNodes) { const last = childNodes && childNodes[childNodes.length - 1].nextSibling; function insert(child, before) { try { parentNode.insertBefore(child, before || last); } catch (e) { } } return { parentNode, firstChild: childNodes[0], childNodes, appendChild: insert, insertBefore: insert, removeChild(child) { parentNode.removeChild(child); }, }; } const ATTR_REGEX = /(:?\w+)=([^\s]*)/g; function parseHydrateBoundary(node) { if (!node.textContent) return {}; const text = node.textContent.slice("?h ".length, -1); let props = {}; let result = ATTR_REGEX.exec(text); while (result) { let [, attr, val] = result; if (attr === "p") { props[attr] = JSON.parse(atob(val)); } else { props[attr] = val; } result = ATTR_REGEX.exec(text); } return props; } function findHydrationPoints() { const nodeIterator = document.createNodeIterator(document.documentElement, NodeFilter.SHOW_COMMENT, { acceptNode(node) { if (node.textContent && node.textContent.startsWith("?h c")) return NodeFilter.FILTER_ACCEPT; return NodeFilter.FILTER_REJECT; }, }); const toHydrate = []; while (nodeIterator.nextNode()) { const start = nodeIterator.referenceNode; const data = parseHydrateBoundary(start); const childNodes = []; let end = start.nextSibling; while (end) { if (end.nodeType === end.COMMENT_NODE && end.textContent && end.textContent.startsWith("?h p")) { Object.assign(data, parseHydrateBoundary(end)); break; } childNodes.push(end); end = end.nextSibling; } toHydrate.push([ createPersistentFragment(start.parentNode, childNodes), data, [start, end], ]); } return toHydrate; } export default (manifest) => { const init = () => { const $cmps = findHydrationPoints(); for (const [fragment, data, markers] of $cmps) { const { c: key } = data; const [name, source] = manifest[key]; if (name && source) { attach(fragment, data, { name, source }, () => { fragment.childNodes.forEach((child) => child.tagName === "HYDRATE-PLACEHOLDER" ? child.remove() : null); markers.forEach((marker) => marker.remove()); }); } } }; win.requestIdleCallback(init, { timeout: 1000 }); };