@unhead/dom 0.2.7 → 0.3.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
@@ -11,97 +11,95 @@ const createElement = (tag, document) => {
11
11
  return $el;
12
12
  };
13
13
 
14
- function setAttributesWithSideEffects(head, $el, entry, tag) {
15
- const attrs = tag.props || {};
16
- const sdeKey = `${tag._p}:attr`;
17
- const sdeToRun = { ...entry._sde };
18
- Object.entries(attrs).forEach(([k, value]) => {
19
- value = String(value);
20
- const attrSdeKey = `${sdeKey}:${k}`;
21
- head._removeQueuedSideEffect(attrSdeKey);
22
- delete sdeToRun[attrSdeKey];
23
- if (k === "class") {
24
- for (const c of value.split(" ")) {
25
- const classSdeKey = `${sdeKey}:class:${c}`;
26
- if (!$el.classList.contains(c)) {
27
- $el.classList.add(c);
28
- entry._sde[classSdeKey] = () => $el.classList.remove(c);
29
- }
30
- head._removeQueuedSideEffect(classSdeKey);
31
- delete sdeToRun[classSdeKey];
32
- }
33
- return;
34
- }
35
- if ($el.getAttribute(k) !== value) {
36
- $el.setAttribute(k, value);
37
- if (!k.startsWith("data-h-"))
38
- entry._sde[attrSdeKey] = () => $el.removeAttribute(k);
39
- }
40
- });
41
- Object.entries(sdeToRun).filter(([key]) => key.startsWith(sdeKey)).forEach(([key, fn]) => {
42
- delete entry._sde[key] && fn();
43
- });
44
- }
45
-
46
14
  async function renderDOMHead(head, options = {}) {
47
15
  const dom = options.document || window.document;
48
16
  const tags = await head.resolveTags();
49
- const context = { shouldRender: true, tags };
50
- await head.hooks.callHook("dom:beforeRender", context);
51
- if (!context.shouldRender)
17
+ const ctx = { shouldRender: true, tags };
18
+ await head.hooks.callHook("dom:beforeRender", ctx);
19
+ if (!ctx.shouldRender)
52
20
  return;
53
- for (const tag of context.tags) {
54
- const renderCtx = { shouldRender: true, tag };
55
- await head.hooks.callHook("dom:renderTag", renderCtx);
56
- if (!renderCtx.shouldRender)
57
- return;
58
- const entry = head.headEntries().find((e) => e._i === Number(tag._e));
21
+ const queuedSideEffects = head._popSideEffectQueue();
22
+ head.headEntries().map((entry) => entry._sde).forEach((sde) => {
23
+ Object.entries(sde).forEach(([key, fn]) => {
24
+ queuedSideEffects[key] = fn;
25
+ });
26
+ });
27
+ const renderTag = (tag, entry) => {
59
28
  if (tag.tag === "title" && tag.children) {
60
29
  dom.title = tag.children;
61
- continue;
30
+ return;
62
31
  }
32
+ const markSideEffect = (key, fn) => {
33
+ key = `${tag._s || tag._p}:${key}`;
34
+ entry._sde[key] = fn;
35
+ delete queuedSideEffects[key];
36
+ };
37
+ const setAttrs = ($el) => {
38
+ Object.entries(tag.props).forEach(([k, value]) => {
39
+ value = String(value);
40
+ const attrSdeKey = `attr:${k}`;
41
+ if (k === "class") {
42
+ for (const c of value.split(" ")) {
43
+ const classSdeKey = `${attrSdeKey}:${c}`;
44
+ markSideEffect(classSdeKey, () => $el.classList.remove(c));
45
+ if (!$el.classList.contains(c))
46
+ $el.classList.add(c);
47
+ }
48
+ return;
49
+ }
50
+ if (!k.startsWith("data-h-"))
51
+ markSideEffect(attrSdeKey, () => $el.removeAttribute(k));
52
+ if ($el.getAttribute(k) !== value)
53
+ $el.setAttribute(k, value);
54
+ });
55
+ };
63
56
  if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
64
- setAttributesWithSideEffects(head, dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"], entry, tag);
65
- continue;
57
+ setAttrs(dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"]);
58
+ return;
66
59
  }
67
- const sdeKey = `${tag._s || tag._p}:el`;
68
- const $newEl = createElement(tag, dom);
69
- let $previousEl = null;
60
+ let $newEl = createElement(tag, dom);
61
+ let $previousEl;
70
62
  for (const $el of dom[tag.tagPosition?.startsWith("body") ? "body" : "head"].children) {
71
- if ($el.hasAttribute(`${tag._s}`)) {
63
+ if ($el.hasAttribute(`${tag._s}`) || $el.isEqualNode($newEl)) {
72
64
  $previousEl = $el;
73
65
  break;
74
66
  }
75
67
  }
76
68
  if ($previousEl) {
77
- head._removeQueuedSideEffect(sdeKey);
78
- if ($newEl.isEqualNode($previousEl))
79
- continue;
69
+ markSideEffect("el", () => $previousEl?.remove());
80
70
  if (Object.keys(tag.props).length === 0) {
81
71
  $previousEl.remove();
82
- continue;
72
+ return;
83
73
  }
84
- setAttributesWithSideEffects(head, $previousEl, entry, tag);
74
+ if ($newEl.isEqualNode($previousEl))
75
+ return;
76
+ setAttrs($previousEl);
85
77
  if (TagsWithInnerContent.includes(tag.tag))
86
78
  $previousEl.innerHTML = tag.children || "";
87
- entry._sde[sdeKey] = () => $previousEl?.remove();
88
- continue;
79
+ return;
89
80
  }
90
81
  switch (tag.tagPosition) {
91
82
  case "bodyClose":
92
- dom.body.appendChild($newEl);
83
+ $newEl = dom.body.appendChild($newEl);
93
84
  break;
94
85
  case "bodyOpen":
95
- dom.body.insertBefore($newEl, dom.body.firstChild);
86
+ $newEl = dom.body.insertBefore($newEl, dom.body.firstChild);
96
87
  break;
97
88
  case "head":
98
89
  default:
99
- dom.head.appendChild($newEl);
90
+ $newEl = dom.head.appendChild($newEl);
100
91
  break;
101
92
  }
102
- entry._sde[sdeKey] = () => $newEl?.remove();
93
+ markSideEffect("el", () => $newEl?.remove());
94
+ };
95
+ for (const tag of ctx.tags) {
96
+ const renderCtx = { shouldRender: true, tag };
97
+ await head.hooks.callHook("dom:renderTag", renderCtx);
98
+ if (!renderCtx.shouldRender)
99
+ continue;
100
+ renderTag(tag, head.headEntries().find((e) => e._i === Number(tag._e)));
103
101
  }
104
- head._flushQueuedSideEffects();
102
+ Object.values(queuedSideEffects).forEach((fn) => fn());
105
103
  }
106
104
  exports.domUpdatePromise = null;
107
105
  async function debouncedRenderDOMHead(head, options = {}) {
@@ -109,7 +107,7 @@ async function debouncedRenderDOMHead(head, options = {}) {
109
107
  exports.domUpdatePromise = null;
110
108
  return renderDOMHead(head, options);
111
109
  }
112
- const delayFn = options.delayFn || ((fn) => setTimeout(fn, 25));
110
+ const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
113
111
  return exports.domUpdatePromise = exports.domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
114
112
  }
115
113
 
package/dist/index.mjs CHANGED
@@ -9,97 +9,95 @@ const createElement = (tag, document) => {
9
9
  return $el;
10
10
  };
11
11
 
12
- function setAttributesWithSideEffects(head, $el, entry, tag) {
13
- const attrs = tag.props || {};
14
- const sdeKey = `${tag._p}:attr`;
15
- const sdeToRun = { ...entry._sde };
16
- Object.entries(attrs).forEach(([k, value]) => {
17
- value = String(value);
18
- const attrSdeKey = `${sdeKey}:${k}`;
19
- head._removeQueuedSideEffect(attrSdeKey);
20
- delete sdeToRun[attrSdeKey];
21
- if (k === "class") {
22
- for (const c of value.split(" ")) {
23
- const classSdeKey = `${sdeKey}:class:${c}`;
24
- if (!$el.classList.contains(c)) {
25
- $el.classList.add(c);
26
- entry._sde[classSdeKey] = () => $el.classList.remove(c);
27
- }
28
- head._removeQueuedSideEffect(classSdeKey);
29
- delete sdeToRun[classSdeKey];
30
- }
31
- return;
32
- }
33
- if ($el.getAttribute(k) !== value) {
34
- $el.setAttribute(k, value);
35
- if (!k.startsWith("data-h-"))
36
- entry._sde[attrSdeKey] = () => $el.removeAttribute(k);
37
- }
38
- });
39
- Object.entries(sdeToRun).filter(([key]) => key.startsWith(sdeKey)).forEach(([key, fn]) => {
40
- delete entry._sde[key] && fn();
41
- });
42
- }
43
-
44
12
  async function renderDOMHead(head, options = {}) {
45
13
  const dom = options.document || window.document;
46
14
  const tags = await head.resolveTags();
47
- const context = { shouldRender: true, tags };
48
- await head.hooks.callHook("dom:beforeRender", context);
49
- if (!context.shouldRender)
15
+ const ctx = { shouldRender: true, tags };
16
+ await head.hooks.callHook("dom:beforeRender", ctx);
17
+ if (!ctx.shouldRender)
50
18
  return;
51
- for (const tag of context.tags) {
52
- const renderCtx = { shouldRender: true, tag };
53
- await head.hooks.callHook("dom:renderTag", renderCtx);
54
- if (!renderCtx.shouldRender)
55
- return;
56
- const entry = head.headEntries().find((e) => e._i === Number(tag._e));
19
+ const queuedSideEffects = head._popSideEffectQueue();
20
+ head.headEntries().map((entry) => entry._sde).forEach((sde) => {
21
+ Object.entries(sde).forEach(([key, fn]) => {
22
+ queuedSideEffects[key] = fn;
23
+ });
24
+ });
25
+ const renderTag = (tag, entry) => {
57
26
  if (tag.tag === "title" && tag.children) {
58
27
  dom.title = tag.children;
59
- continue;
28
+ return;
60
29
  }
30
+ const markSideEffect = (key, fn) => {
31
+ key = `${tag._s || tag._p}:${key}`;
32
+ entry._sde[key] = fn;
33
+ delete queuedSideEffects[key];
34
+ };
35
+ const setAttrs = ($el) => {
36
+ Object.entries(tag.props).forEach(([k, value]) => {
37
+ value = String(value);
38
+ const attrSdeKey = `attr:${k}`;
39
+ if (k === "class") {
40
+ for (const c of value.split(" ")) {
41
+ const classSdeKey = `${attrSdeKey}:${c}`;
42
+ markSideEffect(classSdeKey, () => $el.classList.remove(c));
43
+ if (!$el.classList.contains(c))
44
+ $el.classList.add(c);
45
+ }
46
+ return;
47
+ }
48
+ if (!k.startsWith("data-h-"))
49
+ markSideEffect(attrSdeKey, () => $el.removeAttribute(k));
50
+ if ($el.getAttribute(k) !== value)
51
+ $el.setAttribute(k, value);
52
+ });
53
+ };
61
54
  if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
62
- setAttributesWithSideEffects(head, dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"], entry, tag);
63
- continue;
55
+ setAttrs(dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"]);
56
+ return;
64
57
  }
65
- const sdeKey = `${tag._s || tag._p}:el`;
66
- const $newEl = createElement(tag, dom);
67
- let $previousEl = null;
58
+ let $newEl = createElement(tag, dom);
59
+ let $previousEl;
68
60
  for (const $el of dom[tag.tagPosition?.startsWith("body") ? "body" : "head"].children) {
69
- if ($el.hasAttribute(`${tag._s}`)) {
61
+ if ($el.hasAttribute(`${tag._s}`) || $el.isEqualNode($newEl)) {
70
62
  $previousEl = $el;
71
63
  break;
72
64
  }
73
65
  }
74
66
  if ($previousEl) {
75
- head._removeQueuedSideEffect(sdeKey);
76
- if ($newEl.isEqualNode($previousEl))
77
- continue;
67
+ markSideEffect("el", () => $previousEl?.remove());
78
68
  if (Object.keys(tag.props).length === 0) {
79
69
  $previousEl.remove();
80
- continue;
70
+ return;
81
71
  }
82
- setAttributesWithSideEffects(head, $previousEl, entry, tag);
72
+ if ($newEl.isEqualNode($previousEl))
73
+ return;
74
+ setAttrs($previousEl);
83
75
  if (TagsWithInnerContent.includes(tag.tag))
84
76
  $previousEl.innerHTML = tag.children || "";
85
- entry._sde[sdeKey] = () => $previousEl?.remove();
86
- continue;
77
+ return;
87
78
  }
88
79
  switch (tag.tagPosition) {
89
80
  case "bodyClose":
90
- dom.body.appendChild($newEl);
81
+ $newEl = dom.body.appendChild($newEl);
91
82
  break;
92
83
  case "bodyOpen":
93
- dom.body.insertBefore($newEl, dom.body.firstChild);
84
+ $newEl = dom.body.insertBefore($newEl, dom.body.firstChild);
94
85
  break;
95
86
  case "head":
96
87
  default:
97
- dom.head.appendChild($newEl);
88
+ $newEl = dom.head.appendChild($newEl);
98
89
  break;
99
90
  }
100
- entry._sde[sdeKey] = () => $newEl?.remove();
91
+ markSideEffect("el", () => $newEl?.remove());
92
+ };
93
+ for (const tag of ctx.tags) {
94
+ const renderCtx = { shouldRender: true, tag };
95
+ await head.hooks.callHook("dom:renderTag", renderCtx);
96
+ if (!renderCtx.shouldRender)
97
+ continue;
98
+ renderTag(tag, head.headEntries().find((e) => e._i === Number(tag._e)));
101
99
  }
102
- head._flushQueuedSideEffects();
100
+ Object.values(queuedSideEffects).forEach((fn) => fn());
103
101
  }
104
102
  let domUpdatePromise = null;
105
103
  async function debouncedRenderDOMHead(head, options = {}) {
@@ -107,7 +105,7 @@ async function debouncedRenderDOMHead(head, options = {}) {
107
105
  domUpdatePromise = null;
108
106
  return renderDOMHead(head, options);
109
107
  }
110
- const delayFn = options.delayFn || ((fn) => setTimeout(fn, 25));
108
+ const delayFn = options.delayFn || ((fn) => setTimeout(fn, 10));
111
109
  return domUpdatePromise = domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
112
110
  }
113
111
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unhead/dom",
3
3
  "type": "module",
4
- "version": "0.2.7",
4
+ "version": "0.3.1",
5
5
  "packageManager": "pnpm@7.14.0",
6
6
  "author": "Harlan Wilton <harlan@harlanzw.com>",
7
7
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "dist"
31
31
  ],
32
32
  "dependencies": {
33
- "@unhead/schema": "0.2.7"
33
+ "@unhead/schema": "0.3.1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "zhead": "1.0.0-beta.10"