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