223 lines
7.1 KiB
JavaScript
223 lines
7.1 KiB
JavaScript
import { a as createUnhead } from './shared/unhead.DH45uomy.mjs';
|
|
import { H as HasElementTags } from './shared/unhead.yem5I2v_.mjs';
|
|
import { h as hashTag, i as isMetaArrayDupeKey, a as normalizeProps, d as dedupeKey } from './shared/unhead.BpRRHAhY.mjs';
|
|
import 'hookable';
|
|
import './shared/unhead.DZbvapt-.mjs';
|
|
|
|
async function renderDOMHead(head, options = {}) {
|
|
const dom = options.document || head.resolvedOptions.document;
|
|
if (!dom || !head.dirty)
|
|
return;
|
|
const beforeRenderCtx = { shouldRender: true, tags: [] };
|
|
await head.hooks.callHook("dom:beforeRender", beforeRenderCtx);
|
|
if (!beforeRenderCtx.shouldRender)
|
|
return;
|
|
if (head._domUpdatePromise) {
|
|
return head._domUpdatePromise;
|
|
}
|
|
head._domUpdatePromise = new Promise(async (resolve) => {
|
|
const dupeKeyCounter = /* @__PURE__ */ new Map();
|
|
const resolveTagPromise = new Promise((resolve2) => {
|
|
head.resolveTags().then((tags2) => {
|
|
resolve2(
|
|
tags2.map((tag) => {
|
|
const count = dupeKeyCounter.get(tag._d) || 0;
|
|
const res = {
|
|
tag,
|
|
id: (count ? `${tag._d}:${count}` : tag._d) || hashTag(tag),
|
|
shouldRender: true
|
|
};
|
|
if (tag._d && isMetaArrayDupeKey(tag._d)) {
|
|
dupeKeyCounter.set(tag._d, count + 1);
|
|
}
|
|
return res;
|
|
})
|
|
);
|
|
});
|
|
});
|
|
let state = head._dom;
|
|
if (!state) {
|
|
state = {
|
|
title: dom.title,
|
|
elMap: (/* @__PURE__ */ new Map()).set("htmlAttrs", dom.documentElement).set("bodyAttrs", dom.body)
|
|
};
|
|
for (const key of ["body", "head"]) {
|
|
const children = dom[key]?.children;
|
|
for (const c of children) {
|
|
const tag = c.tagName.toLowerCase();
|
|
if (!HasElementTags.has(tag)) {
|
|
continue;
|
|
}
|
|
const next = normalizeProps({ tag, props: {} }, {
|
|
innerHTML: c.innerHTML,
|
|
...c.getAttributeNames().reduce((props, name) => {
|
|
props[name] = c.getAttribute(name);
|
|
return props;
|
|
}, {}) || {}
|
|
});
|
|
next.key = c.getAttribute("data-hid") || void 0;
|
|
next._d = dedupeKey(next) || hashTag(next);
|
|
if (state.elMap.has(next._d)) {
|
|
let count = 1;
|
|
let k = next._d;
|
|
while (state.elMap.has(k)) {
|
|
k = `${next._d}:${count++}`;
|
|
}
|
|
state.elMap.set(k, c);
|
|
} else {
|
|
state.elMap.set(next._d, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
state.pendingSideEffects = { ...state.sideEffects };
|
|
state.sideEffects = {};
|
|
function track(id, scope, fn) {
|
|
const k = `${id}:${scope}`;
|
|
state.sideEffects[k] = fn;
|
|
delete state.pendingSideEffects[k];
|
|
}
|
|
function trackCtx({ id, $el, tag }) {
|
|
const isAttrTag = tag.tag.endsWith("Attrs");
|
|
state.elMap.set(id, $el);
|
|
if (!isAttrTag) {
|
|
if (tag.textContent && tag.textContent !== $el.textContent) {
|
|
$el.textContent = tag.textContent;
|
|
}
|
|
if (tag.innerHTML && tag.innerHTML !== $el.innerHTML) {
|
|
$el.innerHTML = tag.innerHTML;
|
|
}
|
|
track(id, "el", () => {
|
|
$el?.remove();
|
|
state.elMap.delete(id);
|
|
});
|
|
}
|
|
for (const k in tag.props) {
|
|
if (!Object.prototype.hasOwnProperty.call(tag.props, k))
|
|
continue;
|
|
const value = tag.props[k];
|
|
if (k.startsWith("on") && typeof value === "function") {
|
|
const dataset = $el?.dataset;
|
|
if (dataset && dataset[`${k}fired`]) {
|
|
const ek = k.slice(0, -5);
|
|
value.call($el, new Event(ek.substring(2)));
|
|
}
|
|
if ($el.getAttribute(`data-${k}`) !== "") {
|
|
(tag.tag === "bodyAttrs" ? dom.defaultView : $el).addEventListener(
|
|
// onload -> load
|
|
k.substring(2),
|
|
value.bind($el)
|
|
);
|
|
$el.setAttribute(`data-${k}`, "");
|
|
}
|
|
continue;
|
|
}
|
|
const ck = `attr:${k}`;
|
|
if (k === "class") {
|
|
if (!value) {
|
|
continue;
|
|
}
|
|
for (const c of value) {
|
|
isAttrTag && track(id, `${ck}:${c}`, () => $el.classList.remove(c));
|
|
!$el.classList.contains(c) && $el.classList.add(c);
|
|
}
|
|
} else if (k === "style") {
|
|
if (!value) {
|
|
continue;
|
|
}
|
|
for (const [k2, v] of value) {
|
|
track(id, `${ck}:${k2}`, () => {
|
|
$el.style.removeProperty(k2);
|
|
});
|
|
$el.style.setProperty(k2, v);
|
|
}
|
|
} else if (value !== false && value !== null) {
|
|
$el.getAttribute(k) !== value && $el.setAttribute(k, value === true ? "" : String(value));
|
|
isAttrTag && track(id, ck, () => $el.removeAttribute(k));
|
|
}
|
|
}
|
|
}
|
|
const pending = [];
|
|
const frag = {
|
|
bodyClose: void 0,
|
|
bodyOpen: void 0,
|
|
head: void 0
|
|
};
|
|
const tags = await resolveTagPromise;
|
|
for (const ctx of tags) {
|
|
const { tag, shouldRender, id } = ctx;
|
|
if (!shouldRender)
|
|
continue;
|
|
if (tag.tag === "title") {
|
|
dom.title = tag.textContent;
|
|
track("title", "", () => dom.title = state.title);
|
|
continue;
|
|
}
|
|
ctx.$el = ctx.$el || state.elMap.get(id);
|
|
if (ctx.$el) {
|
|
trackCtx(ctx);
|
|
} else if (HasElementTags.has(tag.tag)) {
|
|
pending.push(ctx);
|
|
}
|
|
}
|
|
for (const ctx of pending) {
|
|
const pos = ctx.tag.tagPosition || "head";
|
|
ctx.$el = dom.createElement(ctx.tag.tag);
|
|
trackCtx(ctx);
|
|
frag[pos] = frag[pos] || dom.createDocumentFragment();
|
|
frag[pos].appendChild(ctx.$el);
|
|
}
|
|
for (const ctx of tags)
|
|
await head.hooks.callHook("dom:renderTag", ctx, dom, track);
|
|
frag.head && dom.head.appendChild(frag.head);
|
|
frag.bodyOpen && dom.body.insertBefore(frag.bodyOpen, dom.body.firstChild);
|
|
frag.bodyClose && dom.body.appendChild(frag.bodyClose);
|
|
for (const k in state.pendingSideEffects) {
|
|
state.pendingSideEffects[k]();
|
|
}
|
|
head._dom = state;
|
|
await head.hooks.callHook("dom:rendered", { renders: tags });
|
|
resolve();
|
|
}).finally(() => {
|
|
head._domUpdatePromise = void 0;
|
|
head.dirty = false;
|
|
});
|
|
return head._domUpdatePromise;
|
|
}
|
|
|
|
function createHead(options = {}) {
|
|
const render = options.domOptions?.render || renderDOMHead;
|
|
options.document = options.document || (typeof window !== "undefined" ? document : void 0);
|
|
const initialPayload = options.document?.head.querySelector('script[id="unhead:payload"]')?.innerHTML || false;
|
|
return createUnhead({
|
|
...options,
|
|
plugins: [
|
|
...options.plugins || [],
|
|
{
|
|
key: "client",
|
|
hooks: {
|
|
"entries:updated": render
|
|
}
|
|
}
|
|
],
|
|
init: [
|
|
initialPayload ? JSON.parse(initialPayload) : false,
|
|
...options.init || []
|
|
]
|
|
});
|
|
}
|
|
|
|
function createDebouncedFn(callee, delayer) {
|
|
let ctxId = 0;
|
|
return () => {
|
|
const delayFnCtxId = ++ctxId;
|
|
delayer(() => {
|
|
if (ctxId === delayFnCtxId) {
|
|
callee();
|
|
}
|
|
});
|
|
};
|
|
}
|
|
|
|
export { createDebouncedFn, createHead, renderDOMHead };
|