@unhead/dom 1.0.21 → 1.0.22

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
@@ -1,37 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const TagsWithInnerContent = ["script", "style", "noscript"];
4
- const HasElementTags = [
5
- "base",
6
- "meta",
7
- "link",
8
- "style",
9
- "script",
10
- "noscript"
11
- ];
12
-
13
- const UniqueTags = ["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"];
14
- function tagDedupeKey(tag, fn) {
15
- const { props, tag: tagName } = tag;
16
- if (UniqueTags.includes(tagName))
17
- return tagName;
18
- if (tagName === "link" && props.rel === "canonical")
19
- return "canonical";
20
- if (props.charset)
21
- return "charset";
22
- const name = ["id"];
23
- if (tagName === "meta")
24
- name.push(...["name", "property", "http-equiv"]);
25
- for (const n of name) {
26
- if (typeof props[n] !== "undefined") {
27
- const val = String(props[n]);
28
- if (fn && !fn(val))
29
- return false;
30
- return `${tagName}:${n}:${val}`;
31
- }
32
- }
33
- return false;
34
- }
3
+ const shared = require('@unhead/shared');
35
4
 
36
5
  const setAttrs = (ctx, markSideEffect) => {
37
6
  const { tag, $el } = ctx;
@@ -57,17 +26,10 @@ const setAttrs = (ctx, markSideEffect) => {
57
26
  if ($el.getAttribute(k) !== value)
58
27
  $el.setAttribute(k, value);
59
28
  });
60
- if (TagsWithInnerContent.includes(tag.tag) && $el.innerHTML !== (tag.children || ""))
29
+ if (shared.TagsWithInnerContent.includes(tag.tag) && $el.innerHTML !== (tag.children || ""))
61
30
  $el.innerHTML = tag.children || "";
62
31
  };
63
32
 
64
- function hashCode(s) {
65
- let h = 9;
66
- for (let i = 0; i < s.length; )
67
- h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
68
- return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 8).toLowerCase();
69
- }
70
-
71
33
  async function renderDOMHead(head, options = {}) {
72
34
  const ctx = { shouldRender: true };
73
35
  await head.hooks.callHook("dom:beforeRender", ctx);
@@ -80,10 +42,10 @@ async function renderDOMHead(head, options = {}) {
80
42
  staleSideEffects[key] = fn;
81
43
  });
82
44
  });
83
- const preRenderTag = async (tag) => {
45
+ const setupTagRenderCtx = async (tag) => {
84
46
  const entry = head.headEntries().find((e) => e._i === tag._e);
85
47
  const renderCtx = {
86
- renderId: tag._d || hashCode(JSON.stringify({ ...tag, _e: void 0, _p: void 0 })),
48
+ renderId: tag._d || shared.hashCode(JSON.stringify({ ...tag, _e: void 0, _p: void 0 })),
87
49
  $el: null,
88
50
  shouldRender: true,
89
51
  tag,
@@ -113,7 +75,7 @@ async function renderDOMHead(head, options = {}) {
113
75
  });
114
76
  };
115
77
  for (const t of await head.resolveTags()) {
116
- const ctx2 = await preRenderTag(t);
78
+ const ctx2 = await setupTagRenderCtx(t);
117
79
  if (!ctx2.shouldRender)
118
80
  continue;
119
81
  const { tag } = ctx2;
@@ -142,6 +104,11 @@ async function renderDOMHead(head, options = {}) {
142
104
  setAttrs(ctx2);
143
105
  pendingRenders[tag.tagPosition?.startsWith("body") ? "body" : "head"].push(ctx2);
144
106
  }
107
+ const fragments = {
108
+ bodyClose: void 0,
109
+ bodyOpen: void 0,
110
+ head: void 0
111
+ };
145
112
  Object.entries(pendingRenders).forEach(([pos, queue]) => {
146
113
  if (!queue.length)
147
114
  return;
@@ -150,9 +117,9 @@ async function renderDOMHead(head, options = {}) {
150
117
  return;
151
118
  for (const $el of [...children].reverse()) {
152
119
  const elTag = $el.tagName.toLowerCase();
153
- if (!HasElementTags.includes(elTag))
120
+ if (!shared.HasElementTags.includes(elTag))
154
121
  continue;
155
- const dedupeKey = tagDedupeKey({
122
+ const dedupeKey = shared.tagDedupeKey({
156
123
  tag: elTag,
157
124
  // convert attributes to object
158
125
  props: $el.getAttributeNames().reduce((props, name) => ({ ...props, [name]: $el.getAttribute(name) }), {})
@@ -167,23 +134,18 @@ async function renderDOMHead(head, options = {}) {
167
134
  }
168
135
  }
169
136
  queue.forEach((ctx2) => {
170
- if (!ctx2.$el)
171
- return;
172
- switch (ctx2.tag.tagPosition) {
173
- case "bodyClose":
174
- dom.body.appendChild(ctx2.$el);
175
- break;
176
- case "bodyOpen":
177
- dom.body.insertBefore(ctx2.$el, dom.body.firstChild);
178
- break;
179
- case "head":
180
- default:
181
- dom.head.appendChild(ctx2.$el);
182
- break;
183
- }
137
+ const pos2 = ctx2.tag.tagPosition || "head";
138
+ fragments[pos2] = fragments[pos2] || dom.createDocumentFragment();
139
+ fragments[pos2].appendChild(ctx2.$el);
184
140
  markEl(ctx2);
185
141
  });
186
142
  });
143
+ if (fragments.head)
144
+ dom.head.appendChild(fragments.head);
145
+ if (fragments.bodyOpen)
146
+ dom.body.insertBefore(fragments.bodyOpen, dom.body.firstChild);
147
+ if (fragments.bodyClose)
148
+ dom.body.appendChild(fragments.bodyClose);
187
149
  for (const ctx2 of renders)
188
150
  await head.hooks.callHook("dom:renderTag", ctx2);
189
151
  Object.values(staleSideEffects).forEach((fn) => fn());
@@ -198,6 +160,22 @@ async function debouncedRenderDOMHead(head, options = {}) {
198
160
  return exports.domUpdatePromise = exports.domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
199
161
  }
200
162
 
163
+ const PatchDomOnEntryUpdatesPlugin = (options) => {
164
+ return shared.defineHeadPlugin({
165
+ hooks: {
166
+ "entries:updated": function(head) {
167
+ if (typeof options?.document === "undefined" && typeof window === "undefined")
168
+ return;
169
+ let delayFn = options?.delayFn;
170
+ if (!delayFn && typeof requestAnimationFrame !== "undefined")
171
+ delayFn = requestAnimationFrame;
172
+ debouncedRenderDOMHead(head, { document: options?.document || window.document, delayFn });
173
+ }
174
+ }
175
+ });
176
+ };
177
+
178
+ exports.PatchDomOnEntryUpdatesPlugin = PatchDomOnEntryUpdatesPlugin;
201
179
  exports.debouncedRenderDOMHead = debouncedRenderDOMHead;
202
- exports.hashCode = hashCode;
203
180
  exports.renderDOMHead = renderDOMHead;
181
+ exports.setAttrs = setAttrs;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Unhead } from '@unhead/schema';
1
+ import * as _unhead_schema from '@unhead/schema';
2
+ import { Unhead, DomRenderTagContext } from '@unhead/schema';
2
3
 
3
4
  interface RenderDomHeadOptions {
4
5
  /**
@@ -21,6 +22,14 @@ declare function debouncedRenderDOMHead<T extends Unhead<any>>(head: T, options?
21
22
  delayFn?: (fn: () => void) => void;
22
23
  }): Promise<void>;
23
24
 
24
- declare function hashCode(s: string): string;
25
+ interface TriggerDomPatchingOnUpdatesPluginOptions extends RenderDomHeadOptions {
26
+ delayFn?: (fn: () => void) => void;
27
+ }
28
+ declare const PatchDomOnEntryUpdatesPlugin: (options?: TriggerDomPatchingOnUpdatesPluginOptions) => _unhead_schema.HeadPlugin;
29
+
30
+ /**
31
+ * Set attributes on a DOM element, while adding entry side effects.
32
+ */
33
+ declare const setAttrs: (ctx: DomRenderTagContext, markSideEffect?: ((ctx: DomRenderTagContext, k: string, fn: () => void) => void) | undefined) => void;
25
34
 
26
- export { RenderDomHeadOptions, debouncedRenderDOMHead, domUpdatePromise, hashCode, renderDOMHead };
35
+ export { PatchDomOnEntryUpdatesPlugin, RenderDomHeadOptions, TriggerDomPatchingOnUpdatesPluginOptions, debouncedRenderDOMHead, domUpdatePromise, renderDOMHead, setAttrs };
package/dist/index.mjs CHANGED
@@ -1,35 +1,4 @@
1
- const TagsWithInnerContent = ["script", "style", "noscript"];
2
- const HasElementTags = [
3
- "base",
4
- "meta",
5
- "link",
6
- "style",
7
- "script",
8
- "noscript"
9
- ];
10
-
11
- const UniqueTags = ["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"];
12
- function tagDedupeKey(tag, fn) {
13
- const { props, tag: tagName } = tag;
14
- if (UniqueTags.includes(tagName))
15
- return tagName;
16
- if (tagName === "link" && props.rel === "canonical")
17
- return "canonical";
18
- if (props.charset)
19
- return "charset";
20
- const name = ["id"];
21
- if (tagName === "meta")
22
- name.push(...["name", "property", "http-equiv"]);
23
- for (const n of name) {
24
- if (typeof props[n] !== "undefined") {
25
- const val = String(props[n]);
26
- if (fn && !fn(val))
27
- return false;
28
- return `${tagName}:${n}:${val}`;
29
- }
30
- }
31
- return false;
32
- }
1
+ import { TagsWithInnerContent, HasElementTags, tagDedupeKey, hashCode, defineHeadPlugin } from '@unhead/shared';
33
2
 
34
3
  const setAttrs = (ctx, markSideEffect) => {
35
4
  const { tag, $el } = ctx;
@@ -59,13 +28,6 @@ const setAttrs = (ctx, markSideEffect) => {
59
28
  $el.innerHTML = tag.children || "";
60
29
  };
61
30
 
62
- function hashCode(s) {
63
- let h = 9;
64
- for (let i = 0; i < s.length; )
65
- h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
66
- return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 8).toLowerCase();
67
- }
68
-
69
31
  async function renderDOMHead(head, options = {}) {
70
32
  const ctx = { shouldRender: true };
71
33
  await head.hooks.callHook("dom:beforeRender", ctx);
@@ -78,7 +40,7 @@ async function renderDOMHead(head, options = {}) {
78
40
  staleSideEffects[key] = fn;
79
41
  });
80
42
  });
81
- const preRenderTag = async (tag) => {
43
+ const setupTagRenderCtx = async (tag) => {
82
44
  const entry = head.headEntries().find((e) => e._i === tag._e);
83
45
  const renderCtx = {
84
46
  renderId: tag._d || hashCode(JSON.stringify({ ...tag, _e: void 0, _p: void 0 })),
@@ -111,7 +73,7 @@ async function renderDOMHead(head, options = {}) {
111
73
  });
112
74
  };
113
75
  for (const t of await head.resolveTags()) {
114
- const ctx2 = await preRenderTag(t);
76
+ const ctx2 = await setupTagRenderCtx(t);
115
77
  if (!ctx2.shouldRender)
116
78
  continue;
117
79
  const { tag } = ctx2;
@@ -140,6 +102,11 @@ async function renderDOMHead(head, options = {}) {
140
102
  setAttrs(ctx2);
141
103
  pendingRenders[tag.tagPosition?.startsWith("body") ? "body" : "head"].push(ctx2);
142
104
  }
105
+ const fragments = {
106
+ bodyClose: void 0,
107
+ bodyOpen: void 0,
108
+ head: void 0
109
+ };
143
110
  Object.entries(pendingRenders).forEach(([pos, queue]) => {
144
111
  if (!queue.length)
145
112
  return;
@@ -165,23 +132,18 @@ async function renderDOMHead(head, options = {}) {
165
132
  }
166
133
  }
167
134
  queue.forEach((ctx2) => {
168
- if (!ctx2.$el)
169
- return;
170
- switch (ctx2.tag.tagPosition) {
171
- case "bodyClose":
172
- dom.body.appendChild(ctx2.$el);
173
- break;
174
- case "bodyOpen":
175
- dom.body.insertBefore(ctx2.$el, dom.body.firstChild);
176
- break;
177
- case "head":
178
- default:
179
- dom.head.appendChild(ctx2.$el);
180
- break;
181
- }
135
+ const pos2 = ctx2.tag.tagPosition || "head";
136
+ fragments[pos2] = fragments[pos2] || dom.createDocumentFragment();
137
+ fragments[pos2].appendChild(ctx2.$el);
182
138
  markEl(ctx2);
183
139
  });
184
140
  });
141
+ if (fragments.head)
142
+ dom.head.appendChild(fragments.head);
143
+ if (fragments.bodyOpen)
144
+ dom.body.insertBefore(fragments.bodyOpen, dom.body.firstChild);
145
+ if (fragments.bodyClose)
146
+ dom.body.appendChild(fragments.bodyClose);
185
147
  for (const ctx2 of renders)
186
148
  await head.hooks.callHook("dom:renderTag", ctx2);
187
149
  Object.values(staleSideEffects).forEach((fn) => fn());
@@ -196,4 +158,19 @@ async function debouncedRenderDOMHead(head, options = {}) {
196
158
  return domUpdatePromise = domUpdatePromise || new Promise((resolve) => delayFn(() => resolve(doDomUpdate())));
197
159
  }
198
160
 
199
- export { debouncedRenderDOMHead, domUpdatePromise, hashCode, renderDOMHead };
161
+ const PatchDomOnEntryUpdatesPlugin = (options) => {
162
+ return defineHeadPlugin({
163
+ hooks: {
164
+ "entries:updated": function(head) {
165
+ if (typeof options?.document === "undefined" && typeof window === "undefined")
166
+ return;
167
+ let delayFn = options?.delayFn;
168
+ if (!delayFn && typeof requestAnimationFrame !== "undefined")
169
+ delayFn = requestAnimationFrame;
170
+ debouncedRenderDOMHead(head, { document: options?.document || window.document, delayFn });
171
+ }
172
+ }
173
+ });
174
+ };
175
+
176
+ export { PatchDomOnEntryUpdatesPlugin, debouncedRenderDOMHead, domUpdatePromise, renderDOMHead, setAttrs };
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@unhead/dom",
3
3
  "type": "module",
4
- "version": "1.0.21",
5
- "packageManager": "pnpm@7.25.1",
4
+ "version": "1.0.22",
5
+ "packageManager": "pnpm@7.26.3",
6
6
  "author": "Harlan Wilton <harlan@harlanzw.com>",
7
7
  "license": "MIT",
8
8
  "funding": "https://github.com/sponsors/harlan-zw",
9
- "homepage": "https://github.com/harlan-zw/unhead#readme",
9
+ "homepage": "https://unhead.harlanzw.com",
10
10
  "repository": {
11
11
  "type": "git",
12
- "url": "git+https://github.com/harlan-zw/unhead.git",
12
+ "url": "git+https://github.com/unjs/unhead.git",
13
13
  "directory": "packages/dom"
14
14
  },
15
15
  "bugs": {
16
- "url": "https://github.com/harlan-zw/unhead/issues"
16
+ "url": "https://github.com/unjs/unhead/issues"
17
17
  },
18
18
  "sideEffects": false,
19
19
  "exports": {
@@ -30,10 +30,8 @@
30
30
  "dist"
31
31
  ],
32
32
  "dependencies": {
33
- "@unhead/schema": "1.0.21"
34
- },
35
- "devDependencies": {
36
- "zhead": "^1.1.0"
33
+ "@unhead/schema": "1.0.22",
34
+ "@unhead/shared": "1.0.22"
37
35
  },
38
36
  "scripts": {
39
37
  "build": "unbuild .",