@unhead/vue 0.6.6 → 0.6.8
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 +224 -18
- package/dist/index.mjs +224 -18
- package/package.json +3 -3
- package/dist/chunks/index.cjs +0 -165
- package/dist/chunks/index.mjs +0 -162
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,222 @@
|
|
|
3
3
|
const hookable = require('hookable');
|
|
4
4
|
const vue = require('vue');
|
|
5
5
|
|
|
6
|
+
const TagsWithInnerContent = ["script", "style", "noscript"];
|
|
7
|
+
const HasElementTags$1 = [
|
|
8
|
+
"base",
|
|
9
|
+
"meta",
|
|
10
|
+
"link",
|
|
11
|
+
"style",
|
|
12
|
+
"script",
|
|
13
|
+
"noscript"
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const UniqueTags$1 = ["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"];
|
|
17
|
+
const ArrayMetaProperties$1 = [
|
|
18
|
+
"og:image",
|
|
19
|
+
"og:video",
|
|
20
|
+
"og:audio",
|
|
21
|
+
"og:locale:alternate",
|
|
22
|
+
"video:actor",
|
|
23
|
+
"video:director",
|
|
24
|
+
"video:writer",
|
|
25
|
+
"video:tag",
|
|
26
|
+
"article:author",
|
|
27
|
+
"article:tag",
|
|
28
|
+
"book:tag",
|
|
29
|
+
"book:author",
|
|
30
|
+
"music:album",
|
|
31
|
+
"music:musician"
|
|
32
|
+
];
|
|
33
|
+
function tagDedupeKey$1(tag) {
|
|
34
|
+
const { props, tag: tagName } = tag;
|
|
35
|
+
if (UniqueTags$1.includes(tagName))
|
|
36
|
+
return tagName;
|
|
37
|
+
if (tagName === "link" && props.rel === "canonical")
|
|
38
|
+
return "canonical";
|
|
39
|
+
if (props.charset)
|
|
40
|
+
return "charset";
|
|
41
|
+
const name = ["id"];
|
|
42
|
+
if (tagName === "meta")
|
|
43
|
+
name.push(...["name", "property", "http-equiv"]);
|
|
44
|
+
for (const n of name) {
|
|
45
|
+
if (typeof props[n] !== "undefined") {
|
|
46
|
+
const val = String(props[n]);
|
|
47
|
+
if (ArrayMetaProperties$1.findIndex((p) => val.startsWith(p)) !== -1)
|
|
48
|
+
return false;
|
|
49
|
+
return `${tagName}:${n}:${val}`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const setAttrs = (ctx, markSideEffect) => {
|
|
56
|
+
const { tag, $el } = ctx;
|
|
57
|
+
if (!$el)
|
|
58
|
+
return;
|
|
59
|
+
Object.entries(tag.props).forEach(([k, value]) => {
|
|
60
|
+
value = String(value);
|
|
61
|
+
const attrSdeKey = `attr:${k}`;
|
|
62
|
+
if (k === "class") {
|
|
63
|
+
for (const c of value.split(" ")) {
|
|
64
|
+
const classSdeKey = `${attrSdeKey}:${c}`;
|
|
65
|
+
if (markSideEffect)
|
|
66
|
+
markSideEffect(ctx, classSdeKey, () => $el.classList.remove(c));
|
|
67
|
+
if (!$el.classList.contains(c))
|
|
68
|
+
$el.classList.add(c);
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (markSideEffect && !k.startsWith("data-h-"))
|
|
73
|
+
markSideEffect(ctx, attrSdeKey, () => $el.removeAttribute(k));
|
|
74
|
+
if ($el.getAttribute(k) !== value)
|
|
75
|
+
$el.setAttribute(k, value);
|
|
76
|
+
});
|
|
77
|
+
if (TagsWithInnerContent.includes(tag.tag) && $el.innerHTML !== (tag.children || ""))
|
|
78
|
+
$el.innerHTML = tag.children || "";
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
function hashCode(s) {
|
|
82
|
+
let h = 9;
|
|
83
|
+
for (let i = 0; i < s.length; )
|
|
84
|
+
h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
|
|
85
|
+
return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 8).toLowerCase();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function renderDOMHead(head, options = {}) {
|
|
89
|
+
const ctx = { shouldRender: true };
|
|
90
|
+
await head.hooks.callHook("dom:beforeRender", ctx);
|
|
91
|
+
if (!ctx.shouldRender)
|
|
92
|
+
return;
|
|
93
|
+
const dom = options.document || window.document;
|
|
94
|
+
const staleSideEffects = head._popSideEffectQueue();
|
|
95
|
+
head.headEntries().map((entry) => entry._sde).forEach((sde) => {
|
|
96
|
+
Object.entries(sde).forEach(([key, fn]) => {
|
|
97
|
+
staleSideEffects[key] = fn;
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
const preRenderTag = async (tag) => {
|
|
101
|
+
const entry = head.headEntries().find((e) => e._i === tag._e);
|
|
102
|
+
const renderCtx = {
|
|
103
|
+
renderId: tag._d || hashCode(JSON.stringify({ ...tag, _e: void 0, _p: void 0 })),
|
|
104
|
+
$el: null,
|
|
105
|
+
shouldRender: true,
|
|
106
|
+
tag,
|
|
107
|
+
entry,
|
|
108
|
+
staleSideEffects
|
|
109
|
+
};
|
|
110
|
+
await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
|
|
111
|
+
return renderCtx;
|
|
112
|
+
};
|
|
113
|
+
const renders = [];
|
|
114
|
+
const pendingRenders = {
|
|
115
|
+
body: [],
|
|
116
|
+
head: []
|
|
117
|
+
};
|
|
118
|
+
const markSideEffect = (ctx2, key, fn) => {
|
|
119
|
+
key = `${ctx2.renderId}:${key}`;
|
|
120
|
+
if (ctx2.entry)
|
|
121
|
+
ctx2.entry._sde[key] = fn;
|
|
122
|
+
delete staleSideEffects[key];
|
|
123
|
+
};
|
|
124
|
+
const markEl = (ctx2) => {
|
|
125
|
+
head._elMap[ctx2.renderId] = ctx2.$el;
|
|
126
|
+
renders.push(ctx2);
|
|
127
|
+
markSideEffect(ctx2, "el", () => {
|
|
128
|
+
ctx2.$el?.remove();
|
|
129
|
+
delete head._elMap[ctx2.renderId];
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
for (const t of await head.resolveTags()) {
|
|
133
|
+
const ctx2 = await preRenderTag(t);
|
|
134
|
+
if (!ctx2.shouldRender)
|
|
135
|
+
continue;
|
|
136
|
+
const { tag } = ctx2;
|
|
137
|
+
if (tag.tag === "title") {
|
|
138
|
+
dom.title = tag.children || "";
|
|
139
|
+
renders.push(ctx2);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
|
|
143
|
+
ctx2.$el = dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"];
|
|
144
|
+
setAttrs(ctx2, markSideEffect);
|
|
145
|
+
renders.push(ctx2);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
ctx2.$el = head._elMap[ctx2.renderId];
|
|
149
|
+
if (!ctx2.$el && tag._hash) {
|
|
150
|
+
ctx2.$el = dom.querySelector(`${tag.tagPosition?.startsWith("body") ? "body" : "head"} > ${tag.tag}[data-h-${tag._hash}]`);
|
|
151
|
+
}
|
|
152
|
+
if (ctx2.$el) {
|
|
153
|
+
if (ctx2.tag._d)
|
|
154
|
+
setAttrs(ctx2);
|
|
155
|
+
markEl(ctx2);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
ctx2.$el = dom.createElement(tag.tag);
|
|
159
|
+
setAttrs(ctx2);
|
|
160
|
+
pendingRenders[tag.tagPosition?.startsWith("body") ? "body" : "head"].push(ctx2);
|
|
161
|
+
}
|
|
162
|
+
Object.entries(pendingRenders).forEach(([pos, queue]) => {
|
|
163
|
+
if (!queue.length)
|
|
164
|
+
return;
|
|
165
|
+
for (const $el of [...dom[pos].children].reverse()) {
|
|
166
|
+
const elTag = $el.tagName.toLowerCase();
|
|
167
|
+
if (!HasElementTags$1.includes(elTag))
|
|
168
|
+
continue;
|
|
169
|
+
const dedupeKey = tagDedupeKey$1({
|
|
170
|
+
tag: elTag,
|
|
171
|
+
props: $el.getAttributeNames().reduce((props, name) => ({ ...props, [name]: $el.getAttribute(name) }), {})
|
|
172
|
+
});
|
|
173
|
+
const matchIdx = queue.findIndex((ctx2) => ctx2 && (ctx2.tag._d === dedupeKey || $el.isEqualNode(ctx2.$el)));
|
|
174
|
+
if (matchIdx !== -1) {
|
|
175
|
+
const ctx2 = queue[matchIdx];
|
|
176
|
+
ctx2.$el = $el;
|
|
177
|
+
setAttrs(ctx2);
|
|
178
|
+
markEl(ctx2);
|
|
179
|
+
delete queue[matchIdx];
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
queue.forEach((ctx2) => {
|
|
183
|
+
if (!ctx2.$el)
|
|
184
|
+
return;
|
|
185
|
+
switch (ctx2.tag.tagPosition) {
|
|
186
|
+
case "bodyClose":
|
|
187
|
+
dom.body.appendChild(ctx2.$el);
|
|
188
|
+
break;
|
|
189
|
+
case "bodyOpen":
|
|
190
|
+
dom.body.insertBefore(ctx2.$el, dom.body.firstChild);
|
|
191
|
+
break;
|
|
192
|
+
case "head":
|
|
193
|
+
default:
|
|
194
|
+
dom.head.appendChild(ctx2.$el);
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
markEl(ctx2);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
for (const ctx2 of renders)
|
|
201
|
+
await head.hooks.callHook("dom:renderTag", ctx2);
|
|
202
|
+
Object.values(staleSideEffects).forEach((fn) => fn());
|
|
203
|
+
}
|
|
204
|
+
let domUpdatePromise = null;
|
|
205
|
+
async function debouncedRenderDOMHead(head, options = {}) {
|
|
206
|
+
function doDomUpdate() {
|
|
207
|
+
domUpdatePromise = null;
|
|
208
|
+
return renderDOMHead(head, options);
|
|
209
|
+
}
|
|
210
|
+
const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
|
|
211
|
+
return domUpdatePromise = domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const index = {
|
|
215
|
+
__proto__: null,
|
|
216
|
+
debouncedRenderDOMHead: debouncedRenderDOMHead,
|
|
217
|
+
get domUpdatePromise () { return domUpdatePromise; },
|
|
218
|
+
hashCode: hashCode,
|
|
219
|
+
renderDOMHead: renderDOMHead
|
|
220
|
+
};
|
|
221
|
+
|
|
6
222
|
const ValidHeadTags = [
|
|
7
223
|
"title",
|
|
8
224
|
"titleTemplate",
|
|
@@ -295,7 +511,7 @@ const PatchDomOnEntryUpdatesPlugin = (options) => {
|
|
|
295
511
|
let delayFn = options?.delayFn;
|
|
296
512
|
if (!delayFn && typeof requestAnimationFrame !== "undefined")
|
|
297
513
|
delayFn = requestAnimationFrame;
|
|
298
|
-
|
|
514
|
+
Promise.resolve().then(function () { return index; }).then(({ debouncedRenderDOMHead }) => {
|
|
299
515
|
debouncedRenderDOMHead(head, { document: options?.document || window.document, delayFn });
|
|
300
516
|
});
|
|
301
517
|
}
|
|
@@ -345,7 +561,7 @@ const EventHandlersPlugin = () => {
|
|
|
345
561
|
const sdeKey = `${ctx.tag._d || ctx.tag._p}:${k}`;
|
|
346
562
|
const eventName = k.slice(2).toLowerCase();
|
|
347
563
|
const eventDedupeKey = `data-h-${eventName}`;
|
|
348
|
-
delete ctx.
|
|
564
|
+
delete ctx.staleSideEffects[sdeKey];
|
|
349
565
|
if ($el.hasAttribute(eventDedupeKey))
|
|
350
566
|
return;
|
|
351
567
|
const handler = value;
|
|
@@ -369,12 +585,6 @@ const EventHandlersPlugin = () => {
|
|
|
369
585
|
function asArray$1(value) {
|
|
370
586
|
return Array.isArray(value) ? value : [value];
|
|
371
587
|
}
|
|
372
|
-
function hashCode(s) {
|
|
373
|
-
let h = 9;
|
|
374
|
-
for (let i = 0; i < s.length; )
|
|
375
|
-
h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
|
|
376
|
-
return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 8).toLowerCase();
|
|
377
|
-
}
|
|
378
588
|
const HasElementTags = [
|
|
379
589
|
"base",
|
|
380
590
|
"meta",
|
|
@@ -431,7 +641,7 @@ function createHeadCore(options = {}) {
|
|
|
431
641
|
...options?.plugins || []
|
|
432
642
|
];
|
|
433
643
|
options.plugins.forEach((p) => p.hooks && hooks.addHooks(p.hooks));
|
|
434
|
-
const
|
|
644
|
+
const updated = () => hooks.callHook("entries:updated", head);
|
|
435
645
|
const head = {
|
|
436
646
|
resolvedOptions: options,
|
|
437
647
|
headEntries() {
|
|
@@ -449,27 +659,23 @@ function createHeadCore(options = {}) {
|
|
|
449
659
|
if (options2?.mode)
|
|
450
660
|
activeEntry._m = options2?.mode;
|
|
451
661
|
entries.push(activeEntry);
|
|
452
|
-
|
|
453
|
-
const queueSideEffects = (e) => {
|
|
454
|
-
_sde = { ..._sde, ...e._sde || {} };
|
|
455
|
-
e._sde = {};
|
|
456
|
-
triggerUpdateHook();
|
|
457
|
-
};
|
|
662
|
+
updated();
|
|
458
663
|
return {
|
|
459
664
|
dispose() {
|
|
460
665
|
entries = entries.filter((e) => {
|
|
461
666
|
if (e._i !== activeEntry._i)
|
|
462
667
|
return true;
|
|
463
|
-
|
|
668
|
+
_sde = { ..._sde, ...e._sde || {} };
|
|
669
|
+
e._sde = {};
|
|
670
|
+
updated();
|
|
464
671
|
return false;
|
|
465
672
|
});
|
|
466
673
|
},
|
|
467
674
|
patch(input2) {
|
|
468
675
|
entries = entries.map((e) => {
|
|
469
676
|
if (e._i === activeEntry._i) {
|
|
470
|
-
queueSideEffects(e);
|
|
471
677
|
activeEntry.input = e.input = input2;
|
|
472
|
-
|
|
678
|
+
updated();
|
|
473
679
|
}
|
|
474
680
|
return e;
|
|
475
681
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,222 @@
|
|
|
1
1
|
import { createHooks } from 'hookable';
|
|
2
2
|
import { unref, isRef, version, getCurrentInstance, inject, nextTick, ref, watchEffect, watch, onBeforeUnmount } from 'vue';
|
|
3
3
|
|
|
4
|
+
const TagsWithInnerContent = ["script", "style", "noscript"];
|
|
5
|
+
const HasElementTags$1 = [
|
|
6
|
+
"base",
|
|
7
|
+
"meta",
|
|
8
|
+
"link",
|
|
9
|
+
"style",
|
|
10
|
+
"script",
|
|
11
|
+
"noscript"
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const UniqueTags$1 = ["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"];
|
|
15
|
+
const ArrayMetaProperties$1 = [
|
|
16
|
+
"og:image",
|
|
17
|
+
"og:video",
|
|
18
|
+
"og:audio",
|
|
19
|
+
"og:locale:alternate",
|
|
20
|
+
"video:actor",
|
|
21
|
+
"video:director",
|
|
22
|
+
"video:writer",
|
|
23
|
+
"video:tag",
|
|
24
|
+
"article:author",
|
|
25
|
+
"article:tag",
|
|
26
|
+
"book:tag",
|
|
27
|
+
"book:author",
|
|
28
|
+
"music:album",
|
|
29
|
+
"music:musician"
|
|
30
|
+
];
|
|
31
|
+
function tagDedupeKey$1(tag) {
|
|
32
|
+
const { props, tag: tagName } = tag;
|
|
33
|
+
if (UniqueTags$1.includes(tagName))
|
|
34
|
+
return tagName;
|
|
35
|
+
if (tagName === "link" && props.rel === "canonical")
|
|
36
|
+
return "canonical";
|
|
37
|
+
if (props.charset)
|
|
38
|
+
return "charset";
|
|
39
|
+
const name = ["id"];
|
|
40
|
+
if (tagName === "meta")
|
|
41
|
+
name.push(...["name", "property", "http-equiv"]);
|
|
42
|
+
for (const n of name) {
|
|
43
|
+
if (typeof props[n] !== "undefined") {
|
|
44
|
+
const val = String(props[n]);
|
|
45
|
+
if (ArrayMetaProperties$1.findIndex((p) => val.startsWith(p)) !== -1)
|
|
46
|
+
return false;
|
|
47
|
+
return `${tagName}:${n}:${val}`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const setAttrs = (ctx, markSideEffect) => {
|
|
54
|
+
const { tag, $el } = ctx;
|
|
55
|
+
if (!$el)
|
|
56
|
+
return;
|
|
57
|
+
Object.entries(tag.props).forEach(([k, value]) => {
|
|
58
|
+
value = String(value);
|
|
59
|
+
const attrSdeKey = `attr:${k}`;
|
|
60
|
+
if (k === "class") {
|
|
61
|
+
for (const c of value.split(" ")) {
|
|
62
|
+
const classSdeKey = `${attrSdeKey}:${c}`;
|
|
63
|
+
if (markSideEffect)
|
|
64
|
+
markSideEffect(ctx, classSdeKey, () => $el.classList.remove(c));
|
|
65
|
+
if (!$el.classList.contains(c))
|
|
66
|
+
$el.classList.add(c);
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (markSideEffect && !k.startsWith("data-h-"))
|
|
71
|
+
markSideEffect(ctx, attrSdeKey, () => $el.removeAttribute(k));
|
|
72
|
+
if ($el.getAttribute(k) !== value)
|
|
73
|
+
$el.setAttribute(k, value);
|
|
74
|
+
});
|
|
75
|
+
if (TagsWithInnerContent.includes(tag.tag) && $el.innerHTML !== (tag.children || ""))
|
|
76
|
+
$el.innerHTML = tag.children || "";
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
function hashCode(s) {
|
|
80
|
+
let h = 9;
|
|
81
|
+
for (let i = 0; i < s.length; )
|
|
82
|
+
h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
|
|
83
|
+
return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 8).toLowerCase();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function renderDOMHead(head, options = {}) {
|
|
87
|
+
const ctx = { shouldRender: true };
|
|
88
|
+
await head.hooks.callHook("dom:beforeRender", ctx);
|
|
89
|
+
if (!ctx.shouldRender)
|
|
90
|
+
return;
|
|
91
|
+
const dom = options.document || window.document;
|
|
92
|
+
const staleSideEffects = head._popSideEffectQueue();
|
|
93
|
+
head.headEntries().map((entry) => entry._sde).forEach((sde) => {
|
|
94
|
+
Object.entries(sde).forEach(([key, fn]) => {
|
|
95
|
+
staleSideEffects[key] = fn;
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
const preRenderTag = async (tag) => {
|
|
99
|
+
const entry = head.headEntries().find((e) => e._i === tag._e);
|
|
100
|
+
const renderCtx = {
|
|
101
|
+
renderId: tag._d || hashCode(JSON.stringify({ ...tag, _e: void 0, _p: void 0 })),
|
|
102
|
+
$el: null,
|
|
103
|
+
shouldRender: true,
|
|
104
|
+
tag,
|
|
105
|
+
entry,
|
|
106
|
+
staleSideEffects
|
|
107
|
+
};
|
|
108
|
+
await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
|
|
109
|
+
return renderCtx;
|
|
110
|
+
};
|
|
111
|
+
const renders = [];
|
|
112
|
+
const pendingRenders = {
|
|
113
|
+
body: [],
|
|
114
|
+
head: []
|
|
115
|
+
};
|
|
116
|
+
const markSideEffect = (ctx2, key, fn) => {
|
|
117
|
+
key = `${ctx2.renderId}:${key}`;
|
|
118
|
+
if (ctx2.entry)
|
|
119
|
+
ctx2.entry._sde[key] = fn;
|
|
120
|
+
delete staleSideEffects[key];
|
|
121
|
+
};
|
|
122
|
+
const markEl = (ctx2) => {
|
|
123
|
+
head._elMap[ctx2.renderId] = ctx2.$el;
|
|
124
|
+
renders.push(ctx2);
|
|
125
|
+
markSideEffect(ctx2, "el", () => {
|
|
126
|
+
ctx2.$el?.remove();
|
|
127
|
+
delete head._elMap[ctx2.renderId];
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
for (const t of await head.resolveTags()) {
|
|
131
|
+
const ctx2 = await preRenderTag(t);
|
|
132
|
+
if (!ctx2.shouldRender)
|
|
133
|
+
continue;
|
|
134
|
+
const { tag } = ctx2;
|
|
135
|
+
if (tag.tag === "title") {
|
|
136
|
+
dom.title = tag.children || "";
|
|
137
|
+
renders.push(ctx2);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
|
|
141
|
+
ctx2.$el = dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"];
|
|
142
|
+
setAttrs(ctx2, markSideEffect);
|
|
143
|
+
renders.push(ctx2);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
ctx2.$el = head._elMap[ctx2.renderId];
|
|
147
|
+
if (!ctx2.$el && tag._hash) {
|
|
148
|
+
ctx2.$el = dom.querySelector(`${tag.tagPosition?.startsWith("body") ? "body" : "head"} > ${tag.tag}[data-h-${tag._hash}]`);
|
|
149
|
+
}
|
|
150
|
+
if (ctx2.$el) {
|
|
151
|
+
if (ctx2.tag._d)
|
|
152
|
+
setAttrs(ctx2);
|
|
153
|
+
markEl(ctx2);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
ctx2.$el = dom.createElement(tag.tag);
|
|
157
|
+
setAttrs(ctx2);
|
|
158
|
+
pendingRenders[tag.tagPosition?.startsWith("body") ? "body" : "head"].push(ctx2);
|
|
159
|
+
}
|
|
160
|
+
Object.entries(pendingRenders).forEach(([pos, queue]) => {
|
|
161
|
+
if (!queue.length)
|
|
162
|
+
return;
|
|
163
|
+
for (const $el of [...dom[pos].children].reverse()) {
|
|
164
|
+
const elTag = $el.tagName.toLowerCase();
|
|
165
|
+
if (!HasElementTags$1.includes(elTag))
|
|
166
|
+
continue;
|
|
167
|
+
const dedupeKey = tagDedupeKey$1({
|
|
168
|
+
tag: elTag,
|
|
169
|
+
props: $el.getAttributeNames().reduce((props, name) => ({ ...props, [name]: $el.getAttribute(name) }), {})
|
|
170
|
+
});
|
|
171
|
+
const matchIdx = queue.findIndex((ctx2) => ctx2 && (ctx2.tag._d === dedupeKey || $el.isEqualNode(ctx2.$el)));
|
|
172
|
+
if (matchIdx !== -1) {
|
|
173
|
+
const ctx2 = queue[matchIdx];
|
|
174
|
+
ctx2.$el = $el;
|
|
175
|
+
setAttrs(ctx2);
|
|
176
|
+
markEl(ctx2);
|
|
177
|
+
delete queue[matchIdx];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
queue.forEach((ctx2) => {
|
|
181
|
+
if (!ctx2.$el)
|
|
182
|
+
return;
|
|
183
|
+
switch (ctx2.tag.tagPosition) {
|
|
184
|
+
case "bodyClose":
|
|
185
|
+
dom.body.appendChild(ctx2.$el);
|
|
186
|
+
break;
|
|
187
|
+
case "bodyOpen":
|
|
188
|
+
dom.body.insertBefore(ctx2.$el, dom.body.firstChild);
|
|
189
|
+
break;
|
|
190
|
+
case "head":
|
|
191
|
+
default:
|
|
192
|
+
dom.head.appendChild(ctx2.$el);
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
markEl(ctx2);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
for (const ctx2 of renders)
|
|
199
|
+
await head.hooks.callHook("dom:renderTag", ctx2);
|
|
200
|
+
Object.values(staleSideEffects).forEach((fn) => fn());
|
|
201
|
+
}
|
|
202
|
+
let domUpdatePromise = null;
|
|
203
|
+
async function debouncedRenderDOMHead(head, options = {}) {
|
|
204
|
+
function doDomUpdate() {
|
|
205
|
+
domUpdatePromise = null;
|
|
206
|
+
return renderDOMHead(head, options);
|
|
207
|
+
}
|
|
208
|
+
const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
|
|
209
|
+
return domUpdatePromise = domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const index = {
|
|
213
|
+
__proto__: null,
|
|
214
|
+
debouncedRenderDOMHead: debouncedRenderDOMHead,
|
|
215
|
+
get domUpdatePromise () { return domUpdatePromise; },
|
|
216
|
+
hashCode: hashCode,
|
|
217
|
+
renderDOMHead: renderDOMHead
|
|
218
|
+
};
|
|
219
|
+
|
|
4
220
|
const ValidHeadTags = [
|
|
5
221
|
"title",
|
|
6
222
|
"titleTemplate",
|
|
@@ -293,7 +509,7 @@ const PatchDomOnEntryUpdatesPlugin = (options) => {
|
|
|
293
509
|
let delayFn = options?.delayFn;
|
|
294
510
|
if (!delayFn && typeof requestAnimationFrame !== "undefined")
|
|
295
511
|
delayFn = requestAnimationFrame;
|
|
296
|
-
|
|
512
|
+
Promise.resolve().then(function () { return index; }).then(({ debouncedRenderDOMHead }) => {
|
|
297
513
|
debouncedRenderDOMHead(head, { document: options?.document || window.document, delayFn });
|
|
298
514
|
});
|
|
299
515
|
}
|
|
@@ -343,7 +559,7 @@ const EventHandlersPlugin = () => {
|
|
|
343
559
|
const sdeKey = `${ctx.tag._d || ctx.tag._p}:${k}`;
|
|
344
560
|
const eventName = k.slice(2).toLowerCase();
|
|
345
561
|
const eventDedupeKey = `data-h-${eventName}`;
|
|
346
|
-
delete ctx.
|
|
562
|
+
delete ctx.staleSideEffects[sdeKey];
|
|
347
563
|
if ($el.hasAttribute(eventDedupeKey))
|
|
348
564
|
return;
|
|
349
565
|
const handler = value;
|
|
@@ -367,12 +583,6 @@ const EventHandlersPlugin = () => {
|
|
|
367
583
|
function asArray$1(value) {
|
|
368
584
|
return Array.isArray(value) ? value : [value];
|
|
369
585
|
}
|
|
370
|
-
function hashCode(s) {
|
|
371
|
-
let h = 9;
|
|
372
|
-
for (let i = 0; i < s.length; )
|
|
373
|
-
h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
|
|
374
|
-
return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 8).toLowerCase();
|
|
375
|
-
}
|
|
376
586
|
const HasElementTags = [
|
|
377
587
|
"base",
|
|
378
588
|
"meta",
|
|
@@ -429,7 +639,7 @@ function createHeadCore(options = {}) {
|
|
|
429
639
|
...options?.plugins || []
|
|
430
640
|
];
|
|
431
641
|
options.plugins.forEach((p) => p.hooks && hooks.addHooks(p.hooks));
|
|
432
|
-
const
|
|
642
|
+
const updated = () => hooks.callHook("entries:updated", head);
|
|
433
643
|
const head = {
|
|
434
644
|
resolvedOptions: options,
|
|
435
645
|
headEntries() {
|
|
@@ -447,27 +657,23 @@ function createHeadCore(options = {}) {
|
|
|
447
657
|
if (options2?.mode)
|
|
448
658
|
activeEntry._m = options2?.mode;
|
|
449
659
|
entries.push(activeEntry);
|
|
450
|
-
|
|
451
|
-
const queueSideEffects = (e) => {
|
|
452
|
-
_sde = { ..._sde, ...e._sde || {} };
|
|
453
|
-
e._sde = {};
|
|
454
|
-
triggerUpdateHook();
|
|
455
|
-
};
|
|
660
|
+
updated();
|
|
456
661
|
return {
|
|
457
662
|
dispose() {
|
|
458
663
|
entries = entries.filter((e) => {
|
|
459
664
|
if (e._i !== activeEntry._i)
|
|
460
665
|
return true;
|
|
461
|
-
|
|
666
|
+
_sde = { ..._sde, ...e._sde || {} };
|
|
667
|
+
e._sde = {};
|
|
668
|
+
updated();
|
|
462
669
|
return false;
|
|
463
670
|
});
|
|
464
671
|
},
|
|
465
672
|
patch(input2) {
|
|
466
673
|
entries = entries.map((e) => {
|
|
467
674
|
if (e._i === activeEntry._i) {
|
|
468
|
-
queueSideEffects(e);
|
|
469
675
|
activeEntry.input = e.input = input2;
|
|
470
|
-
|
|
676
|
+
updated();
|
|
471
677
|
}
|
|
472
678
|
return e;
|
|
473
679
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unhead/vue",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.8",
|
|
5
5
|
"packageManager": "pnpm@7.14.0",
|
|
6
6
|
"author": "Harlan Wilton <harlan@harlanzw.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"vue": ">=2.7 || >=3"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@unhead/schema": "0.6.
|
|
36
|
+
"@unhead/schema": "0.6.8",
|
|
37
37
|
"hookable": "^5.4.1"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"unhead": "0.6.
|
|
40
|
+
"unhead": "0.6.8",
|
|
41
41
|
"vue": "^3.2.45"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
package/dist/chunks/index.cjs
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const TagsWithInnerContent = ["script", "style", "noscript"];
|
|
4
|
-
|
|
5
|
-
const UniqueTags = ["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"];
|
|
6
|
-
const ArrayMetaProperties = [
|
|
7
|
-
"og:image",
|
|
8
|
-
"og:video",
|
|
9
|
-
"og:audio",
|
|
10
|
-
"og:locale:alternate",
|
|
11
|
-
"video:actor",
|
|
12
|
-
"video:director",
|
|
13
|
-
"video:writer",
|
|
14
|
-
"video:tag",
|
|
15
|
-
"article:author",
|
|
16
|
-
"article:tag",
|
|
17
|
-
"book:tag",
|
|
18
|
-
"book:author",
|
|
19
|
-
"music:album",
|
|
20
|
-
"music:musician"
|
|
21
|
-
];
|
|
22
|
-
function tagDedupeKey(tag) {
|
|
23
|
-
const { props, tag: tagName } = tag;
|
|
24
|
-
if (UniqueTags.includes(tagName))
|
|
25
|
-
return tagName;
|
|
26
|
-
if (tagName === "link" && props.rel === "canonical")
|
|
27
|
-
return "canonical";
|
|
28
|
-
if (props.charset)
|
|
29
|
-
return "charset";
|
|
30
|
-
const name = ["id"];
|
|
31
|
-
if (tagName === "meta")
|
|
32
|
-
name.push(...["name", "property", "http-equiv"]);
|
|
33
|
-
for (const n of name) {
|
|
34
|
-
if (typeof props[n] !== "undefined") {
|
|
35
|
-
const val = String(props[n]);
|
|
36
|
-
if (ArrayMetaProperties.findIndex((p) => val.startsWith(p)) !== -1)
|
|
37
|
-
return false;
|
|
38
|
-
return `${tagName}:${n}:${val}`;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function renderDOMHead(head, options = {}) {
|
|
45
|
-
const ctx = { shouldRender: true };
|
|
46
|
-
await head.hooks.callHook("dom:beforeRender", ctx);
|
|
47
|
-
if (!ctx.shouldRender)
|
|
48
|
-
return;
|
|
49
|
-
const dom = options.document || window.document;
|
|
50
|
-
const staleSideEffects = head._popSideEffectQueue();
|
|
51
|
-
head.headEntries().map((entry) => entry._sde).forEach((sde) => {
|
|
52
|
-
Object.entries(sde).forEach(([key, fn]) => {
|
|
53
|
-
staleSideEffects[key] = fn;
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
const renderTag = (tag, entry) => {
|
|
57
|
-
if (tag.tag === "title" && tag.children) {
|
|
58
|
-
dom.title = tag.children;
|
|
59
|
-
return dom.head.querySelector("title");
|
|
60
|
-
}
|
|
61
|
-
const tagRenderId = tag._d || tag._p;
|
|
62
|
-
const markSideEffect = (key, fn) => {
|
|
63
|
-
key = `${tagRenderId}:${key}`;
|
|
64
|
-
if (entry)
|
|
65
|
-
entry._sde[key] = fn;
|
|
66
|
-
delete staleSideEffects[key];
|
|
67
|
-
};
|
|
68
|
-
const setAttrs = ($el, sideEffects = true) => {
|
|
69
|
-
Object.entries(tag.props).forEach(([k, value]) => {
|
|
70
|
-
value = String(value);
|
|
71
|
-
const attrSdeKey = `attr:${k}`;
|
|
72
|
-
if (k === "class") {
|
|
73
|
-
for (const c of value.split(" ")) {
|
|
74
|
-
const classSdeKey = `${attrSdeKey}:${c}`;
|
|
75
|
-
sideEffects && markSideEffect(classSdeKey, () => $el.classList.remove(c));
|
|
76
|
-
if (!$el.classList.contains(c))
|
|
77
|
-
$el.classList.add(c);
|
|
78
|
-
}
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
if (sideEffects && !k.startsWith("data-h-"))
|
|
82
|
-
markSideEffect(attrSdeKey, () => $el.removeAttribute(k));
|
|
83
|
-
if ($el.getAttribute(k) !== value)
|
|
84
|
-
$el.setAttribute(k, value);
|
|
85
|
-
});
|
|
86
|
-
if (TagsWithInnerContent.includes(tag.tag) && $el.innerHTML !== (tag.children || ""))
|
|
87
|
-
$el.innerHTML = tag.children || "";
|
|
88
|
-
};
|
|
89
|
-
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
|
|
90
|
-
const $el = dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"];
|
|
91
|
-
setAttrs($el);
|
|
92
|
-
return $el;
|
|
93
|
-
}
|
|
94
|
-
let $newEl = dom.createElement(tag.tag);
|
|
95
|
-
setAttrs($newEl, false);
|
|
96
|
-
let $previousEl = head._elMap[tagRenderId];
|
|
97
|
-
const $target = dom[tag.tagPosition?.startsWith("body") ? "body" : "head"];
|
|
98
|
-
if (!$previousEl && tag._hash) {
|
|
99
|
-
$previousEl = $target.querySelector(`${tag.tag}[data-h-${tag._hash}]`);
|
|
100
|
-
}
|
|
101
|
-
if (!$previousEl) {
|
|
102
|
-
for (const $el of tag.tagPosition === "bodyClose" ? [...$target.children].reverse() : $target.children) {
|
|
103
|
-
const elTag = $el.tagName.toLowerCase();
|
|
104
|
-
if (elTag !== tag.tag)
|
|
105
|
-
continue;
|
|
106
|
-
const key = tagDedupeKey({
|
|
107
|
-
tag: elTag,
|
|
108
|
-
props: $el.getAttributeNames().reduce((props, name) => ({ ...props, [name]: $el.getAttribute(name) }), {})
|
|
109
|
-
});
|
|
110
|
-
if (key === tag._d || $el.isEqualNode($newEl)) {
|
|
111
|
-
$previousEl = $el;
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
const markEl = ($el) => {
|
|
117
|
-
head._elMap[tagRenderId] = $el;
|
|
118
|
-
markSideEffect("el", () => {
|
|
119
|
-
$el?.remove();
|
|
120
|
-
delete head._elMap[tagRenderId];
|
|
121
|
-
});
|
|
122
|
-
};
|
|
123
|
-
if ($previousEl) {
|
|
124
|
-
markEl($previousEl);
|
|
125
|
-
setAttrs($previousEl, false);
|
|
126
|
-
return $previousEl;
|
|
127
|
-
}
|
|
128
|
-
switch (tag.tagPosition) {
|
|
129
|
-
case "bodyClose":
|
|
130
|
-
$newEl = dom.body.appendChild($newEl);
|
|
131
|
-
break;
|
|
132
|
-
case "bodyOpen":
|
|
133
|
-
$newEl = dom.body.insertBefore($newEl, dom.body.firstChild);
|
|
134
|
-
break;
|
|
135
|
-
case "head":
|
|
136
|
-
default:
|
|
137
|
-
$newEl = dom.head.appendChild($newEl);
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
markEl($newEl);
|
|
141
|
-
return $newEl;
|
|
142
|
-
};
|
|
143
|
-
for (const tag of await head.resolveTags()) {
|
|
144
|
-
const entry = head.headEntries().find((e) => e._i === tag._e);
|
|
145
|
-
const renderCtx = { $el: null, shouldRender: true, tag, entry, queuedSideEffects: staleSideEffects };
|
|
146
|
-
await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
|
|
147
|
-
if (!renderCtx.shouldRender)
|
|
148
|
-
continue;
|
|
149
|
-
renderCtx.$el = renderTag(renderCtx.tag, renderCtx.entry);
|
|
150
|
-
await head.hooks.callHook("dom:renderTag", renderCtx);
|
|
151
|
-
}
|
|
152
|
-
Object.values(staleSideEffects).forEach((fn) => fn());
|
|
153
|
-
}
|
|
154
|
-
exports.domUpdatePromise = null;
|
|
155
|
-
async function debouncedRenderDOMHead(head, options = {}) {
|
|
156
|
-
function doDomUpdate() {
|
|
157
|
-
exports.domUpdatePromise = null;
|
|
158
|
-
return renderDOMHead(head, options);
|
|
159
|
-
}
|
|
160
|
-
const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
|
|
161
|
-
return exports.domUpdatePromise = exports.domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
exports.debouncedRenderDOMHead = debouncedRenderDOMHead;
|
|
165
|
-
exports.renderDOMHead = renderDOMHead;
|
package/dist/chunks/index.mjs
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
const TagsWithInnerContent = ["script", "style", "noscript"];
|
|
2
|
-
|
|
3
|
-
const UniqueTags = ["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"];
|
|
4
|
-
const ArrayMetaProperties = [
|
|
5
|
-
"og:image",
|
|
6
|
-
"og:video",
|
|
7
|
-
"og:audio",
|
|
8
|
-
"og:locale:alternate",
|
|
9
|
-
"video:actor",
|
|
10
|
-
"video:director",
|
|
11
|
-
"video:writer",
|
|
12
|
-
"video:tag",
|
|
13
|
-
"article:author",
|
|
14
|
-
"article:tag",
|
|
15
|
-
"book:tag",
|
|
16
|
-
"book:author",
|
|
17
|
-
"music:album",
|
|
18
|
-
"music:musician"
|
|
19
|
-
];
|
|
20
|
-
function tagDedupeKey(tag) {
|
|
21
|
-
const { props, tag: tagName } = tag;
|
|
22
|
-
if (UniqueTags.includes(tagName))
|
|
23
|
-
return tagName;
|
|
24
|
-
if (tagName === "link" && props.rel === "canonical")
|
|
25
|
-
return "canonical";
|
|
26
|
-
if (props.charset)
|
|
27
|
-
return "charset";
|
|
28
|
-
const name = ["id"];
|
|
29
|
-
if (tagName === "meta")
|
|
30
|
-
name.push(...["name", "property", "http-equiv"]);
|
|
31
|
-
for (const n of name) {
|
|
32
|
-
if (typeof props[n] !== "undefined") {
|
|
33
|
-
const val = String(props[n]);
|
|
34
|
-
if (ArrayMetaProperties.findIndex((p) => val.startsWith(p)) !== -1)
|
|
35
|
-
return false;
|
|
36
|
-
return `${tagName}:${n}:${val}`;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function renderDOMHead(head, options = {}) {
|
|
43
|
-
const ctx = { shouldRender: true };
|
|
44
|
-
await head.hooks.callHook("dom:beforeRender", ctx);
|
|
45
|
-
if (!ctx.shouldRender)
|
|
46
|
-
return;
|
|
47
|
-
const dom = options.document || window.document;
|
|
48
|
-
const staleSideEffects = head._popSideEffectQueue();
|
|
49
|
-
head.headEntries().map((entry) => entry._sde).forEach((sde) => {
|
|
50
|
-
Object.entries(sde).forEach(([key, fn]) => {
|
|
51
|
-
staleSideEffects[key] = fn;
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
const renderTag = (tag, entry) => {
|
|
55
|
-
if (tag.tag === "title" && tag.children) {
|
|
56
|
-
dom.title = tag.children;
|
|
57
|
-
return dom.head.querySelector("title");
|
|
58
|
-
}
|
|
59
|
-
const tagRenderId = tag._d || tag._p;
|
|
60
|
-
const markSideEffect = (key, fn) => {
|
|
61
|
-
key = `${tagRenderId}:${key}`;
|
|
62
|
-
if (entry)
|
|
63
|
-
entry._sde[key] = fn;
|
|
64
|
-
delete staleSideEffects[key];
|
|
65
|
-
};
|
|
66
|
-
const setAttrs = ($el, sideEffects = true) => {
|
|
67
|
-
Object.entries(tag.props).forEach(([k, value]) => {
|
|
68
|
-
value = String(value);
|
|
69
|
-
const attrSdeKey = `attr:${k}`;
|
|
70
|
-
if (k === "class") {
|
|
71
|
-
for (const c of value.split(" ")) {
|
|
72
|
-
const classSdeKey = `${attrSdeKey}:${c}`;
|
|
73
|
-
sideEffects && markSideEffect(classSdeKey, () => $el.classList.remove(c));
|
|
74
|
-
if (!$el.classList.contains(c))
|
|
75
|
-
$el.classList.add(c);
|
|
76
|
-
}
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (sideEffects && !k.startsWith("data-h-"))
|
|
80
|
-
markSideEffect(attrSdeKey, () => $el.removeAttribute(k));
|
|
81
|
-
if ($el.getAttribute(k) !== value)
|
|
82
|
-
$el.setAttribute(k, value);
|
|
83
|
-
});
|
|
84
|
-
if (TagsWithInnerContent.includes(tag.tag) && $el.innerHTML !== (tag.children || ""))
|
|
85
|
-
$el.innerHTML = tag.children || "";
|
|
86
|
-
};
|
|
87
|
-
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
|
|
88
|
-
const $el = dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"];
|
|
89
|
-
setAttrs($el);
|
|
90
|
-
return $el;
|
|
91
|
-
}
|
|
92
|
-
let $newEl = dom.createElement(tag.tag);
|
|
93
|
-
setAttrs($newEl, false);
|
|
94
|
-
let $previousEl = head._elMap[tagRenderId];
|
|
95
|
-
const $target = dom[tag.tagPosition?.startsWith("body") ? "body" : "head"];
|
|
96
|
-
if (!$previousEl && tag._hash) {
|
|
97
|
-
$previousEl = $target.querySelector(`${tag.tag}[data-h-${tag._hash}]`);
|
|
98
|
-
}
|
|
99
|
-
if (!$previousEl) {
|
|
100
|
-
for (const $el of tag.tagPosition === "bodyClose" ? [...$target.children].reverse() : $target.children) {
|
|
101
|
-
const elTag = $el.tagName.toLowerCase();
|
|
102
|
-
if (elTag !== tag.tag)
|
|
103
|
-
continue;
|
|
104
|
-
const key = tagDedupeKey({
|
|
105
|
-
tag: elTag,
|
|
106
|
-
props: $el.getAttributeNames().reduce((props, name) => ({ ...props, [name]: $el.getAttribute(name) }), {})
|
|
107
|
-
});
|
|
108
|
-
if (key === tag._d || $el.isEqualNode($newEl)) {
|
|
109
|
-
$previousEl = $el;
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
const markEl = ($el) => {
|
|
115
|
-
head._elMap[tagRenderId] = $el;
|
|
116
|
-
markSideEffect("el", () => {
|
|
117
|
-
$el?.remove();
|
|
118
|
-
delete head._elMap[tagRenderId];
|
|
119
|
-
});
|
|
120
|
-
};
|
|
121
|
-
if ($previousEl) {
|
|
122
|
-
markEl($previousEl);
|
|
123
|
-
setAttrs($previousEl, false);
|
|
124
|
-
return $previousEl;
|
|
125
|
-
}
|
|
126
|
-
switch (tag.tagPosition) {
|
|
127
|
-
case "bodyClose":
|
|
128
|
-
$newEl = dom.body.appendChild($newEl);
|
|
129
|
-
break;
|
|
130
|
-
case "bodyOpen":
|
|
131
|
-
$newEl = dom.body.insertBefore($newEl, dom.body.firstChild);
|
|
132
|
-
break;
|
|
133
|
-
case "head":
|
|
134
|
-
default:
|
|
135
|
-
$newEl = dom.head.appendChild($newEl);
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
138
|
-
markEl($newEl);
|
|
139
|
-
return $newEl;
|
|
140
|
-
};
|
|
141
|
-
for (const tag of await head.resolveTags()) {
|
|
142
|
-
const entry = head.headEntries().find((e) => e._i === tag._e);
|
|
143
|
-
const renderCtx = { $el: null, shouldRender: true, tag, entry, queuedSideEffects: staleSideEffects };
|
|
144
|
-
await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
|
|
145
|
-
if (!renderCtx.shouldRender)
|
|
146
|
-
continue;
|
|
147
|
-
renderCtx.$el = renderTag(renderCtx.tag, renderCtx.entry);
|
|
148
|
-
await head.hooks.callHook("dom:renderTag", renderCtx);
|
|
149
|
-
}
|
|
150
|
-
Object.values(staleSideEffects).forEach((fn) => fn());
|
|
151
|
-
}
|
|
152
|
-
let domUpdatePromise = null;
|
|
153
|
-
async function debouncedRenderDOMHead(head, options = {}) {
|
|
154
|
-
function doDomUpdate() {
|
|
155
|
-
domUpdatePromise = null;
|
|
156
|
-
return renderDOMHead(head, options);
|
|
157
|
-
}
|
|
158
|
-
const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
|
|
159
|
-
return domUpdatePromise = domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export { debouncedRenderDOMHead, domUpdatePromise, renderDOMHead };
|