@unhead/dom 0.4.4 → 0.4.6

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
@@ -2,15 +2,49 @@
2
2
 
3
3
  const TagsWithInnerContent = ["script", "style", "noscript"];
4
4
 
5
+ const UniqueTags = ["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"];
6
+ const ArrayMetaProperties = [
7
+ "og:image",
8
+ "og:video",
9
+ "og:audio",
10
+ "og:locale:alternate",
11
+ "video:actor",
12
+ "video:director",
13
+ "video:writer",
14
+ "video:tag",
15
+ "article:author",
16
+ "article:tag",
17
+ "book:tag",
18
+ "book:author",
19
+ "music:album",
20
+ "music:musician"
21
+ ];
22
+ function tagDedupeKey(tag) {
23
+ const { props, tag: tagName } = tag;
24
+ if (UniqueTags.includes(tagName))
25
+ return tagName;
26
+ if (tagName === "link" && props.rel === "canonical")
27
+ return "canonical";
28
+ if (props.charset)
29
+ return "charset";
30
+ const name = ["id"];
31
+ if (tagName === "meta")
32
+ name.push(...["name", "property", "http-equiv"]);
33
+ for (const n of name) {
34
+ if (typeof props[n] !== "undefined" && !ArrayMetaProperties.findIndex((p) => !props[n].startsWith(p))) {
35
+ return `${tagName}:${n}:${props[n]}`;
36
+ }
37
+ }
38
+ return false;
39
+ }
40
+
5
41
  async function renderDOMHead(head, options = {}) {
6
- const dom = options.document || window.document;
7
- const tags = await head.resolveTags();
8
- const ctx = { shouldRender: true, tags };
42
+ const ctx = { shouldRender: true };
9
43
  await head.hooks.callHook("dom:beforeRender", ctx);
10
44
  if (!ctx.shouldRender)
11
45
  return;
12
- Object.values(head._popSideEffectQueue()).forEach((fn) => fn());
13
- const staleSideEffects = {};
46
+ const dom = options.document || window.document;
47
+ const staleSideEffects = head._popSideEffectQueue();
14
48
  head.headEntries().map((entry) => entry._sde).forEach((sde) => {
15
49
  Object.entries(sde).forEach(([key, fn]) => {
16
50
  staleSideEffects[key] = fn;
@@ -22,7 +56,7 @@ async function renderDOMHead(head, options = {}) {
22
56
  return dom.head.querySelector("title");
23
57
  }
24
58
  const markSideEffect = (key, fn) => {
25
- key = `${tag._p}:${key}`;
59
+ key = `${tag._d || tag._p}:${key}`;
26
60
  entry._sde[key] = fn;
27
61
  delete staleSideEffects[key];
28
62
  };
@@ -56,7 +90,11 @@ async function renderDOMHead(head, options = {}) {
56
90
  setAttrs($newEl, false);
57
91
  let $previousEl;
58
92
  for (const $el of dom[tag.tagPosition?.startsWith("body") ? "body" : "head"].children) {
59
- if (tag._s && $el.hasAttribute(`${tag._s}`) || $el.isEqualNode($newEl)) {
93
+ const key = $el.getAttribute("data-h-key") || tagDedupeKey({
94
+ tag: $el.tagName.toLowerCase(),
95
+ props: Array.from($el.attributes).map((attr) => [attr.name, attr.value]).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
96
+ });
97
+ if (key === tag._d || $el.isEqualNode($newEl)) {
60
98
  $previousEl = $el;
61
99
  break;
62
100
  }
@@ -83,7 +121,7 @@ async function renderDOMHead(head, options = {}) {
83
121
  markSideEffect("el", () => $newEl?.remove());
84
122
  return $newEl;
85
123
  };
86
- for (const tag of ctx.tags) {
124
+ for (const tag of await head.resolveTags()) {
87
125
  const entry = head.headEntries().find((e) => e._i === Number(tag._e));
88
126
  const renderCtx = { $el: null, shouldRender: true, tag, entry, queuedSideEffects: staleSideEffects };
89
127
  await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
package/dist/index.mjs CHANGED
@@ -1,14 +1,48 @@
1
1
  const TagsWithInnerContent = ["script", "style", "noscript"];
2
2
 
3
+ const UniqueTags = ["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"];
4
+ const ArrayMetaProperties = [
5
+ "og:image",
6
+ "og:video",
7
+ "og:audio",
8
+ "og:locale:alternate",
9
+ "video:actor",
10
+ "video:director",
11
+ "video:writer",
12
+ "video:tag",
13
+ "article:author",
14
+ "article:tag",
15
+ "book:tag",
16
+ "book:author",
17
+ "music:album",
18
+ "music:musician"
19
+ ];
20
+ function tagDedupeKey(tag) {
21
+ const { props, tag: tagName } = tag;
22
+ if (UniqueTags.includes(tagName))
23
+ return tagName;
24
+ if (tagName === "link" && props.rel === "canonical")
25
+ return "canonical";
26
+ if (props.charset)
27
+ return "charset";
28
+ const name = ["id"];
29
+ if (tagName === "meta")
30
+ name.push(...["name", "property", "http-equiv"]);
31
+ for (const n of name) {
32
+ if (typeof props[n] !== "undefined" && !ArrayMetaProperties.findIndex((p) => !props[n].startsWith(p))) {
33
+ return `${tagName}:${n}:${props[n]}`;
34
+ }
35
+ }
36
+ return false;
37
+ }
38
+
3
39
  async function renderDOMHead(head, options = {}) {
4
- const dom = options.document || window.document;
5
- const tags = await head.resolveTags();
6
- const ctx = { shouldRender: true, tags };
40
+ const ctx = { shouldRender: true };
7
41
  await head.hooks.callHook("dom:beforeRender", ctx);
8
42
  if (!ctx.shouldRender)
9
43
  return;
10
- Object.values(head._popSideEffectQueue()).forEach((fn) => fn());
11
- const staleSideEffects = {};
44
+ const dom = options.document || window.document;
45
+ const staleSideEffects = head._popSideEffectQueue();
12
46
  head.headEntries().map((entry) => entry._sde).forEach((sde) => {
13
47
  Object.entries(sde).forEach(([key, fn]) => {
14
48
  staleSideEffects[key] = fn;
@@ -20,7 +54,7 @@ async function renderDOMHead(head, options = {}) {
20
54
  return dom.head.querySelector("title");
21
55
  }
22
56
  const markSideEffect = (key, fn) => {
23
- key = `${tag._p}:${key}`;
57
+ key = `${tag._d || tag._p}:${key}`;
24
58
  entry._sde[key] = fn;
25
59
  delete staleSideEffects[key];
26
60
  };
@@ -54,7 +88,11 @@ async function renderDOMHead(head, options = {}) {
54
88
  setAttrs($newEl, false);
55
89
  let $previousEl;
56
90
  for (const $el of dom[tag.tagPosition?.startsWith("body") ? "body" : "head"].children) {
57
- if (tag._s && $el.hasAttribute(`${tag._s}`) || $el.isEqualNode($newEl)) {
91
+ const key = $el.getAttribute("data-h-key") || tagDedupeKey({
92
+ tag: $el.tagName.toLowerCase(),
93
+ props: Array.from($el.attributes).map((attr) => [attr.name, attr.value]).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
94
+ });
95
+ if (key === tag._d || $el.isEqualNode($newEl)) {
58
96
  $previousEl = $el;
59
97
  break;
60
98
  }
@@ -81,7 +119,7 @@ async function renderDOMHead(head, options = {}) {
81
119
  markSideEffect("el", () => $newEl?.remove());
82
120
  return $newEl;
83
121
  };
84
- for (const tag of ctx.tags) {
122
+ for (const tag of await head.resolveTags()) {
85
123
  const entry = head.headEntries().find((e) => e._i === Number(tag._e));
86
124
  const renderCtx = { $el: null, shouldRender: true, tag, entry, queuedSideEffects: staleSideEffects };
87
125
  await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unhead/dom",
3
3
  "type": "module",
4
- "version": "0.4.4",
4
+ "version": "0.4.6",
5
5
  "packageManager": "pnpm@7.14.0",
6
6
  "author": "Harlan Wilton <harlan@harlanzw.com>",
7
7
  "license": "MIT",
@@ -30,10 +30,10 @@
30
30
  "dist"
31
31
  ],
32
32
  "dependencies": {
33
- "@unhead/schema": "0.4.4"
33
+ "@unhead/schema": "0.4.6"
34
34
  },
35
35
  "devDependencies": {
36
- "zhead": "1.0.0-beta.11"
36
+ "zhead": "1.0.0-beta.12"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "unbuild .",