@unhead/dom 1.9.16 → 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 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.includes(tag.tag) ? shared.hashTag(tag) : tag.tag,
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 tags2 = [];
26
- for (const c of [...children].filter((c2) => shared.HasElementTags.includes(c2.tagName.toLowerCase()))) {
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: c.tagName.toLowerCase(),
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
- let d = shared.tagDedupeKey(t);
36
- while (d && tags2.find((t2) => t2._d === d))
37
- d = `${d}:${i++}`;
38
- t._d = d || void 0;
39
- tags2.push(t);
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
- ["textContent", "innerHTML"].forEach((k) => {
56
- tag[k] && tag[k] !== $el[k] && ($el[k] = tag[k]);
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
- for (const [k, value] of Object.entries(tag._eventHandlers || {})) {
64
- if ($el.getAttribute(`data-${k}`) !== "") {
65
- (tag.tag === "bodyAttrs" ? dom.defaultView : $el).addEventListener(k.replace("on", ""), value.bind($el));
66
- $el.setAttribute(`data-${k}`, "");
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
- Object.entries(tag.props).forEach(([k, value]) => {
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
- for (const c of (value || "").split(" ").filter(Boolean)) {
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
- for (const c of (value || "").split(";").filter(Boolean)) {
78
- const [k2, ...v] = c.split(":").map((s) => s.trim());
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.join(":"));
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
- shared.HasElementTags.includes(tag.tag) && pending.push(ctx);
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
- Object.values(state.pendingSideEffects).forEach((fn) => fn());
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
- async function debouncedRenderDOMHead(head, options = {}) {
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(async () => {
131
- await renderDOMHead(head, options);
132
- delete head._domUpdatePromise;
133
- resolve();
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": function(head2) {
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.includes(tag.tag) ? hashTag(tag) : tag.tag,
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 tags2 = [];
24
- for (const c of [...children].filter((c2) => HasElementTags.includes(c2.tagName.toLowerCase()))) {
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: c.tagName.toLowerCase(),
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
- let d = tagDedupeKey(t);
34
- while (d && tags2.find((t2) => t2._d === d))
35
- d = `${d}:${i++}`;
36
- t._d = d || void 0;
37
- tags2.push(t);
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
- ["textContent", "innerHTML"].forEach((k) => {
54
- tag[k] && tag[k] !== $el[k] && ($el[k] = tag[k]);
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
- for (const [k, value] of Object.entries(tag._eventHandlers || {})) {
62
- if ($el.getAttribute(`data-${k}`) !== "") {
63
- (tag.tag === "bodyAttrs" ? dom.defaultView : $el).addEventListener(k.replace("on", ""), value.bind($el));
64
- $el.setAttribute(`data-${k}`, "");
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
- Object.entries(tag.props).forEach(([k, value]) => {
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
- for (const c of (value || "").split(" ").filter(Boolean)) {
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
- for (const c of (value || "").split(";").filter(Boolean)) {
76
- const [k2, ...v] = c.split(":").map((s) => s.trim());
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.join(":"));
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
- HasElementTags.includes(tag.tag) && pending.push(ctx);
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
- Object.values(state.pendingSideEffects).forEach((fn) => fn());
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
- async function debouncedRenderDOMHead(head, options = {}) {
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(async () => {
129
- await renderDOMHead(head, options);
130
- delete head._domUpdatePromise;
131
- resolve();
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": function(head2) {
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.9.16",
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/shared": "1.9.16",
33
- "@unhead/schema": "1.9.16"
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 .",