@unhead/dom 0.2.6 → 0.3.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 +59 -61
- package/dist/index.mjs +59 -61
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -11,97 +11,95 @@ const createElement = (tag, document) => {
|
|
|
11
11
|
return $el;
|
|
12
12
|
};
|
|
13
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
14
|
async function renderDOMHead(head, options = {}) {
|
|
47
15
|
const dom = options.document || window.document;
|
|
48
16
|
const tags = await head.resolveTags();
|
|
49
|
-
const
|
|
50
|
-
await head.hooks.callHook("dom:beforeRender",
|
|
51
|
-
if (!
|
|
17
|
+
const ctx = { shouldRender: true, tags };
|
|
18
|
+
await head.hooks.callHook("dom:beforeRender", ctx);
|
|
19
|
+
if (!ctx.shouldRender)
|
|
52
20
|
return;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
21
|
+
const queuedSideEffects = head._popSideEffectQueue();
|
|
22
|
+
head.headEntries().map((entry) => entry._sde).forEach((sde) => {
|
|
23
|
+
Object.entries(sde).forEach(([key, fn]) => {
|
|
24
|
+
queuedSideEffects[key] = fn;
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
const renderTag = (tag, entry) => {
|
|
59
28
|
if (tag.tag === "title" && tag.children) {
|
|
60
29
|
dom.title = tag.children;
|
|
61
|
-
|
|
30
|
+
return;
|
|
62
31
|
}
|
|
32
|
+
const markSideEffect = (key, fn) => {
|
|
33
|
+
key = `${tag._s || tag._p}:${key}`;
|
|
34
|
+
entry._sde[key] = fn;
|
|
35
|
+
delete queuedSideEffects[key];
|
|
36
|
+
};
|
|
37
|
+
const setAttrs = ($el) => {
|
|
38
|
+
Object.entries(tag.props).forEach(([k, value]) => {
|
|
39
|
+
value = String(value);
|
|
40
|
+
const attrSdeKey = `attr:${k}`;
|
|
41
|
+
if (k === "class") {
|
|
42
|
+
for (const c of value.split(" ")) {
|
|
43
|
+
const classSdeKey = `${attrSdeKey}:${c}`;
|
|
44
|
+
markSideEffect(classSdeKey, () => $el.classList.remove(c));
|
|
45
|
+
if (!$el.classList.contains(c))
|
|
46
|
+
$el.classList.add(c);
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!k.startsWith("data-h-"))
|
|
51
|
+
markSideEffect(attrSdeKey, () => $el.removeAttribute(k));
|
|
52
|
+
if ($el.getAttribute(k) !== value)
|
|
53
|
+
$el.setAttribute(k, value);
|
|
54
|
+
});
|
|
55
|
+
};
|
|
63
56
|
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
setAttrs(dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"]);
|
|
58
|
+
return;
|
|
66
59
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
let $previousEl = null;
|
|
60
|
+
let $newEl = createElement(tag, dom);
|
|
61
|
+
let $previousEl;
|
|
70
62
|
for (const $el of dom[tag.tagPosition?.startsWith("body") ? "body" : "head"].children) {
|
|
71
|
-
if ($el.hasAttribute(`${tag._s}`)) {
|
|
63
|
+
if ($el.hasAttribute(`${tag._s}`) || $el.isEqualNode($newEl)) {
|
|
72
64
|
$previousEl = $el;
|
|
73
65
|
break;
|
|
74
66
|
}
|
|
75
67
|
}
|
|
76
68
|
if ($previousEl) {
|
|
77
|
-
|
|
78
|
-
if ($newEl.isEqualNode($previousEl))
|
|
79
|
-
continue;
|
|
69
|
+
markSideEffect("el", () => $previousEl?.remove());
|
|
80
70
|
if (Object.keys(tag.props).length === 0) {
|
|
81
71
|
$previousEl.remove();
|
|
82
|
-
|
|
72
|
+
return;
|
|
83
73
|
}
|
|
84
|
-
|
|
74
|
+
if ($newEl.isEqualNode($previousEl))
|
|
75
|
+
return;
|
|
76
|
+
setAttrs($previousEl);
|
|
85
77
|
if (TagsWithInnerContent.includes(tag.tag))
|
|
86
78
|
$previousEl.innerHTML = tag.children || "";
|
|
87
|
-
|
|
88
|
-
continue;
|
|
79
|
+
return;
|
|
89
80
|
}
|
|
90
81
|
switch (tag.tagPosition) {
|
|
91
82
|
case "bodyClose":
|
|
92
|
-
dom.body.appendChild($newEl);
|
|
83
|
+
$newEl = dom.body.appendChild($newEl);
|
|
93
84
|
break;
|
|
94
85
|
case "bodyOpen":
|
|
95
|
-
dom.body.insertBefore($newEl, dom.body.firstChild);
|
|
86
|
+
$newEl = dom.body.insertBefore($newEl, dom.body.firstChild);
|
|
96
87
|
break;
|
|
97
88
|
case "head":
|
|
98
89
|
default:
|
|
99
|
-
dom.head.appendChild($newEl);
|
|
90
|
+
$newEl = dom.head.appendChild($newEl);
|
|
100
91
|
break;
|
|
101
92
|
}
|
|
102
|
-
|
|
93
|
+
markSideEffect("el", () => $newEl?.remove());
|
|
94
|
+
};
|
|
95
|
+
for (const tag of ctx.tags) {
|
|
96
|
+
const renderCtx = { shouldRender: true, tag };
|
|
97
|
+
await head.hooks.callHook("dom:renderTag", renderCtx);
|
|
98
|
+
if (!renderCtx.shouldRender)
|
|
99
|
+
continue;
|
|
100
|
+
renderTag(tag, head.headEntries().find((e) => e._i === Number(tag._e)));
|
|
103
101
|
}
|
|
104
|
-
|
|
102
|
+
Object.values(queuedSideEffects).forEach((fn) => fn());
|
|
105
103
|
}
|
|
106
104
|
exports.domUpdatePromise = null;
|
|
107
105
|
async function debouncedRenderDOMHead(head, options = {}) {
|
|
@@ -109,7 +107,7 @@ async function debouncedRenderDOMHead(head, options = {}) {
|
|
|
109
107
|
exports.domUpdatePromise = null;
|
|
110
108
|
return renderDOMHead(head, options);
|
|
111
109
|
}
|
|
112
|
-
const delayFn = options.delayFn || ((fn) => setTimeout(fn,
|
|
110
|
+
const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
|
|
113
111
|
return exports.domUpdatePromise = exports.domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
|
|
114
112
|
}
|
|
115
113
|
|
package/dist/index.mjs
CHANGED
|
@@ -9,97 +9,95 @@ const createElement = (tag, document) => {
|
|
|
9
9
|
return $el;
|
|
10
10
|
};
|
|
11
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
12
|
async function renderDOMHead(head, options = {}) {
|
|
45
13
|
const dom = options.document || window.document;
|
|
46
14
|
const tags = await head.resolveTags();
|
|
47
|
-
const
|
|
48
|
-
await head.hooks.callHook("dom:beforeRender",
|
|
49
|
-
if (!
|
|
15
|
+
const ctx = { shouldRender: true, tags };
|
|
16
|
+
await head.hooks.callHook("dom:beforeRender", ctx);
|
|
17
|
+
if (!ctx.shouldRender)
|
|
50
18
|
return;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
19
|
+
const queuedSideEffects = head._popSideEffectQueue();
|
|
20
|
+
head.headEntries().map((entry) => entry._sde).forEach((sde) => {
|
|
21
|
+
Object.entries(sde).forEach(([key, fn]) => {
|
|
22
|
+
queuedSideEffects[key] = fn;
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
const renderTag = (tag, entry) => {
|
|
57
26
|
if (tag.tag === "title" && tag.children) {
|
|
58
27
|
dom.title = tag.children;
|
|
59
|
-
|
|
28
|
+
return;
|
|
60
29
|
}
|
|
30
|
+
const markSideEffect = (key, fn) => {
|
|
31
|
+
key = `${tag._s || tag._p}:${key}`;
|
|
32
|
+
entry._sde[key] = fn;
|
|
33
|
+
delete queuedSideEffects[key];
|
|
34
|
+
};
|
|
35
|
+
const setAttrs = ($el) => {
|
|
36
|
+
Object.entries(tag.props).forEach(([k, value]) => {
|
|
37
|
+
value = String(value);
|
|
38
|
+
const attrSdeKey = `attr:${k}`;
|
|
39
|
+
if (k === "class") {
|
|
40
|
+
for (const c of value.split(" ")) {
|
|
41
|
+
const classSdeKey = `${attrSdeKey}:${c}`;
|
|
42
|
+
markSideEffect(classSdeKey, () => $el.classList.remove(c));
|
|
43
|
+
if (!$el.classList.contains(c))
|
|
44
|
+
$el.classList.add(c);
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!k.startsWith("data-h-"))
|
|
49
|
+
markSideEffect(attrSdeKey, () => $el.removeAttribute(k));
|
|
50
|
+
if ($el.getAttribute(k) !== value)
|
|
51
|
+
$el.setAttribute(k, value);
|
|
52
|
+
});
|
|
53
|
+
};
|
|
61
54
|
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
setAttrs(dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"]);
|
|
56
|
+
return;
|
|
64
57
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
let $previousEl = null;
|
|
58
|
+
let $newEl = createElement(tag, dom);
|
|
59
|
+
let $previousEl;
|
|
68
60
|
for (const $el of dom[tag.tagPosition?.startsWith("body") ? "body" : "head"].children) {
|
|
69
|
-
if ($el.hasAttribute(`${tag._s}`)) {
|
|
61
|
+
if ($el.hasAttribute(`${tag._s}`) || $el.isEqualNode($newEl)) {
|
|
70
62
|
$previousEl = $el;
|
|
71
63
|
break;
|
|
72
64
|
}
|
|
73
65
|
}
|
|
74
66
|
if ($previousEl) {
|
|
75
|
-
|
|
76
|
-
if ($newEl.isEqualNode($previousEl))
|
|
77
|
-
continue;
|
|
67
|
+
markSideEffect("el", () => $previousEl?.remove());
|
|
78
68
|
if (Object.keys(tag.props).length === 0) {
|
|
79
69
|
$previousEl.remove();
|
|
80
|
-
|
|
70
|
+
return;
|
|
81
71
|
}
|
|
82
|
-
|
|
72
|
+
if ($newEl.isEqualNode($previousEl))
|
|
73
|
+
return;
|
|
74
|
+
setAttrs($previousEl);
|
|
83
75
|
if (TagsWithInnerContent.includes(tag.tag))
|
|
84
76
|
$previousEl.innerHTML = tag.children || "";
|
|
85
|
-
|
|
86
|
-
continue;
|
|
77
|
+
return;
|
|
87
78
|
}
|
|
88
79
|
switch (tag.tagPosition) {
|
|
89
80
|
case "bodyClose":
|
|
90
|
-
dom.body.appendChild($newEl);
|
|
81
|
+
$newEl = dom.body.appendChild($newEl);
|
|
91
82
|
break;
|
|
92
83
|
case "bodyOpen":
|
|
93
|
-
dom.body.insertBefore($newEl, dom.body.firstChild);
|
|
84
|
+
$newEl = dom.body.insertBefore($newEl, dom.body.firstChild);
|
|
94
85
|
break;
|
|
95
86
|
case "head":
|
|
96
87
|
default:
|
|
97
|
-
dom.head.appendChild($newEl);
|
|
88
|
+
$newEl = dom.head.appendChild($newEl);
|
|
98
89
|
break;
|
|
99
90
|
}
|
|
100
|
-
|
|
91
|
+
markSideEffect("el", () => $newEl?.remove());
|
|
92
|
+
};
|
|
93
|
+
for (const tag of ctx.tags) {
|
|
94
|
+
const renderCtx = { shouldRender: true, tag };
|
|
95
|
+
await head.hooks.callHook("dom:renderTag", renderCtx);
|
|
96
|
+
if (!renderCtx.shouldRender)
|
|
97
|
+
continue;
|
|
98
|
+
renderTag(tag, head.headEntries().find((e) => e._i === Number(tag._e)));
|
|
101
99
|
}
|
|
102
|
-
|
|
100
|
+
Object.values(queuedSideEffects).forEach((fn) => fn());
|
|
103
101
|
}
|
|
104
102
|
let domUpdatePromise = null;
|
|
105
103
|
async function debouncedRenderDOMHead(head, options = {}) {
|
|
@@ -107,7 +105,7 @@ async function debouncedRenderDOMHead(head, options = {}) {
|
|
|
107
105
|
domUpdatePromise = null;
|
|
108
106
|
return renderDOMHead(head, options);
|
|
109
107
|
}
|
|
110
|
-
const delayFn = options.delayFn || ((fn) => setTimeout(fn,
|
|
108
|
+
const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
|
|
111
109
|
return domUpdatePromise = domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
|
|
112
110
|
}
|
|
113
111
|
|
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.3.1",
|
|
5
5
|
"packageManager": "pnpm@7.14.0",
|
|
6
6
|
"author": "Harlan Wilton <harlan@harlanzw.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@unhead/schema": "0.
|
|
33
|
+
"@unhead/schema": "0.3.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"zhead": "1.0.0-beta.10"
|