@unhead/dom 0.2.7 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +67 -75
- package/dist/index.mjs +67 -75
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -2,106 +2,98 @@
|
|
|
2
2
|
|
|
3
3
|
const TagsWithInnerContent = ["script", "style", "noscript"];
|
|
4
4
|
|
|
5
|
-
const createElement = (tag, document) => {
|
|
6
|
-
const $el = document.createElement(tag.tag);
|
|
7
|
-
for (const k in tag.props)
|
|
8
|
-
$el.setAttribute(k, String(tag.props[k]));
|
|
9
|
-
if (tag.children)
|
|
10
|
-
$el.innerHTML = tag.children;
|
|
11
|
-
return $el;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
function setAttributesWithSideEffects(head, $el, entry, tag) {
|
|
15
|
-
const attrs = tag.props || {};
|
|
16
|
-
const sdeKey = `${tag._p}:attr`;
|
|
17
|
-
const sdeToRun = { ...entry._sde };
|
|
18
|
-
Object.entries(attrs).forEach(([k, value]) => {
|
|
19
|
-
value = String(value);
|
|
20
|
-
const attrSdeKey = `${sdeKey}:${k}`;
|
|
21
|
-
head._removeQueuedSideEffect(attrSdeKey);
|
|
22
|
-
delete sdeToRun[attrSdeKey];
|
|
23
|
-
if (k === "class") {
|
|
24
|
-
for (const c of value.split(" ")) {
|
|
25
|
-
const classSdeKey = `${sdeKey}:class:${c}`;
|
|
26
|
-
if (!$el.classList.contains(c)) {
|
|
27
|
-
$el.classList.add(c);
|
|
28
|
-
entry._sde[classSdeKey] = () => $el.classList.remove(c);
|
|
29
|
-
}
|
|
30
|
-
head._removeQueuedSideEffect(classSdeKey);
|
|
31
|
-
delete sdeToRun[classSdeKey];
|
|
32
|
-
}
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
if ($el.getAttribute(k) !== value) {
|
|
36
|
-
$el.setAttribute(k, value);
|
|
37
|
-
if (!k.startsWith("data-h-"))
|
|
38
|
-
entry._sde[attrSdeKey] = () => $el.removeAttribute(k);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
Object.entries(sdeToRun).filter(([key]) => key.startsWith(sdeKey)).forEach(([key, fn]) => {
|
|
42
|
-
delete entry._sde[key] && fn();
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
5
|
async function renderDOMHead(head, options = {}) {
|
|
47
6
|
const dom = options.document || window.document;
|
|
48
7
|
const tags = await head.resolveTags();
|
|
49
|
-
const
|
|
50
|
-
await head.hooks.callHook("dom:beforeRender",
|
|
51
|
-
if (!
|
|
8
|
+
const ctx = { shouldRender: true, tags };
|
|
9
|
+
await head.hooks.callHook("dom:beforeRender", ctx);
|
|
10
|
+
if (!ctx.shouldRender)
|
|
52
11
|
return;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
12
|
+
const queuedSideEffects = head._popSideEffectQueue();
|
|
13
|
+
head.headEntries().map((entry) => entry._sde).forEach((sde) => {
|
|
14
|
+
Object.entries(sde).forEach(([key, fn]) => {
|
|
15
|
+
queuedSideEffects[key] = fn;
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
const renderTag = (tag, entry) => {
|
|
59
19
|
if (tag.tag === "title" && tag.children) {
|
|
60
20
|
dom.title = tag.children;
|
|
61
|
-
|
|
21
|
+
return dom.head.querySelector("title");
|
|
62
22
|
}
|
|
23
|
+
const markSideEffect = (key, fn) => {
|
|
24
|
+
key = `${tag._s || tag._p}:${key}`;
|
|
25
|
+
entry._sde[key] = fn;
|
|
26
|
+
delete queuedSideEffects[key];
|
|
27
|
+
};
|
|
28
|
+
const setAttrs = ($el) => {
|
|
29
|
+
Object.entries(tag.props).forEach(([k, value]) => {
|
|
30
|
+
value = String(value);
|
|
31
|
+
const attrSdeKey = `attr:${k}`;
|
|
32
|
+
if (k === "class") {
|
|
33
|
+
for (const c of value.split(" ")) {
|
|
34
|
+
const classSdeKey = `${attrSdeKey}:${c}`;
|
|
35
|
+
markSideEffect(classSdeKey, () => $el.classList.remove(c));
|
|
36
|
+
if (!$el.classList.contains(c))
|
|
37
|
+
$el.classList.add(c);
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!k.startsWith("data-h-"))
|
|
42
|
+
markSideEffect(attrSdeKey, () => $el.removeAttribute(k));
|
|
43
|
+
if ($el.getAttribute(k) !== value)
|
|
44
|
+
$el.setAttribute(k, value);
|
|
45
|
+
});
|
|
46
|
+
if (TagsWithInnerContent.includes(tag.tag))
|
|
47
|
+
$el.innerHTML = tag.children || "";
|
|
48
|
+
};
|
|
63
49
|
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
|
|
64
|
-
|
|
65
|
-
|
|
50
|
+
const $el = dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"];
|
|
51
|
+
setAttrs($el);
|
|
52
|
+
return $el;
|
|
66
53
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
let $previousEl
|
|
54
|
+
let $newEl = dom.createElement(tag.tag);
|
|
55
|
+
setAttrs($newEl);
|
|
56
|
+
let $previousEl;
|
|
70
57
|
for (const $el of dom[tag.tagPosition?.startsWith("body") ? "body" : "head"].children) {
|
|
71
|
-
if ($el.hasAttribute(`${tag._s}`)) {
|
|
58
|
+
if ($el.hasAttribute(`${tag._s}`) || $el.isEqualNode($newEl)) {
|
|
72
59
|
$previousEl = $el;
|
|
73
60
|
break;
|
|
74
61
|
}
|
|
75
62
|
}
|
|
63
|
+
if ($newEl.attributes.length === 0 && !$newEl.innerHTML) {
|
|
64
|
+
$previousEl?.remove();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
76
67
|
if ($previousEl) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (Object.keys(tag.props).length === 0) {
|
|
81
|
-
$previousEl.remove();
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
setAttributesWithSideEffects(head, $previousEl, entry, tag);
|
|
85
|
-
if (TagsWithInnerContent.includes(tag.tag))
|
|
86
|
-
$previousEl.innerHTML = tag.children || "";
|
|
87
|
-
entry._sde[sdeKey] = () => $previousEl?.remove();
|
|
88
|
-
continue;
|
|
68
|
+
markSideEffect("el", () => $previousEl?.remove());
|
|
69
|
+
setAttrs($previousEl);
|
|
70
|
+
return $previousEl;
|
|
89
71
|
}
|
|
90
72
|
switch (tag.tagPosition) {
|
|
91
73
|
case "bodyClose":
|
|
92
|
-
dom.body.appendChild($newEl);
|
|
74
|
+
$newEl = dom.body.appendChild($newEl);
|
|
93
75
|
break;
|
|
94
76
|
case "bodyOpen":
|
|
95
|
-
dom.body.insertBefore($newEl, dom.body.firstChild);
|
|
77
|
+
$newEl = dom.body.insertBefore($newEl, dom.body.firstChild);
|
|
96
78
|
break;
|
|
97
79
|
case "head":
|
|
98
80
|
default:
|
|
99
|
-
dom.head.appendChild($newEl);
|
|
81
|
+
$newEl = dom.head.appendChild($newEl);
|
|
100
82
|
break;
|
|
101
83
|
}
|
|
102
|
-
|
|
84
|
+
markSideEffect("el", () => $newEl?.remove());
|
|
85
|
+
return $newEl;
|
|
86
|
+
};
|
|
87
|
+
for (const tag of ctx.tags) {
|
|
88
|
+
const entry = head.headEntries().find((e) => e._i === Number(tag._e));
|
|
89
|
+
const renderCtx = { $el: null, shouldRender: true, tag, entry, queuedSideEffects };
|
|
90
|
+
await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
|
|
91
|
+
if (!renderCtx.shouldRender)
|
|
92
|
+
continue;
|
|
93
|
+
renderCtx.$el = renderTag(renderCtx.tag, renderCtx.entry);
|
|
94
|
+
await head.hooks.callHook("dom:renderTag", renderCtx);
|
|
103
95
|
}
|
|
104
|
-
|
|
96
|
+
Object.values(queuedSideEffects).forEach((fn) => fn());
|
|
105
97
|
}
|
|
106
98
|
exports.domUpdatePromise = null;
|
|
107
99
|
async function debouncedRenderDOMHead(head, options = {}) {
|
|
@@ -109,7 +101,7 @@ async function debouncedRenderDOMHead(head, options = {}) {
|
|
|
109
101
|
exports.domUpdatePromise = null;
|
|
110
102
|
return renderDOMHead(head, options);
|
|
111
103
|
}
|
|
112
|
-
const delayFn = options.delayFn || ((fn) => setTimeout(fn,
|
|
104
|
+
const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
|
|
113
105
|
return exports.domUpdatePromise = exports.domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
|
|
114
106
|
}
|
|
115
107
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,105 +1,97 @@
|
|
|
1
1
|
const TagsWithInnerContent = ["script", "style", "noscript"];
|
|
2
2
|
|
|
3
|
-
const createElement = (tag, document) => {
|
|
4
|
-
const $el = document.createElement(tag.tag);
|
|
5
|
-
for (const k in tag.props)
|
|
6
|
-
$el.setAttribute(k, String(tag.props[k]));
|
|
7
|
-
if (tag.children)
|
|
8
|
-
$el.innerHTML = tag.children;
|
|
9
|
-
return $el;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
function setAttributesWithSideEffects(head, $el, entry, tag) {
|
|
13
|
-
const attrs = tag.props || {};
|
|
14
|
-
const sdeKey = `${tag._p}:attr`;
|
|
15
|
-
const sdeToRun = { ...entry._sde };
|
|
16
|
-
Object.entries(attrs).forEach(([k, value]) => {
|
|
17
|
-
value = String(value);
|
|
18
|
-
const attrSdeKey = `${sdeKey}:${k}`;
|
|
19
|
-
head._removeQueuedSideEffect(attrSdeKey);
|
|
20
|
-
delete sdeToRun[attrSdeKey];
|
|
21
|
-
if (k === "class") {
|
|
22
|
-
for (const c of value.split(" ")) {
|
|
23
|
-
const classSdeKey = `${sdeKey}:class:${c}`;
|
|
24
|
-
if (!$el.classList.contains(c)) {
|
|
25
|
-
$el.classList.add(c);
|
|
26
|
-
entry._sde[classSdeKey] = () => $el.classList.remove(c);
|
|
27
|
-
}
|
|
28
|
-
head._removeQueuedSideEffect(classSdeKey);
|
|
29
|
-
delete sdeToRun[classSdeKey];
|
|
30
|
-
}
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
if ($el.getAttribute(k) !== value) {
|
|
34
|
-
$el.setAttribute(k, value);
|
|
35
|
-
if (!k.startsWith("data-h-"))
|
|
36
|
-
entry._sde[attrSdeKey] = () => $el.removeAttribute(k);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
Object.entries(sdeToRun).filter(([key]) => key.startsWith(sdeKey)).forEach(([key, fn]) => {
|
|
40
|
-
delete entry._sde[key] && fn();
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
3
|
async function renderDOMHead(head, options = {}) {
|
|
45
4
|
const dom = options.document || window.document;
|
|
46
5
|
const tags = await head.resolveTags();
|
|
47
|
-
const
|
|
48
|
-
await head.hooks.callHook("dom:beforeRender",
|
|
49
|
-
if (!
|
|
6
|
+
const ctx = { shouldRender: true, tags };
|
|
7
|
+
await head.hooks.callHook("dom:beforeRender", ctx);
|
|
8
|
+
if (!ctx.shouldRender)
|
|
50
9
|
return;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
10
|
+
const queuedSideEffects = head._popSideEffectQueue();
|
|
11
|
+
head.headEntries().map((entry) => entry._sde).forEach((sde) => {
|
|
12
|
+
Object.entries(sde).forEach(([key, fn]) => {
|
|
13
|
+
queuedSideEffects[key] = fn;
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
const renderTag = (tag, entry) => {
|
|
57
17
|
if (tag.tag === "title" && tag.children) {
|
|
58
18
|
dom.title = tag.children;
|
|
59
|
-
|
|
19
|
+
return dom.head.querySelector("title");
|
|
60
20
|
}
|
|
21
|
+
const markSideEffect = (key, fn) => {
|
|
22
|
+
key = `${tag._s || tag._p}:${key}`;
|
|
23
|
+
entry._sde[key] = fn;
|
|
24
|
+
delete queuedSideEffects[key];
|
|
25
|
+
};
|
|
26
|
+
const setAttrs = ($el) => {
|
|
27
|
+
Object.entries(tag.props).forEach(([k, value]) => {
|
|
28
|
+
value = String(value);
|
|
29
|
+
const attrSdeKey = `attr:${k}`;
|
|
30
|
+
if (k === "class") {
|
|
31
|
+
for (const c of value.split(" ")) {
|
|
32
|
+
const classSdeKey = `${attrSdeKey}:${c}`;
|
|
33
|
+
markSideEffect(classSdeKey, () => $el.classList.remove(c));
|
|
34
|
+
if (!$el.classList.contains(c))
|
|
35
|
+
$el.classList.add(c);
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!k.startsWith("data-h-"))
|
|
40
|
+
markSideEffect(attrSdeKey, () => $el.removeAttribute(k));
|
|
41
|
+
if ($el.getAttribute(k) !== value)
|
|
42
|
+
$el.setAttribute(k, value);
|
|
43
|
+
});
|
|
44
|
+
if (TagsWithInnerContent.includes(tag.tag))
|
|
45
|
+
$el.innerHTML = tag.children || "";
|
|
46
|
+
};
|
|
61
47
|
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
|
|
62
|
-
|
|
63
|
-
|
|
48
|
+
const $el = dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"];
|
|
49
|
+
setAttrs($el);
|
|
50
|
+
return $el;
|
|
64
51
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
let $previousEl
|
|
52
|
+
let $newEl = dom.createElement(tag.tag);
|
|
53
|
+
setAttrs($newEl);
|
|
54
|
+
let $previousEl;
|
|
68
55
|
for (const $el of dom[tag.tagPosition?.startsWith("body") ? "body" : "head"].children) {
|
|
69
|
-
if ($el.hasAttribute(`${tag._s}`)) {
|
|
56
|
+
if ($el.hasAttribute(`${tag._s}`) || $el.isEqualNode($newEl)) {
|
|
70
57
|
$previousEl = $el;
|
|
71
58
|
break;
|
|
72
59
|
}
|
|
73
60
|
}
|
|
61
|
+
if ($newEl.attributes.length === 0 && !$newEl.innerHTML) {
|
|
62
|
+
$previousEl?.remove();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
74
65
|
if ($previousEl) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (Object.keys(tag.props).length === 0) {
|
|
79
|
-
$previousEl.remove();
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
setAttributesWithSideEffects(head, $previousEl, entry, tag);
|
|
83
|
-
if (TagsWithInnerContent.includes(tag.tag))
|
|
84
|
-
$previousEl.innerHTML = tag.children || "";
|
|
85
|
-
entry._sde[sdeKey] = () => $previousEl?.remove();
|
|
86
|
-
continue;
|
|
66
|
+
markSideEffect("el", () => $previousEl?.remove());
|
|
67
|
+
setAttrs($previousEl);
|
|
68
|
+
return $previousEl;
|
|
87
69
|
}
|
|
88
70
|
switch (tag.tagPosition) {
|
|
89
71
|
case "bodyClose":
|
|
90
|
-
dom.body.appendChild($newEl);
|
|
72
|
+
$newEl = dom.body.appendChild($newEl);
|
|
91
73
|
break;
|
|
92
74
|
case "bodyOpen":
|
|
93
|
-
dom.body.insertBefore($newEl, dom.body.firstChild);
|
|
75
|
+
$newEl = dom.body.insertBefore($newEl, dom.body.firstChild);
|
|
94
76
|
break;
|
|
95
77
|
case "head":
|
|
96
78
|
default:
|
|
97
|
-
dom.head.appendChild($newEl);
|
|
79
|
+
$newEl = dom.head.appendChild($newEl);
|
|
98
80
|
break;
|
|
99
81
|
}
|
|
100
|
-
|
|
82
|
+
markSideEffect("el", () => $newEl?.remove());
|
|
83
|
+
return $newEl;
|
|
84
|
+
};
|
|
85
|
+
for (const tag of ctx.tags) {
|
|
86
|
+
const entry = head.headEntries().find((e) => e._i === Number(tag._e));
|
|
87
|
+
const renderCtx = { $el: null, shouldRender: true, tag, entry, queuedSideEffects };
|
|
88
|
+
await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
|
|
89
|
+
if (!renderCtx.shouldRender)
|
|
90
|
+
continue;
|
|
91
|
+
renderCtx.$el = renderTag(renderCtx.tag, renderCtx.entry);
|
|
92
|
+
await head.hooks.callHook("dom:renderTag", renderCtx);
|
|
101
93
|
}
|
|
102
|
-
|
|
94
|
+
Object.values(queuedSideEffects).forEach((fn) => fn());
|
|
103
95
|
}
|
|
104
96
|
let domUpdatePromise = null;
|
|
105
97
|
async function debouncedRenderDOMHead(head, options = {}) {
|
|
@@ -107,7 +99,7 @@ async function debouncedRenderDOMHead(head, options = {}) {
|
|
|
107
99
|
domUpdatePromise = null;
|
|
108
100
|
return renderDOMHead(head, options);
|
|
109
101
|
}
|
|
110
|
-
const delayFn = options.delayFn || ((fn) => setTimeout(fn,
|
|
102
|
+
const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
|
|
111
103
|
return domUpdatePromise = domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
|
|
112
104
|
}
|
|
113
105
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unhead/dom",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"packageManager": "pnpm@7.14.0",
|
|
6
6
|
"author": "Harlan Wilton <harlan@harlanzw.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@unhead/schema": "0.
|
|
33
|
+
"@unhead/schema": "0.4.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"zhead": "1.0.0-beta.
|
|
36
|
+
"zhead": "1.0.0-beta.11"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "unbuild .",
|