@unhead/dom 0.4.5 → 0.4.7

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,52 @@
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") {
35
+ const val = String(props[n]);
36
+ if (ArrayMetaProperties.findIndex((p) => val.startsWith(p)) !== -1)
37
+ return false;
38
+ return `${tagName}:${n}:${val}`;
39
+ }
40
+ }
41
+ return false;
42
+ }
43
+
5
44
  async function renderDOMHead(head, options = {}) {
6
- const dom = options.document || window.document;
7
- const tags = await head.resolveTags();
8
- const ctx = { shouldRender: true, tags };
45
+ const ctx = { shouldRender: true };
9
46
  await head.hooks.callHook("dom:beforeRender", ctx);
10
47
  if (!ctx.shouldRender)
11
48
  return;
12
- Object.values(head._popSideEffectQueue()).forEach((fn) => fn());
13
- const staleSideEffects = {};
49
+ const dom = options.document || window.document;
50
+ const staleSideEffects = head._popSideEffectQueue();
14
51
  head.headEntries().map((entry) => entry._sde).forEach((sde) => {
15
52
  Object.entries(sde).forEach(([key, fn]) => {
16
53
  staleSideEffects[key] = fn;
@@ -22,7 +59,7 @@ async function renderDOMHead(head, options = {}) {
22
59
  return dom.head.querySelector("title");
23
60
  }
24
61
  const markSideEffect = (key, fn) => {
25
- key = `${tag._p}:${key}`;
62
+ key = `${tag._d || tag._p}:${key}`;
26
63
  entry._sde[key] = fn;
27
64
  delete staleSideEffects[key];
28
65
  };
@@ -56,7 +93,11 @@ async function renderDOMHead(head, options = {}) {
56
93
  setAttrs($newEl, false);
57
94
  let $previousEl;
58
95
  for (const $el of dom[tag.tagPosition?.startsWith("body") ? "body" : "head"].children) {
59
- if (tag._s && $el.hasAttribute(`${tag._s}`) || $el.isEqualNode($newEl)) {
96
+ const key = $el.getAttribute("data-h-key") || tagDedupeKey({
97
+ tag: $el.tagName.toLowerCase(),
98
+ props: Array.from($el.attributes).map((attr) => [attr.name, attr.value]).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
99
+ });
100
+ if (key === tag._d || $el.isEqualNode($newEl)) {
60
101
  $previousEl = $el;
61
102
  break;
62
103
  }
@@ -83,7 +124,7 @@ async function renderDOMHead(head, options = {}) {
83
124
  markSideEffect("el", () => $newEl?.remove());
84
125
  return $newEl;
85
126
  };
86
- for (const tag of ctx.tags) {
127
+ for (const tag of await head.resolveTags()) {
87
128
  const entry = head.headEntries().find((e) => e._i === Number(tag._e));
88
129
  const renderCtx = { $el: null, shouldRender: true, tag, entry, queuedSideEffects: staleSideEffects };
89
130
  await head.hooks.callHook("dom:beforeRenderTag", renderCtx);
package/dist/index.mjs CHANGED
@@ -1,14 +1,51 @@
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") {
33
+ const val = String(props[n]);
34
+ if (ArrayMetaProperties.findIndex((p) => val.startsWith(p)) !== -1)
35
+ return false;
36
+ return `${tagName}:${n}:${val}`;
37
+ }
38
+ }
39
+ return false;
40
+ }
41
+
3
42
  async function renderDOMHead(head, options = {}) {
4
- const dom = options.document || window.document;
5
- const tags = await head.resolveTags();
6
- const ctx = { shouldRender: true, tags };
43
+ const ctx = { shouldRender: true };
7
44
  await head.hooks.callHook("dom:beforeRender", ctx);
8
45
  if (!ctx.shouldRender)
9
46
  return;
10
- Object.values(head._popSideEffectQueue()).forEach((fn) => fn());
11
- const staleSideEffects = {};
47
+ const dom = options.document || window.document;
48
+ const staleSideEffects = head._popSideEffectQueue();
12
49
  head.headEntries().map((entry) => entry._sde).forEach((sde) => {
13
50
  Object.entries(sde).forEach(([key, fn]) => {
14
51
  staleSideEffects[key] = fn;
@@ -20,7 +57,7 @@ async function renderDOMHead(head, options = {}) {
20
57
  return dom.head.querySelector("title");
21
58
  }
22
59
  const markSideEffect = (key, fn) => {
23
- key = `${tag._p}:${key}`;
60
+ key = `${tag._d || tag._p}:${key}`;
24
61
  entry._sde[key] = fn;
25
62
  delete staleSideEffects[key];
26
63
  };
@@ -54,7 +91,11 @@ async function renderDOMHead(head, options = {}) {
54
91
  setAttrs($newEl, false);
55
92
  let $previousEl;
56
93
  for (const $el of dom[tag.tagPosition?.startsWith("body") ? "body" : "head"].children) {
57
- if (tag._s && $el.hasAttribute(`${tag._s}`) || $el.isEqualNode($newEl)) {
94
+ const key = $el.getAttribute("data-h-key") || tagDedupeKey({
95
+ tag: $el.tagName.toLowerCase(),
96
+ props: Array.from($el.attributes).map((attr) => [attr.name, attr.value]).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
97
+ });
98
+ if (key === tag._d || $el.isEqualNode($newEl)) {
58
99
  $previousEl = $el;
59
100
  break;
60
101
  }
@@ -81,7 +122,7 @@ async function renderDOMHead(head, options = {}) {
81
122
  markSideEffect("el", () => $newEl?.remove());
82
123
  return $newEl;
83
124
  };
84
- for (const tag of ctx.tags) {
125
+ for (const tag of await head.resolveTags()) {
85
126
  const entry = head.headEntries().find((e) => e._i === Number(tag._e));
86
127
  const renderCtx = { $el: null, shouldRender: true, tag, entry, queuedSideEffects: staleSideEffects };
87
128
  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.5",
4
+ "version": "0.4.7",
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.5"
33
+ "@unhead/schema": "0.4.7"
34
34
  },
35
35
  "devDependencies": {
36
- "zhead": "1.0.0-beta.11"
36
+ "zhead": "1.0.0-beta.13"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "unbuild .",