initial commit
This commit is contained in:
12
node_modules/@unhead/dom/README.md
generated
vendored
Normal file
12
node_modules/@unhead/dom/README.md
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# @unhead/dom
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
# yarn add @unhead/dom
|
||||
npm install @unhead/dom
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
See the [@unhead/dom](https://unhead.harlanzw.com/guide/getting-started/how-it-works#unheaddom) for how this works.
|
||||
199
node_modules/@unhead/dom/dist/index.cjs
generated
vendored
Normal file
199
node_modules/@unhead/dom/dist/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
'use strict';
|
||||
|
||||
const TagsWithInnerContent = ["script", "style", "noscript"];
|
||||
const HasElementTags = [
|
||||
"base",
|
||||
"meta",
|
||||
"link",
|
||||
"style",
|
||||
"script",
|
||||
"noscript"
|
||||
];
|
||||
|
||||
const UniqueTags = ["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"];
|
||||
function tagDedupeKey(tag, fn) {
|
||||
const { props, tag: tagName } = tag;
|
||||
if (UniqueTags.includes(tagName))
|
||||
return tagName;
|
||||
if (tagName === "link" && props.rel === "canonical")
|
||||
return "canonical";
|
||||
if (props.charset)
|
||||
return "charset";
|
||||
const name = ["id"];
|
||||
if (tagName === "meta")
|
||||
name.push(...["name", "property", "http-equiv"]);
|
||||
for (const n of name) {
|
||||
if (typeof props[n] !== "undefined") {
|
||||
const val = String(props[n]);
|
||||
if (fn && !fn(val))
|
||||
return false;
|
||||
return `${tagName}:${n}:${val}`;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const setAttrs = (ctx, markSideEffect) => {
|
||||
const { tag, $el } = ctx;
|
||||
if (!$el)
|
||||
return;
|
||||
Object.entries(tag.props).forEach(([k, value]) => {
|
||||
value = String(value);
|
||||
const attrSdeKey = `attr:${k}`;
|
||||
if (k === "class") {
|
||||
if (!value)
|
||||
return;
|
||||
for (const c of value.split(" ")) {
|
||||
const classSdeKey = `${attrSdeKey}:${c}`;
|
||||
if (markSideEffect)
|
||||
markSideEffect(ctx, classSdeKey, () => $el.classList.remove(c));
|
||||
if (!$el.classList.contains(c))
|
||||
$el.classList.add(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (markSideEffect && !k.startsWith("data-h-"))
|
||||
markSideEffect(ctx, attrSdeKey, () => $el.removeAttribute(k));
|
||||
if ($el.getAttribute(k) !== value)
|
||||
$el.setAttribute(k, value);
|
||||
});
|
||||
if (TagsWithInnerContent.includes(tag.tag) && $el.innerHTML !== (tag.children || ""))
|
||||
$el.innerHTML = tag.children || "";
|
||||
};
|
||||
|
||||
function hashCode(s) {
|
||||
let h = 9;
|
||||
for (let i = 0; i < s.length; )
|
||||
h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
|
||||
return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 8).toLowerCase();
|
||||
}
|
||||
|
||||
async function renderDOMHead(head, options = {}) {
|
||||
const ctx = { shouldRender: true };
|
||||
await head.hooks.callHook("dom:beforeRender", ctx);
|
||||
if (!ctx.shouldRender)
|
||||
return;
|
||||
const dom = options.document || window.document;
|
||||
const staleSideEffects = head._popSideEffectQueue();
|
||||
head.headEntries().map((entry) => entry._sde).forEach((sde) => {
|
||||
Object.entries(sde).forEach(([key, fn]) => {
|
||||
staleSideEffects[key] = fn;
|
||||
});
|
||||
});
|
||||
const preRenderTag = async (tag) => {
|
||||
const entry = head.headEntries().find((e) => e._i === tag._e);
|
||||
const renderCtx = {
|
||||
renderId: tag._d || hashCode(JSON.stringify({ ...tag, _e: void 0, _p: void 0 })),
|
||||
$el: null,
|
||||
shouldRender: true,
|
||||
tag,
|
||||
entry,
|
||||
staleSideEffects
|
||||
};
|
||||
await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
|
||||
return renderCtx;
|
||||
};
|
||||
const renders = [];
|
||||
const pendingRenders = {
|
||||
body: [],
|
||||
head: []
|
||||
};
|
||||
const markSideEffect = (ctx2, key, fn) => {
|
||||
key = `${ctx2.renderId}:${key}`;
|
||||
if (ctx2.entry)
|
||||
ctx2.entry._sde[key] = fn;
|
||||
delete staleSideEffects[key];
|
||||
};
|
||||
const markEl = (ctx2) => {
|
||||
head._elMap[ctx2.renderId] = ctx2.$el;
|
||||
renders.push(ctx2);
|
||||
markSideEffect(ctx2, "el", () => {
|
||||
ctx2.$el?.remove();
|
||||
delete head._elMap[ctx2.renderId];
|
||||
});
|
||||
};
|
||||
for (const t of await head.resolveTags()) {
|
||||
const ctx2 = await preRenderTag(t);
|
||||
if (!ctx2.shouldRender)
|
||||
continue;
|
||||
const { tag } = ctx2;
|
||||
if (tag.tag === "title") {
|
||||
dom.title = tag.children || "";
|
||||
renders.push(ctx2);
|
||||
continue;
|
||||
}
|
||||
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
|
||||
ctx2.$el = dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"];
|
||||
setAttrs(ctx2, markSideEffect);
|
||||
renders.push(ctx2);
|
||||
continue;
|
||||
}
|
||||
ctx2.$el = head._elMap[ctx2.renderId];
|
||||
if (!ctx2.$el && tag._hash) {
|
||||
ctx2.$el = dom.querySelector(`${tag.tagPosition?.startsWith("body") ? "body" : "head"} > ${tag.tag}[data-h-${tag._hash}]`);
|
||||
}
|
||||
if (ctx2.$el) {
|
||||
if (ctx2.tag._d)
|
||||
setAttrs(ctx2);
|
||||
markEl(ctx2);
|
||||
continue;
|
||||
}
|
||||
ctx2.$el = dom.createElement(tag.tag);
|
||||
setAttrs(ctx2);
|
||||
pendingRenders[tag.tagPosition?.startsWith("body") ? "body" : "head"].push(ctx2);
|
||||
}
|
||||
Object.entries(pendingRenders).forEach(([pos, queue]) => {
|
||||
if (!queue.length)
|
||||
return;
|
||||
for (const $el of [...dom[pos].children].reverse()) {
|
||||
const elTag = $el.tagName.toLowerCase();
|
||||
if (!HasElementTags.includes(elTag))
|
||||
continue;
|
||||
const dedupeKey = tagDedupeKey({
|
||||
tag: elTag,
|
||||
props: $el.getAttributeNames().reduce((props, name) => ({ ...props, [name]: $el.getAttribute(name) }), {})
|
||||
});
|
||||
const matchIdx = queue.findIndex((ctx2) => ctx2 && (ctx2.tag._d === dedupeKey || $el.isEqualNode(ctx2.$el)));
|
||||
if (matchIdx !== -1) {
|
||||
const ctx2 = queue[matchIdx];
|
||||
ctx2.$el = $el;
|
||||
setAttrs(ctx2);
|
||||
markEl(ctx2);
|
||||
delete queue[matchIdx];
|
||||
}
|
||||
}
|
||||
queue.forEach((ctx2) => {
|
||||
if (!ctx2.$el)
|
||||
return;
|
||||
switch (ctx2.tag.tagPosition) {
|
||||
case "bodyClose":
|
||||
dom.body.appendChild(ctx2.$el);
|
||||
break;
|
||||
case "bodyOpen":
|
||||
dom.body.insertBefore(ctx2.$el, dom.body.firstChild);
|
||||
break;
|
||||
case "head":
|
||||
default:
|
||||
dom.head.appendChild(ctx2.$el);
|
||||
break;
|
||||
}
|
||||
markEl(ctx2);
|
||||
});
|
||||
});
|
||||
for (const ctx2 of renders)
|
||||
await head.hooks.callHook("dom:renderTag", ctx2);
|
||||
Object.values(staleSideEffects).forEach((fn) => fn());
|
||||
}
|
||||
exports.domUpdatePromise = null;
|
||||
async function debouncedRenderDOMHead(head, options = {}) {
|
||||
function doDomUpdate() {
|
||||
exports.domUpdatePromise = null;
|
||||
return renderDOMHead(head, options);
|
||||
}
|
||||
const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
|
||||
return exports.domUpdatePromise = exports.domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
|
||||
}
|
||||
|
||||
exports.debouncedRenderDOMHead = debouncedRenderDOMHead;
|
||||
exports.hashCode = hashCode;
|
||||
exports.renderDOMHead = renderDOMHead;
|
||||
26
node_modules/@unhead/dom/dist/index.d.ts
generated
vendored
Normal file
26
node_modules/@unhead/dom/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Unhead } from '@unhead/schema';
|
||||
|
||||
interface RenderDomHeadOptions {
|
||||
/**
|
||||
* Document to use for rendering. Allows stubbing for testing.
|
||||
*/
|
||||
document?: Document;
|
||||
}
|
||||
/**
|
||||
* Render the head tags to the DOM.
|
||||
*/
|
||||
declare function renderDOMHead<T extends Unhead<any>>(head: T, options?: RenderDomHeadOptions): Promise<void>;
|
||||
/**
|
||||
* Global instance of the dom update promise. Used for debounding head updates.
|
||||
*/
|
||||
declare let domUpdatePromise: Promise<void> | null;
|
||||
/**
|
||||
* Queue a debounced update of the DOM head.
|
||||
*/
|
||||
declare function debouncedRenderDOMHead<T extends Unhead<any>>(head: T, options?: RenderDomHeadOptions & {
|
||||
delayFn?: (fn: () => void) => void;
|
||||
}): Promise<void>;
|
||||
|
||||
declare function hashCode(s: string): string;
|
||||
|
||||
export { RenderDomHeadOptions, debouncedRenderDOMHead, domUpdatePromise, hashCode, renderDOMHead };
|
||||
195
node_modules/@unhead/dom/dist/index.mjs
generated
vendored
Normal file
195
node_modules/@unhead/dom/dist/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
const TagsWithInnerContent = ["script", "style", "noscript"];
|
||||
const HasElementTags = [
|
||||
"base",
|
||||
"meta",
|
||||
"link",
|
||||
"style",
|
||||
"script",
|
||||
"noscript"
|
||||
];
|
||||
|
||||
const UniqueTags = ["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"];
|
||||
function tagDedupeKey(tag, fn) {
|
||||
const { props, tag: tagName } = tag;
|
||||
if (UniqueTags.includes(tagName))
|
||||
return tagName;
|
||||
if (tagName === "link" && props.rel === "canonical")
|
||||
return "canonical";
|
||||
if (props.charset)
|
||||
return "charset";
|
||||
const name = ["id"];
|
||||
if (tagName === "meta")
|
||||
name.push(...["name", "property", "http-equiv"]);
|
||||
for (const n of name) {
|
||||
if (typeof props[n] !== "undefined") {
|
||||
const val = String(props[n]);
|
||||
if (fn && !fn(val))
|
||||
return false;
|
||||
return `${tagName}:${n}:${val}`;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const setAttrs = (ctx, markSideEffect) => {
|
||||
const { tag, $el } = ctx;
|
||||
if (!$el)
|
||||
return;
|
||||
Object.entries(tag.props).forEach(([k, value]) => {
|
||||
value = String(value);
|
||||
const attrSdeKey = `attr:${k}`;
|
||||
if (k === "class") {
|
||||
if (!value)
|
||||
return;
|
||||
for (const c of value.split(" ")) {
|
||||
const classSdeKey = `${attrSdeKey}:${c}`;
|
||||
if (markSideEffect)
|
||||
markSideEffect(ctx, classSdeKey, () => $el.classList.remove(c));
|
||||
if (!$el.classList.contains(c))
|
||||
$el.classList.add(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (markSideEffect && !k.startsWith("data-h-"))
|
||||
markSideEffect(ctx, attrSdeKey, () => $el.removeAttribute(k));
|
||||
if ($el.getAttribute(k) !== value)
|
||||
$el.setAttribute(k, value);
|
||||
});
|
||||
if (TagsWithInnerContent.includes(tag.tag) && $el.innerHTML !== (tag.children || ""))
|
||||
$el.innerHTML = tag.children || "";
|
||||
};
|
||||
|
||||
function hashCode(s) {
|
||||
let h = 9;
|
||||
for (let i = 0; i < s.length; )
|
||||
h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
|
||||
return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 8).toLowerCase();
|
||||
}
|
||||
|
||||
async function renderDOMHead(head, options = {}) {
|
||||
const ctx = { shouldRender: true };
|
||||
await head.hooks.callHook("dom:beforeRender", ctx);
|
||||
if (!ctx.shouldRender)
|
||||
return;
|
||||
const dom = options.document || window.document;
|
||||
const staleSideEffects = head._popSideEffectQueue();
|
||||
head.headEntries().map((entry) => entry._sde).forEach((sde) => {
|
||||
Object.entries(sde).forEach(([key, fn]) => {
|
||||
staleSideEffects[key] = fn;
|
||||
});
|
||||
});
|
||||
const preRenderTag = async (tag) => {
|
||||
const entry = head.headEntries().find((e) => e._i === tag._e);
|
||||
const renderCtx = {
|
||||
renderId: tag._d || hashCode(JSON.stringify({ ...tag, _e: void 0, _p: void 0 })),
|
||||
$el: null,
|
||||
shouldRender: true,
|
||||
tag,
|
||||
entry,
|
||||
staleSideEffects
|
||||
};
|
||||
await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
|
||||
return renderCtx;
|
||||
};
|
||||
const renders = [];
|
||||
const pendingRenders = {
|
||||
body: [],
|
||||
head: []
|
||||
};
|
||||
const markSideEffect = (ctx2, key, fn) => {
|
||||
key = `${ctx2.renderId}:${key}`;
|
||||
if (ctx2.entry)
|
||||
ctx2.entry._sde[key] = fn;
|
||||
delete staleSideEffects[key];
|
||||
};
|
||||
const markEl = (ctx2) => {
|
||||
head._elMap[ctx2.renderId] = ctx2.$el;
|
||||
renders.push(ctx2);
|
||||
markSideEffect(ctx2, "el", () => {
|
||||
ctx2.$el?.remove();
|
||||
delete head._elMap[ctx2.renderId];
|
||||
});
|
||||
};
|
||||
for (const t of await head.resolveTags()) {
|
||||
const ctx2 = await preRenderTag(t);
|
||||
if (!ctx2.shouldRender)
|
||||
continue;
|
||||
const { tag } = ctx2;
|
||||
if (tag.tag === "title") {
|
||||
dom.title = tag.children || "";
|
||||
renders.push(ctx2);
|
||||
continue;
|
||||
}
|
||||
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
|
||||
ctx2.$el = dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"];
|
||||
setAttrs(ctx2, markSideEffect);
|
||||
renders.push(ctx2);
|
||||
continue;
|
||||
}
|
||||
ctx2.$el = head._elMap[ctx2.renderId];
|
||||
if (!ctx2.$el && tag._hash) {
|
||||
ctx2.$el = dom.querySelector(`${tag.tagPosition?.startsWith("body") ? "body" : "head"} > ${tag.tag}[data-h-${tag._hash}]`);
|
||||
}
|
||||
if (ctx2.$el) {
|
||||
if (ctx2.tag._d)
|
||||
setAttrs(ctx2);
|
||||
markEl(ctx2);
|
||||
continue;
|
||||
}
|
||||
ctx2.$el = dom.createElement(tag.tag);
|
||||
setAttrs(ctx2);
|
||||
pendingRenders[tag.tagPosition?.startsWith("body") ? "body" : "head"].push(ctx2);
|
||||
}
|
||||
Object.entries(pendingRenders).forEach(([pos, queue]) => {
|
||||
if (!queue.length)
|
||||
return;
|
||||
for (const $el of [...dom[pos].children].reverse()) {
|
||||
const elTag = $el.tagName.toLowerCase();
|
||||
if (!HasElementTags.includes(elTag))
|
||||
continue;
|
||||
const dedupeKey = tagDedupeKey({
|
||||
tag: elTag,
|
||||
props: $el.getAttributeNames().reduce((props, name) => ({ ...props, [name]: $el.getAttribute(name) }), {})
|
||||
});
|
||||
const matchIdx = queue.findIndex((ctx2) => ctx2 && (ctx2.tag._d === dedupeKey || $el.isEqualNode(ctx2.$el)));
|
||||
if (matchIdx !== -1) {
|
||||
const ctx2 = queue[matchIdx];
|
||||
ctx2.$el = $el;
|
||||
setAttrs(ctx2);
|
||||
markEl(ctx2);
|
||||
delete queue[matchIdx];
|
||||
}
|
||||
}
|
||||
queue.forEach((ctx2) => {
|
||||
if (!ctx2.$el)
|
||||
return;
|
||||
switch (ctx2.tag.tagPosition) {
|
||||
case "bodyClose":
|
||||
dom.body.appendChild(ctx2.$el);
|
||||
break;
|
||||
case "bodyOpen":
|
||||
dom.body.insertBefore(ctx2.$el, dom.body.firstChild);
|
||||
break;
|
||||
case "head":
|
||||
default:
|
||||
dom.head.appendChild(ctx2.$el);
|
||||
break;
|
||||
}
|
||||
markEl(ctx2);
|
||||
});
|
||||
});
|
||||
for (const ctx2 of renders)
|
||||
await head.hooks.callHook("dom:renderTag", ctx2);
|
||||
Object.values(staleSideEffects).forEach((fn) => fn());
|
||||
}
|
||||
let domUpdatePromise = null;
|
||||
async function debouncedRenderDOMHead(head, options = {}) {
|
||||
function doDomUpdate() {
|
||||
domUpdatePromise = null;
|
||||
return renderDOMHead(head, options);
|
||||
}
|
||||
const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
|
||||
return domUpdatePromise = domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
|
||||
}
|
||||
|
||||
export { debouncedRenderDOMHead, domUpdatePromise, hashCode, renderDOMHead };
|
||||
43
node_modules/@unhead/dom/package.json
generated
vendored
Normal file
43
node_modules/@unhead/dom/package.json
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@unhead/dom",
|
||||
"type": "module",
|
||||
"version": "1.0.14",
|
||||
"packageManager": "pnpm@7.19.0",
|
||||
"author": "Harlan Wilton <harlan@harlanzw.com>",
|
||||
"license": "MIT",
|
||||
"funding": "https://github.com/sponsors/harlan-zw",
|
||||
"homepage": "https://github.com/harlan-zw/unhead#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/harlan-zw/unhead.git",
|
||||
"directory": "packages/dom"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/harlan-zw/unhead/issues"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"require": "./dist/index.cjs",
|
||||
"import": "./dist/index.mjs"
|
||||
}
|
||||
},
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@unhead/schema": "1.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"zhead": "^1.0.9"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "unbuild .",
|
||||
"stub": "unbuild . --stub",
|
||||
"export:sizes": "npx export-size . -r"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user