@unhead/dom 1.9.15 → 1.10.0-beta.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 +68 -33
- package/dist/index.mjs +68 -33
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -12,7 +12,7 @@ async function renderDOMHead(head, options = {}) {
|
|
|
12
12
|
return;
|
|
13
13
|
const tags = (await head.resolveTags()).map((tag) => ({
|
|
14
14
|
tag,
|
|
15
|
-
id: shared.HasElementTags.
|
|
15
|
+
id: shared.HasElementTags.has(tag.tag) ? shared.hashTag(tag) : tag.tag,
|
|
16
16
|
shouldRender: true
|
|
17
17
|
}));
|
|
18
18
|
let state = head._dom;
|
|
@@ -20,28 +20,35 @@ async function renderDOMHead(head, options = {}) {
|
|
|
20
20
|
state = {
|
|
21
21
|
elMap: { htmlAttrs: dom.documentElement, bodyAttrs: dom.body }
|
|
22
22
|
};
|
|
23
|
+
const takenDedupeKeys = /* @__PURE__ */ new Set();
|
|
23
24
|
for (const key of ["body", "head"]) {
|
|
24
25
|
const children = dom[key]?.children;
|
|
25
|
-
const
|
|
26
|
-
|
|
26
|
+
for (const c of children) {
|
|
27
|
+
const tag = c.tagName.toLowerCase();
|
|
28
|
+
if (!shared.HasElementTags.has(tag)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
27
31
|
const t = {
|
|
28
|
-
tag
|
|
32
|
+
tag,
|
|
29
33
|
props: await shared.normaliseProps(
|
|
30
34
|
c.getAttributeNames().reduce((props, name) => ({ ...props, [name]: c.getAttribute(name) }), {})
|
|
31
35
|
),
|
|
32
36
|
innerHTML: c.innerHTML
|
|
33
37
|
};
|
|
38
|
+
const dedupeKey = shared.tagDedupeKey(t);
|
|
39
|
+
let d = dedupeKey;
|
|
34
40
|
let i = 1;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
while (d && takenDedupeKeys.has(d))
|
|
42
|
+
d = `${dedupeKey}:${i++}`;
|
|
43
|
+
if (d) {
|
|
44
|
+
t._d = d;
|
|
45
|
+
takenDedupeKeys.add(d);
|
|
46
|
+
}
|
|
40
47
|
state.elMap[c.getAttribute("data-hid") || shared.hashTag(t)] = c;
|
|
41
48
|
}
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
|
-
state.pendingSideEffects = { ...state.sideEffects
|
|
51
|
+
state.pendingSideEffects = { ...state.sideEffects };
|
|
45
52
|
state.sideEffects = {};
|
|
46
53
|
function track(id, scope, fn) {
|
|
47
54
|
const k = `${id}:${scope}`;
|
|
@@ -52,40 +59,64 @@ async function renderDOMHead(head, options = {}) {
|
|
|
52
59
|
const isAttrTag = tag.tag.endsWith("Attrs");
|
|
53
60
|
state.elMap[id] = $el;
|
|
54
61
|
if (!isAttrTag) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
62
|
+
if (tag.textContent && tag.textContent !== $el.textContent) {
|
|
63
|
+
$el.textContent = tag.textContent;
|
|
64
|
+
}
|
|
65
|
+
if (tag.innerHTML && tag.innerHTML !== $el.innerHTML) {
|
|
66
|
+
$el.innerHTML = tag.innerHTML;
|
|
67
|
+
}
|
|
58
68
|
track(id, "el", () => {
|
|
59
69
|
state.elMap[id]?.remove();
|
|
60
70
|
delete state.elMap[id];
|
|
61
71
|
});
|
|
62
72
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
(
|
|
66
|
-
|
|
73
|
+
if (tag._eventHandlers) {
|
|
74
|
+
for (const k in tag._eventHandlers) {
|
|
75
|
+
if (!Object.prototype.hasOwnProperty.call(tag._eventHandlers, k)) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if ($el.getAttribute(`data-${k}`) !== "") {
|
|
79
|
+
(tag.tag === "bodyAttrs" ? dom.defaultView : $el).addEventListener(
|
|
80
|
+
// onload -> load
|
|
81
|
+
k.substring(2),
|
|
82
|
+
tag._eventHandlers[k].bind($el)
|
|
83
|
+
);
|
|
84
|
+
$el.setAttribute(`data-${k}`, "");
|
|
85
|
+
}
|
|
67
86
|
}
|
|
68
87
|
}
|
|
69
|
-
|
|
88
|
+
for (const k in tag.props) {
|
|
89
|
+
if (!Object.prototype.hasOwnProperty.call(tag.props, k)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const value = tag.props[k];
|
|
70
93
|
const ck = `attr:${k}`;
|
|
71
94
|
if (k === "class") {
|
|
72
|
-
|
|
95
|
+
if (!value) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
for (const c of value.split(" ")) {
|
|
73
99
|
isAttrTag && track(id, `${ck}:${c}`, () => $el.classList.remove(c));
|
|
74
100
|
!$el.classList.contains(c) && $el.classList.add(c);
|
|
75
101
|
}
|
|
76
102
|
} else if (k === "style") {
|
|
77
|
-
|
|
78
|
-
|
|
103
|
+
if (!value) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
for (const c of value.split(";")) {
|
|
107
|
+
const propIndex = c.indexOf(":");
|
|
108
|
+
const k2 = c.substring(0, propIndex).trim();
|
|
109
|
+
const v = c.substring(propIndex + 1).trim();
|
|
79
110
|
track(id, `${ck}:${k2}`, () => {
|
|
80
111
|
$el.style.removeProperty(k2);
|
|
81
112
|
});
|
|
82
|
-
$el.style.setProperty(k2, v
|
|
113
|
+
$el.style.setProperty(k2, v);
|
|
83
114
|
}
|
|
84
115
|
} else {
|
|
85
116
|
$el.getAttribute(k) !== value && $el.setAttribute(k, value === true ? "" : String(value));
|
|
86
117
|
isAttrTag && track(id, ck, () => $el.removeAttribute(k));
|
|
87
118
|
}
|
|
88
|
-
}
|
|
119
|
+
}
|
|
89
120
|
}
|
|
90
121
|
const pending = [];
|
|
91
122
|
const frag = {
|
|
@@ -102,10 +133,11 @@ async function renderDOMHead(head, options = {}) {
|
|
|
102
133
|
continue;
|
|
103
134
|
}
|
|
104
135
|
ctx.$el = ctx.$el || state.elMap[id];
|
|
105
|
-
if (ctx.$el)
|
|
136
|
+
if (ctx.$el) {
|
|
106
137
|
trackCtx(ctx);
|
|
107
|
-
else
|
|
108
|
-
|
|
138
|
+
} else if (shared.HasElementTags.has(tag.tag)) {
|
|
139
|
+
pending.push(ctx);
|
|
140
|
+
}
|
|
109
141
|
}
|
|
110
142
|
for (const ctx of pending) {
|
|
111
143
|
const pos = ctx.tag.tagPosition || "head";
|
|
@@ -119,18 +151,21 @@ async function renderDOMHead(head, options = {}) {
|
|
|
119
151
|
frag.head && dom.head.appendChild(frag.head);
|
|
120
152
|
frag.bodyOpen && dom.body.insertBefore(frag.bodyOpen, dom.body.firstChild);
|
|
121
153
|
frag.bodyClose && dom.body.appendChild(frag.bodyClose);
|
|
122
|
-
|
|
154
|
+
for (const k in state.pendingSideEffects) {
|
|
155
|
+
state.pendingSideEffects[k]();
|
|
156
|
+
}
|
|
123
157
|
head._dom = state;
|
|
124
158
|
head.dirty = false;
|
|
125
159
|
await head.hooks.callHook("dom:rendered", { renders: tags });
|
|
126
160
|
}
|
|
127
161
|
|
|
128
|
-
|
|
162
|
+
function debouncedRenderDOMHead(head, options = {}) {
|
|
129
163
|
const fn = options.delayFn || ((fn2) => setTimeout(fn2, 10));
|
|
130
|
-
return head._domUpdatePromise = head._domUpdatePromise || new Promise((resolve) => fn(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
164
|
+
return head._domUpdatePromise = head._domUpdatePromise || new Promise((resolve) => fn(() => {
|
|
165
|
+
return renderDOMHead(head, options).then(() => {
|
|
166
|
+
delete head._domUpdatePromise;
|
|
167
|
+
resolve();
|
|
168
|
+
});
|
|
134
169
|
}));
|
|
135
170
|
}
|
|
136
171
|
|
|
@@ -142,7 +177,7 @@ function DomPlugin(options) {
|
|
|
142
177
|
return {
|
|
143
178
|
mode: "client",
|
|
144
179
|
hooks: {
|
|
145
|
-
"entries:updated":
|
|
180
|
+
"entries:updated": (head2) => {
|
|
146
181
|
debouncedRenderDOMHead(head2, options);
|
|
147
182
|
}
|
|
148
183
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ async function renderDOMHead(head, options = {}) {
|
|
|
10
10
|
return;
|
|
11
11
|
const tags = (await head.resolveTags()).map((tag) => ({
|
|
12
12
|
tag,
|
|
13
|
-
id: HasElementTags.
|
|
13
|
+
id: HasElementTags.has(tag.tag) ? hashTag(tag) : tag.tag,
|
|
14
14
|
shouldRender: true
|
|
15
15
|
}));
|
|
16
16
|
let state = head._dom;
|
|
@@ -18,28 +18,35 @@ async function renderDOMHead(head, options = {}) {
|
|
|
18
18
|
state = {
|
|
19
19
|
elMap: { htmlAttrs: dom.documentElement, bodyAttrs: dom.body }
|
|
20
20
|
};
|
|
21
|
+
const takenDedupeKeys = /* @__PURE__ */ new Set();
|
|
21
22
|
for (const key of ["body", "head"]) {
|
|
22
23
|
const children = dom[key]?.children;
|
|
23
|
-
const
|
|
24
|
-
|
|
24
|
+
for (const c of children) {
|
|
25
|
+
const tag = c.tagName.toLowerCase();
|
|
26
|
+
if (!HasElementTags.has(tag)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
25
29
|
const t = {
|
|
26
|
-
tag
|
|
30
|
+
tag,
|
|
27
31
|
props: await normaliseProps(
|
|
28
32
|
c.getAttributeNames().reduce((props, name) => ({ ...props, [name]: c.getAttribute(name) }), {})
|
|
29
33
|
),
|
|
30
34
|
innerHTML: c.innerHTML
|
|
31
35
|
};
|
|
36
|
+
const dedupeKey = tagDedupeKey(t);
|
|
37
|
+
let d = dedupeKey;
|
|
32
38
|
let i = 1;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
while (d && takenDedupeKeys.has(d))
|
|
40
|
+
d = `${dedupeKey}:${i++}`;
|
|
41
|
+
if (d) {
|
|
42
|
+
t._d = d;
|
|
43
|
+
takenDedupeKeys.add(d);
|
|
44
|
+
}
|
|
38
45
|
state.elMap[c.getAttribute("data-hid") || hashTag(t)] = c;
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
}
|
|
42
|
-
state.pendingSideEffects = { ...state.sideEffects
|
|
49
|
+
state.pendingSideEffects = { ...state.sideEffects };
|
|
43
50
|
state.sideEffects = {};
|
|
44
51
|
function track(id, scope, fn) {
|
|
45
52
|
const k = `${id}:${scope}`;
|
|
@@ -50,40 +57,64 @@ async function renderDOMHead(head, options = {}) {
|
|
|
50
57
|
const isAttrTag = tag.tag.endsWith("Attrs");
|
|
51
58
|
state.elMap[id] = $el;
|
|
52
59
|
if (!isAttrTag) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
60
|
+
if (tag.textContent && tag.textContent !== $el.textContent) {
|
|
61
|
+
$el.textContent = tag.textContent;
|
|
62
|
+
}
|
|
63
|
+
if (tag.innerHTML && tag.innerHTML !== $el.innerHTML) {
|
|
64
|
+
$el.innerHTML = tag.innerHTML;
|
|
65
|
+
}
|
|
56
66
|
track(id, "el", () => {
|
|
57
67
|
state.elMap[id]?.remove();
|
|
58
68
|
delete state.elMap[id];
|
|
59
69
|
});
|
|
60
70
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
(
|
|
64
|
-
|
|
71
|
+
if (tag._eventHandlers) {
|
|
72
|
+
for (const k in tag._eventHandlers) {
|
|
73
|
+
if (!Object.prototype.hasOwnProperty.call(tag._eventHandlers, k)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if ($el.getAttribute(`data-${k}`) !== "") {
|
|
77
|
+
(tag.tag === "bodyAttrs" ? dom.defaultView : $el).addEventListener(
|
|
78
|
+
// onload -> load
|
|
79
|
+
k.substring(2),
|
|
80
|
+
tag._eventHandlers[k].bind($el)
|
|
81
|
+
);
|
|
82
|
+
$el.setAttribute(`data-${k}`, "");
|
|
83
|
+
}
|
|
65
84
|
}
|
|
66
85
|
}
|
|
67
|
-
|
|
86
|
+
for (const k in tag.props) {
|
|
87
|
+
if (!Object.prototype.hasOwnProperty.call(tag.props, k)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const value = tag.props[k];
|
|
68
91
|
const ck = `attr:${k}`;
|
|
69
92
|
if (k === "class") {
|
|
70
|
-
|
|
93
|
+
if (!value) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
for (const c of value.split(" ")) {
|
|
71
97
|
isAttrTag && track(id, `${ck}:${c}`, () => $el.classList.remove(c));
|
|
72
98
|
!$el.classList.contains(c) && $el.classList.add(c);
|
|
73
99
|
}
|
|
74
100
|
} else if (k === "style") {
|
|
75
|
-
|
|
76
|
-
|
|
101
|
+
if (!value) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
for (const c of value.split(";")) {
|
|
105
|
+
const propIndex = c.indexOf(":");
|
|
106
|
+
const k2 = c.substring(0, propIndex).trim();
|
|
107
|
+
const v = c.substring(propIndex + 1).trim();
|
|
77
108
|
track(id, `${ck}:${k2}`, () => {
|
|
78
109
|
$el.style.removeProperty(k2);
|
|
79
110
|
});
|
|
80
|
-
$el.style.setProperty(k2, v
|
|
111
|
+
$el.style.setProperty(k2, v);
|
|
81
112
|
}
|
|
82
113
|
} else {
|
|
83
114
|
$el.getAttribute(k) !== value && $el.setAttribute(k, value === true ? "" : String(value));
|
|
84
115
|
isAttrTag && track(id, ck, () => $el.removeAttribute(k));
|
|
85
116
|
}
|
|
86
|
-
}
|
|
117
|
+
}
|
|
87
118
|
}
|
|
88
119
|
const pending = [];
|
|
89
120
|
const frag = {
|
|
@@ -100,10 +131,11 @@ async function renderDOMHead(head, options = {}) {
|
|
|
100
131
|
continue;
|
|
101
132
|
}
|
|
102
133
|
ctx.$el = ctx.$el || state.elMap[id];
|
|
103
|
-
if (ctx.$el)
|
|
134
|
+
if (ctx.$el) {
|
|
104
135
|
trackCtx(ctx);
|
|
105
|
-
else
|
|
106
|
-
|
|
136
|
+
} else if (HasElementTags.has(tag.tag)) {
|
|
137
|
+
pending.push(ctx);
|
|
138
|
+
}
|
|
107
139
|
}
|
|
108
140
|
for (const ctx of pending) {
|
|
109
141
|
const pos = ctx.tag.tagPosition || "head";
|
|
@@ -117,18 +149,21 @@ async function renderDOMHead(head, options = {}) {
|
|
|
117
149
|
frag.head && dom.head.appendChild(frag.head);
|
|
118
150
|
frag.bodyOpen && dom.body.insertBefore(frag.bodyOpen, dom.body.firstChild);
|
|
119
151
|
frag.bodyClose && dom.body.appendChild(frag.bodyClose);
|
|
120
|
-
|
|
152
|
+
for (const k in state.pendingSideEffects) {
|
|
153
|
+
state.pendingSideEffects[k]();
|
|
154
|
+
}
|
|
121
155
|
head._dom = state;
|
|
122
156
|
head.dirty = false;
|
|
123
157
|
await head.hooks.callHook("dom:rendered", { renders: tags });
|
|
124
158
|
}
|
|
125
159
|
|
|
126
|
-
|
|
160
|
+
function debouncedRenderDOMHead(head, options = {}) {
|
|
127
161
|
const fn = options.delayFn || ((fn2) => setTimeout(fn2, 10));
|
|
128
|
-
return head._domUpdatePromise = head._domUpdatePromise || new Promise((resolve) => fn(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
162
|
+
return head._domUpdatePromise = head._domUpdatePromise || new Promise((resolve) => fn(() => {
|
|
163
|
+
return renderDOMHead(head, options).then(() => {
|
|
164
|
+
delete head._domUpdatePromise;
|
|
165
|
+
resolve();
|
|
166
|
+
});
|
|
132
167
|
}));
|
|
133
168
|
}
|
|
134
169
|
|
|
@@ -140,7 +175,7 @@ function DomPlugin(options) {
|
|
|
140
175
|
return {
|
|
141
176
|
mode: "client",
|
|
142
177
|
hooks: {
|
|
143
|
-
"entries:updated":
|
|
178
|
+
"entries:updated": (head2) => {
|
|
144
179
|
debouncedRenderDOMHead(head2, options);
|
|
145
180
|
}
|
|
146
181
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unhead/dom",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.10.0-beta.1",
|
|
5
5
|
"author": "Harlan Wilton <harlan@harlanzw.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": "https://github.com/sponsors/harlan-zw",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@unhead/
|
|
33
|
-
"@unhead/
|
|
32
|
+
"@unhead/shared": "1.10.0-beta.1",
|
|
33
|
+
"@unhead/schema": "1.10.0-beta.1"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "unbuild .",
|