@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 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
- const tags = (await head.resolveTags()).map((tag) => ({
14
- tag,
15
- id: shared.HasElementTags.has(tag.tag) ? shared.hashTag(tag) : tag.tag,
16
- shouldRender: true
17
- }));
18
- let state = head._dom;
19
- if (!state) {
20
- state = {
21
- elMap: { htmlAttrs: dom.documentElement, bodyAttrs: dom.body }
22
- };
23
- const takenDedupeKeys = /* @__PURE__ */ new Set();
24
- for (const key of ["body", "head"]) {
25
- const children = dom[key]?.children;
26
- for (const c of children) {
27
- const tag = c.tagName.toLowerCase();
28
- if (!shared.HasElementTags.has(tag)) {
29
- continue;
30
- }
31
- const t = {
32
- tag,
33
- props: await shared.normaliseProps(
34
- c.getAttributeNames().reduce((props, name) => ({ ...props, [name]: c.getAttribute(name) }), {})
35
- ),
36
- innerHTML: c.innerHTML
37
- };
38
- const dedupeKey = shared.tagDedupeKey(t);
39
- let d = dedupeKey;
40
- let i = 1;
41
- while (d && takenDedupeKeys.has(d))
42
- d = `${dedupeKey}:${i++}`;
43
- if (d) {
44
- t._d = d;
45
- takenDedupeKeys.add(d);
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
- state.pendingSideEffects = { ...state.sideEffects };
52
- state.sideEffects = {};
53
- function track(id, scope, fn) {
54
- const k = `${id}:${scope}`;
55
- state.sideEffects[k] = fn;
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
- if (tag._eventHandlers) {
74
- for (const k in tag._eventHandlers) {
75
- if (!Object.prototype.hasOwnProperty.call(tag._eventHandlers, k)) {
76
- continue;
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 ($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}`, "");
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
- for (const k in tag.props) {
89
- if (!Object.prototype.hasOwnProperty.call(tag.props, k)) {
90
- continue;
91
- }
92
- const value = tag.props[k];
93
- const ck = `attr:${k}`;
94
- if (k === "class") {
95
- if (!value) {
96
- continue;
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
- for (const c of value.split(" ")) {
99
- isAttrTag && track(id, `${ck}:${c}`, () => $el.classList.remove(c));
100
- !$el.classList.contains(c) && $el.classList.add(c);
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
- 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();
110
- track(id, `${ck}:${k2}`, () => {
111
- $el.style.removeProperty(k2);
112
- });
113
- $el.style.setProperty(k2, v);
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
- const pending = [];
122
- const frag = {
123
- bodyClose: void 0,
124
- bodyOpen: void 0,
125
- head: void 0
126
- };
127
- for (const ctx of tags) {
128
- const { tag, shouldRender, id } = ctx;
129
- if (!shouldRender)
130
- continue;
131
- if (tag.tag === "title") {
132
- dom.title = tag.textContent;
133
- continue;
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
- ctx.$el = ctx.$el || state.elMap[id];
136
- if (ctx.$el) {
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
- } else if (shared.HasElementTags.has(tag.tag)) {
139
- pending.push(ctx);
150
+ frag[pos] = frag[pos] || dom.createDocumentFragment();
151
+ frag[pos].appendChild(ctx.$el);
140
152
  }
141
- }
142
- for (const ctx of pending) {
143
- const pos = ctx.tag.tagPosition || "head";
144
- ctx.$el = dom.createElement(ctx.tag.tag);
145
- trackCtx(ctx);
146
- frag[pos] = frag[pos] || dom.createDocumentFragment();
147
- frag[pos].appendChild(ctx.$el);
148
- }
149
- for (const ctx of tags)
150
- await head.hooks.callHook("dom:renderTag", ctx, dom, track);
151
- frag.head && dom.head.appendChild(frag.head);
152
- frag.bodyOpen && dom.body.insertBefore(frag.bodyOpen, dom.body.firstChild);
153
- frag.bodyClose && dom.body.appendChild(frag.bodyClose);
154
- for (const k in state.pendingSideEffects) {
155
- state.pendingSideEffects[k]();
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._domUpdatePromise = head._domUpdatePromise || new Promise((resolve) => fn(() => {
173
+ return head._domDebouncedUpdatePromise = head._domDebouncedUpdatePromise || new Promise((resolve) => fn(() => {
165
174
  return renderDOMHead(head, options).then(() => {
166
- delete head._domUpdatePromise;
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
- initialPayload && head.push(JSON.parse(initialPayload));
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
- const tags = (await head.resolveTags()).map((tag) => ({
12
- tag,
13
- id: HasElementTags.has(tag.tag) ? hashTag(tag) : tag.tag,
14
- shouldRender: true
15
- }));
16
- let state = head._dom;
17
- if (!state) {
18
- state = {
19
- elMap: { htmlAttrs: dom.documentElement, bodyAttrs: dom.body }
20
- };
21
- const takenDedupeKeys = /* @__PURE__ */ new Set();
22
- for (const key of ["body", "head"]) {
23
- const children = dom[key]?.children;
24
- for (const c of children) {
25
- const tag = c.tagName.toLowerCase();
26
- if (!HasElementTags.has(tag)) {
27
- continue;
28
- }
29
- const t = {
30
- tag,
31
- props: await normaliseProps(
32
- c.getAttributeNames().reduce((props, name) => ({ ...props, [name]: c.getAttribute(name) }), {})
33
- ),
34
- innerHTML: c.innerHTML
35
- };
36
- const dedupeKey = tagDedupeKey(t);
37
- let d = dedupeKey;
38
- let i = 1;
39
- while (d && takenDedupeKeys.has(d))
40
- d = `${dedupeKey}:${i++}`;
41
- if (d) {
42
- t._d = d;
43
- takenDedupeKeys.add(d);
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
- state.pendingSideEffects = { ...state.sideEffects };
50
- state.sideEffects = {};
51
- function track(id, scope, fn) {
52
- const k = `${id}:${scope}`;
53
- state.sideEffects[k] = fn;
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
- if (tag._eventHandlers) {
72
- for (const k in tag._eventHandlers) {
73
- if (!Object.prototype.hasOwnProperty.call(tag._eventHandlers, k)) {
74
- continue;
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 ($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}`, "");
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
- for (const k in tag.props) {
87
- if (!Object.prototype.hasOwnProperty.call(tag.props, k)) {
88
- continue;
89
- }
90
- const value = tag.props[k];
91
- const ck = `attr:${k}`;
92
- if (k === "class") {
93
- if (!value) {
94
- continue;
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
- for (const c of value.split(" ")) {
97
- isAttrTag && track(id, `${ck}:${c}`, () => $el.classList.remove(c));
98
- !$el.classList.contains(c) && $el.classList.add(c);
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
- 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();
108
- track(id, `${ck}:${k2}`, () => {
109
- $el.style.removeProperty(k2);
110
- });
111
- $el.style.setProperty(k2, v);
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
- const pending = [];
120
- const frag = {
121
- bodyClose: void 0,
122
- bodyOpen: void 0,
123
- head: void 0
124
- };
125
- for (const ctx of tags) {
126
- const { tag, shouldRender, id } = ctx;
127
- if (!shouldRender)
128
- continue;
129
- if (tag.tag === "title") {
130
- dom.title = tag.textContent;
131
- continue;
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
- ctx.$el = ctx.$el || state.elMap[id];
134
- if (ctx.$el) {
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
- } else if (HasElementTags.has(tag.tag)) {
137
- pending.push(ctx);
148
+ frag[pos] = frag[pos] || dom.createDocumentFragment();
149
+ frag[pos].appendChild(ctx.$el);
138
150
  }
139
- }
140
- for (const ctx of pending) {
141
- const pos = ctx.tag.tagPosition || "head";
142
- ctx.$el = dom.createElement(ctx.tag.tag);
143
- trackCtx(ctx);
144
- frag[pos] = frag[pos] || dom.createDocumentFragment();
145
- frag[pos].appendChild(ctx.$el);
146
- }
147
- for (const ctx of tags)
148
- await head.hooks.callHook("dom:renderTag", ctx, dom, track);
149
- frag.head && dom.head.appendChild(frag.head);
150
- frag.bodyOpen && dom.body.insertBefore(frag.bodyOpen, dom.body.firstChild);
151
- frag.bodyClose && dom.body.appendChild(frag.bodyClose);
152
- for (const k in state.pendingSideEffects) {
153
- state.pendingSideEffects[k]();
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._domUpdatePromise = head._domUpdatePromise || new Promise((resolve) => fn(() => {
171
+ return head._domDebouncedUpdatePromise = head._domDebouncedUpdatePromise || new Promise((resolve) => fn(() => {
163
172
  return renderDOMHead(head, options).then(() => {
164
- delete head._domUpdatePromise;
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
- initialPayload && head.push(JSON.parse(initialPayload));
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.10.4",
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.10.4",
33
- "@unhead/shared": "1.10.4"
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 .",