unhead 2.1.10 → 2.1.12

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/client.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { a as createUnhead } from './shared/unhead.D_nrZZPH.mjs';
1
+ import { a as createUnhead } from './shared/unhead.Ct24BOby.mjs';
2
2
  import { H as HasElementTags } from './shared/unhead.yem5I2v_.mjs';
3
- import { i as isMetaArrayDupeKey, a as normalizeProps, d as dedupeKey, h as hashTag } from './shared/unhead.fVVqDC1O.mjs';
3
+ import { i as isMetaArrayDupeKey, a as normalizeProps, d as dedupeKey, h as hashTag } from './shared/unhead.B5FWS6X0.mjs';
4
4
  import 'hookable';
5
5
  import './shared/unhead.CbpEuj3y.mjs';
6
6
 
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
- export { u as useHead, a as useHeadSafe, b as useSeoMeta, c as useServerHead, d as useServerHeadSafe, e as useServerSeoMeta } from './shared/unhead.BPM0-cfG.mjs';
2
- export { c as createHeadCore, a as createUnhead } from './shared/unhead.D_nrZZPH.mjs';
1
+ export { u as useHead, a as useHeadSafe, b as useSeoMeta, c as useServerHead, d as useServerHeadSafe, e as useServerSeoMeta } from './shared/unhead._ybhDo-G.mjs';
2
+ export { c as createHeadCore, a as createUnhead } from './shared/unhead.Ct24BOby.mjs';
3
3
  export { u as useScript } from './shared/unhead.BnoAbrHA.mjs';
4
- import './shared/unhead.CApf5sj3.mjs';
4
+ import './shared/unhead.0eF0fa-Y.mjs';
5
5
  import './shared/unhead.DQc16pHI.mjs';
6
6
  import './shared/unhead.yem5I2v_.mjs';
7
7
  import 'hookable';
8
- import './shared/unhead.fVVqDC1O.mjs';
8
+ import './shared/unhead.B5FWS6X0.mjs';
9
9
  import './shared/unhead.CbpEuj3y.mjs';
package/dist/legacy.mjs CHANGED
@@ -1,12 +1,12 @@
1
- import { a as createUnhead } from './shared/unhead.D_nrZZPH.mjs';
2
- import { D as DeprecationsPlugin, P as PromisesPlugin, T as TemplateParamsPlugin, A as AliasSortingPlugin } from './shared/unhead.ckV6dpEQ.mjs';
3
- export { u as useHead, a as useHeadSafe, b as useSeoMeta, c as useServerHead, d as useServerHeadSafe, e as useServerSeoMeta } from './shared/unhead.BPM0-cfG.mjs';
1
+ import { a as createUnhead } from './shared/unhead.Ct24BOby.mjs';
2
+ import { D as DeprecationsPlugin, P as PromisesPlugin, T as TemplateParamsPlugin, A as AliasSortingPlugin } from './shared/unhead.Chhckluj.mjs';
3
+ export { u as useHead, a as useHeadSafe, b as useSeoMeta, c as useServerHead, d as useServerHeadSafe, e as useServerSeoMeta } from './shared/unhead._ybhDo-G.mjs';
4
4
  export { u as useScript } from './shared/unhead.BnoAbrHA.mjs';
5
5
  import 'hookable';
6
- import './shared/unhead.fVVqDC1O.mjs';
6
+ import './shared/unhead.B5FWS6X0.mjs';
7
7
  import './shared/unhead.yem5I2v_.mjs';
8
8
  import './shared/unhead.CbpEuj3y.mjs';
9
- import './shared/unhead.CApf5sj3.mjs';
9
+ import './shared/unhead.0eF0fa-Y.mjs';
10
10
  import './shared/unhead.DQc16pHI.mjs';
11
11
  import './shared/unhead.BYvz9V1x.mjs';
12
12
 
package/dist/plugins.mjs CHANGED
@@ -1,6 +1,6 @@
1
- export { A as AliasSortingPlugin, D as DeprecationsPlugin, P as PromisesPlugin, T as TemplateParamsPlugin } from './shared/unhead.ckV6dpEQ.mjs';
2
- import { d as defineHeadPlugin } from './shared/unhead.CApf5sj3.mjs';
3
- export { F as FlatMetaPlugin, S as SafeInputPlugin } from './shared/unhead.CApf5sj3.mjs';
1
+ export { A as AliasSortingPlugin, D as DeprecationsPlugin, P as PromisesPlugin, T as TemplateParamsPlugin } from './shared/unhead.Chhckluj.mjs';
2
+ import { d as defineHeadPlugin } from './shared/unhead.0eF0fa-Y.mjs';
3
+ export { F as FlatMetaPlugin, S as SafeInputPlugin } from './shared/unhead.0eF0fa-Y.mjs';
4
4
  import './shared/unhead.CbpEuj3y.mjs';
5
5
  import './shared/unhead.BYvz9V1x.mjs';
6
6
  import './shared/unhead.DQc16pHI.mjs';
package/dist/server.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { a as createUnhead } from './shared/unhead.D_nrZZPH.mjs';
1
+ import { a as createUnhead } from './shared/unhead.Ct24BOby.mjs';
2
2
  import { T as TagsWithInnerContent, S as SelfClosingTags } from './shared/unhead.yem5I2v_.mjs';
3
3
  import { parseHtmlForUnheadExtraction, applyHeadToHtml, parseHtmlForIndexes } from './parser.mjs';
4
4
  import 'hookable';
5
- import './shared/unhead.fVVqDC1O.mjs';
5
+ import './shared/unhead.B5FWS6X0.mjs';
6
6
  import './shared/unhead.CbpEuj3y.mjs';
7
7
 
8
8
  // @__NO_SIDE_EFFECTS__
@@ -0,0 +1,236 @@
1
+ import { u as unpackMeta } from './unhead.DQc16pHI.mjs';
2
+
3
+ function defineHeadPlugin(plugin) {
4
+ return plugin;
5
+ }
6
+
7
+ const FlatMetaPlugin = /* @__PURE__ */ defineHeadPlugin({
8
+ key: "flatMeta",
9
+ hooks: {
10
+ "entries:normalize": (ctx) => {
11
+ const tagsToAdd = [];
12
+ ctx.tags = ctx.tags.map((t) => {
13
+ if (t.tag !== "_flatMeta") {
14
+ return t;
15
+ }
16
+ tagsToAdd.push(unpackMeta(t.props).map((p) => ({
17
+ ...t,
18
+ tag: "meta",
19
+ props: p
20
+ })));
21
+ return false;
22
+ }).filter(Boolean).concat(...tagsToAdd);
23
+ }
24
+ }
25
+ });
26
+
27
+ const WhitelistAttributes = {
28
+ htmlAttrs: /* @__PURE__ */ new Set(["class", "style", "lang", "dir"]),
29
+ bodyAttrs: /* @__PURE__ */ new Set(["class", "style"]),
30
+ meta: /* @__PURE__ */ new Set(["name", "property", "charset", "content", "media"]),
31
+ noscript: /* @__PURE__ */ new Set([]),
32
+ style: /* @__PURE__ */ new Set(["media", "nonce", "title", "blocking"]),
33
+ script: /* @__PURE__ */ new Set(["type", "textContent", "nonce", "blocking"]),
34
+ link: /* @__PURE__ */ new Set(["color", "crossorigin", "fetchpriority", "href", "hreflang", "imagesrcset", "imagesizes", "integrity", "media", "referrerpolicy", "rel", "sizes", "type"])
35
+ };
36
+ const BlockedLinkRels = /* @__PURE__ */ new Set(["canonical", "modulepreload", "prerender", "preload", "prefetch", "dns-prefetch", "preconnect", "manifest", "pingback"]);
37
+ const SafeAttrName = /^[a-z][a-z0-9\-]*[a-z0-9]$/i;
38
+ const HtmlEntityHex = /&#x([0-9a-f]{1,6});?/gi;
39
+ const HtmlEntityDec = /&#(\d{1,7});?/g;
40
+ const HtmlEntityNamed = /&(tab|newline|colon|semi|lpar|rpar|sol|bsol|comma|period|excl|num|dollar|percnt|amp|apos|ast|plus|lt|gt|equals|quest|at|lsqb|rsqb|lcub|rcub|vert|hat|grave|tilde|nbsp);?/gi;
41
+ const ControlChars = /[\x00-\x20]+/g;
42
+ const NamedEntityMap = {
43
+ tab: " ",
44
+ newline: "\n",
45
+ colon: ":",
46
+ semi: ";",
47
+ lpar: "(",
48
+ rpar: ")",
49
+ sol: "/",
50
+ bsol: "\\",
51
+ comma: ",",
52
+ period: ".",
53
+ excl: "!",
54
+ num: "#",
55
+ dollar: "$",
56
+ percnt: "%",
57
+ amp: "&",
58
+ apos: "'",
59
+ ast: "*",
60
+ plus: "+",
61
+ lt: "<",
62
+ gt: ">",
63
+ equals: "=",
64
+ quest: "?",
65
+ at: "@",
66
+ lsqb: "[",
67
+ rsqb: "]",
68
+ lcub: "{",
69
+ rcub: "}",
70
+ vert: "|",
71
+ hat: "^",
72
+ grave: "`",
73
+ tilde: "~",
74
+ nbsp: "\xA0"
75
+ };
76
+ function safeFromCodePoint(codePoint) {
77
+ if (codePoint > 1114111 || codePoint < 0 || Number.isNaN(codePoint))
78
+ return "";
79
+ return String.fromCodePoint(codePoint);
80
+ }
81
+ function decodeHtmlEntities(str) {
82
+ return str.replace(HtmlEntityHex, (_, hex) => safeFromCodePoint(Number.parseInt(hex, 16))).replace(HtmlEntityDec, (_, dec) => safeFromCodePoint(Number(dec))).replace(HtmlEntityNamed, (_, name) => NamedEntityMap[name.toLowerCase()] || "");
83
+ }
84
+ function hasDangerousProtocol(url) {
85
+ const entityDecoded = decodeHtmlEntities(url);
86
+ const cleaned = entityDecoded.replace(ControlChars, "");
87
+ let decoded;
88
+ try {
89
+ decoded = decodeURIComponent(cleaned);
90
+ } catch {
91
+ decoded = cleaned;
92
+ }
93
+ const sanitized = decoded.replace(ControlChars, "");
94
+ const lower = sanitized.toLowerCase();
95
+ return lower.startsWith("javascript:") || lower.startsWith("data:") || lower.startsWith("vbscript:");
96
+ }
97
+ function stripProtoKeys(obj) {
98
+ if (Array.isArray(obj))
99
+ return obj.map(stripProtoKeys);
100
+ if (obj && typeof obj === "object") {
101
+ const clean = {};
102
+ for (const key of Object.keys(obj)) {
103
+ if (key === "__proto__" || key === "constructor" || key === "prototype")
104
+ continue;
105
+ clean[key] = stripProtoKeys(obj[key]);
106
+ }
107
+ return clean;
108
+ }
109
+ return obj;
110
+ }
111
+ function acceptDataAttrs(value, allowId = true) {
112
+ return Object.fromEntries(
113
+ Object.entries(value || {}).filter(([key]) => (allowId && key === "id" || key.startsWith("data-")) && SafeAttrName.test(key))
114
+ );
115
+ }
116
+ function makeTagSafe(tag) {
117
+ let next = {};
118
+ const { tag: type, props: prev } = tag;
119
+ switch (type) {
120
+ // title: textContent is escaped in rendering (tagToString), no props needed
121
+ case "title":
122
+ break;
123
+ // virtual tags, not rendered to HTML — but sanitize to prevent injection if rendered
124
+ case "titleTemplate":
125
+ case "templateParams":
126
+ next = prev;
127
+ break;
128
+ case "htmlAttrs":
129
+ case "bodyAttrs":
130
+ WhitelistAttributes[type].forEach((attr) => {
131
+ if (prev[attr]) {
132
+ next[attr] = prev[attr];
133
+ }
134
+ });
135
+ delete tag.innerHTML;
136
+ delete tag.textContent;
137
+ tag.props = { ...acceptDataAttrs(prev, false), ...next };
138
+ return !Object.keys(tag.props).length ? false : tag;
139
+ case "style":
140
+ next = acceptDataAttrs(prev);
141
+ WhitelistAttributes.style.forEach((key) => {
142
+ if (prev[key]) {
143
+ next[key] = prev[key];
144
+ }
145
+ });
146
+ break;
147
+ // meta is safe, except for http-equiv
148
+ case "meta":
149
+ WhitelistAttributes.meta.forEach((key) => {
150
+ if (prev[key]) {
151
+ next[key] = prev[key];
152
+ }
153
+ });
154
+ break;
155
+ // link tags we block preloading, prerendering, prefetching, dns-prefetch, preconnect, manifest, etc
156
+ case "link":
157
+ WhitelistAttributes.link.forEach((key) => {
158
+ const val = prev[key];
159
+ if (!val) {
160
+ return;
161
+ }
162
+ if (key === "rel" && (typeof val !== "string" || BlockedLinkRels.has(val.toLowerCase()))) {
163
+ return;
164
+ }
165
+ if (key === "href" || key === "imagesrcset") {
166
+ if (typeof val !== "string") {
167
+ return;
168
+ }
169
+ const urls = key === "imagesrcset" ? val.split(",").map((s) => s.trim()) : [val];
170
+ if (urls.some((u) => hasDangerousProtocol(u))) {
171
+ return;
172
+ }
173
+ next[key] = val;
174
+ } else if (val) {
175
+ next[key] = val;
176
+ }
177
+ });
178
+ if (!next.href && !next.imagesrcset || !next.rel) {
179
+ return false;
180
+ }
181
+ break;
182
+ case "noscript":
183
+ WhitelistAttributes.noscript.forEach((key) => {
184
+ if (prev[key]) {
185
+ next[key] = prev[key];
186
+ }
187
+ });
188
+ break;
189
+ // we only allow JSON in scripts
190
+ case "script":
191
+ if (!tag.textContent || typeof prev.type !== "string" || !prev.type.endsWith("json")) {
192
+ return false;
193
+ }
194
+ try {
195
+ const jsonVal = typeof tag.textContent === "string" ? JSON.parse(tag.textContent) : tag.textContent;
196
+ tag.textContent = JSON.stringify(stripProtoKeys(jsonVal), null, 0);
197
+ } catch {
198
+ return false;
199
+ }
200
+ WhitelistAttributes.script.forEach((s) => {
201
+ if (s !== "textContent" && prev[s]) {
202
+ next[s] = prev[s];
203
+ }
204
+ });
205
+ break;
206
+ }
207
+ delete tag.innerHTML;
208
+ if (type !== "title" && type !== "script") {
209
+ delete tag.textContent;
210
+ }
211
+ tag.props = { ...acceptDataAttrs(prev), ...next };
212
+ if (!Object.keys(tag.props).length && !tag.tag.endsWith("Attrs") && !tag.textContent) {
213
+ return false;
214
+ }
215
+ return tag;
216
+ }
217
+ const SafeInputPlugin = (
218
+ /* @PURE */
219
+ defineHeadPlugin({
220
+ key: "safe",
221
+ hooks: {
222
+ "entries:normalize": (ctx) => {
223
+ if (ctx.entry.options?._safe) {
224
+ ctx.tags = ctx.tags.reduce((acc, tag) => {
225
+ const safeTag = makeTagSafe(tag);
226
+ if (safeTag)
227
+ acc.push(safeTag);
228
+ return acc;
229
+ }, []);
230
+ }
231
+ }
232
+ }
233
+ })
234
+ );
235
+
236
+ export { FlatMetaPlugin as F, SafeInputPlugin as S, defineHeadPlugin as d };
@@ -1,4 +1,4 @@
1
- import { U as UniqueTags, T as TagsWithInnerContent, M as MetaTagsArrayable, a as TagConfigKeys, D as DupeableTags } from './unhead.yem5I2v_.mjs';
1
+ import { U as UniqueTags, T as TagsWithInnerContent, M as MetaTagsArrayable, H as HasElementTags, a as TagConfigKeys, D as DupeableTags } from './unhead.yem5I2v_.mjs';
2
2
 
3
3
  const allowedMetaProperties = ["name", "property", "http-equiv"];
4
4
  const StandardSingleMetaTags = /* @__PURE__ */ new Set([
@@ -124,17 +124,20 @@ function normalizeProps(tag, input) {
124
124
  tag.props = input;
125
125
  return tag;
126
126
  }
127
- Object.entries(input).forEach(([key, value]) => {
127
+ const isHtmlTag = HasElementTags.has(tag.tag) || tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs";
128
+ Object.entries(input).forEach(([prop, value]) => {
129
+ if (prop === "__proto__" || prop === "constructor" || prop === "prototype")
130
+ return;
128
131
  if (value === null) {
129
- tag.props[key] = null;
132
+ tag.props[prop] = null;
130
133
  return;
131
134
  }
132
- if (key === "class" || key === "style") {
133
- tag.props[key] = normalizeStyleClassProps(key, value);
135
+ if (prop === "class" || prop === "style") {
136
+ tag.props[prop] = normalizeStyleClassProps(prop, value);
134
137
  return;
135
138
  }
136
- if (TagConfigKeys.has(key)) {
137
- if (["textContent", "innerHTML"].includes(key) && typeof value === "object") {
139
+ if (TagConfigKeys.has(prop)) {
140
+ if ((prop === "textContent" || prop === "innerHTML") && typeof value === "object") {
138
141
  let type = input.type;
139
142
  if (!input.type) {
140
143
  type = "application/json";
@@ -144,14 +147,15 @@ function normalizeProps(tag, input) {
144
147
  }
145
148
  input.type = type;
146
149
  tag.props.type = type;
147
- tag[key] = JSON.stringify(value);
150
+ tag[prop] = JSON.stringify(value);
148
151
  } else {
149
- tag[key] = value;
152
+ tag[prop] = value;
150
153
  }
151
154
  return;
152
155
  }
156
+ const isDataKey = prop.startsWith("data-");
157
+ const key = isHtmlTag && !isDataKey ? prop.toLowerCase() : prop;
153
158
  const strValue = String(value);
154
- const isDataKey = key.startsWith("data-");
155
159
  const isMetaContentKey = tag.tag === "meta" && key === "content";
156
160
  if (strValue === "true" || strValue === "") {
157
161
  tag.props[key] = isDataKey || isMetaContentKey ? strValue : true;
@@ -1,4 +1,4 @@
1
- import { d as defineHeadPlugin } from './unhead.CApf5sj3.mjs';
1
+ import { d as defineHeadPlugin } from './unhead.0eF0fa-Y.mjs';
2
2
  import { s as sortTags } from './unhead.CbpEuj3y.mjs';
3
3
  import { p as processTemplateParams } from './unhead.BYvz9V1x.mjs';
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { createHooks } from 'hookable';
2
- import { n as normalizeEntryToTags, d as dedupeKey, h as hashTag, i as isMetaArrayDupeKey } from './unhead.fVVqDC1O.mjs';
2
+ import { n as normalizeEntryToTags, d as dedupeKey, h as hashTag, i as isMetaArrayDupeKey } from './unhead.B5FWS6X0.mjs';
3
3
  import { t as tagWeight, s as sortTags } from './unhead.CbpEuj3y.mjs';
4
4
  import { c as UsesMergeStrategy, V as ValidHeadTags } from './unhead.yem5I2v_.mjs';
5
5
 
@@ -1,4 +1,4 @@
1
- import { S as SafeInputPlugin, F as FlatMetaPlugin } from './unhead.CApf5sj3.mjs';
1
+ import { S as SafeInputPlugin, F as FlatMetaPlugin } from './unhead.0eF0fa-Y.mjs';
2
2
 
3
3
  function useHead(unhead, input, options = {}) {
4
4
  return unhead.push(input || {}, options);
package/dist/utils.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  export { D as DupeableTags, H as HasElementTags, M as MetaTagsArrayable, b as ScriptNetworkEvents, S as SelfClosingTags, a as TagConfigKeys, T as TagsWithInnerContent, U as UniqueTags, c as UsesMergeStrategy, V as ValidHeadTags } from './shared/unhead.yem5I2v_.mjs';
2
- export { d as dedupeKey, h as hashTag, i as isMetaArrayDupeKey, n as normalizeEntryToTags, a as normalizeProps, w as walkResolver } from './shared/unhead.fVVqDC1O.mjs';
2
+ export { d as dedupeKey, h as hashTag, i as isMetaArrayDupeKey, n as normalizeEntryToTags, a as normalizeProps, w as walkResolver } from './shared/unhead.B5FWS6X0.mjs';
3
3
  export { r as resolveMetaKeyType, a as resolveMetaKeyValue, b as resolvePackedMetaObjectValue, u as unpackMeta } from './shared/unhead.DQc16pHI.mjs';
4
4
  export { s as sortTags, t as tagWeight } from './shared/unhead.CbpEuj3y.mjs';
5
5
  export { p as processTemplateParams } from './shared/unhead.BYvz9V1x.mjs';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "unhead",
3
3
  "type": "module",
4
- "version": "2.1.10",
4
+ "version": "2.1.12",
5
5
  "description": "Full-stack <head> manager built for any framework.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -1,148 +0,0 @@
1
- import { u as unpackMeta } from './unhead.DQc16pHI.mjs';
2
-
3
- function defineHeadPlugin(plugin) {
4
- return plugin;
5
- }
6
-
7
- const FlatMetaPlugin = /* @__PURE__ */ defineHeadPlugin({
8
- key: "flatMeta",
9
- hooks: {
10
- "entries:normalize": (ctx) => {
11
- const tagsToAdd = [];
12
- ctx.tags = ctx.tags.map((t) => {
13
- if (t.tag !== "_flatMeta") {
14
- return t;
15
- }
16
- tagsToAdd.push(unpackMeta(t.props).map((p) => ({
17
- ...t,
18
- tag: "meta",
19
- props: p
20
- })));
21
- return false;
22
- }).filter(Boolean).concat(...tagsToAdd);
23
- }
24
- }
25
- });
26
-
27
- const WhitelistAttributes = {
28
- htmlAttrs: /* @__PURE__ */ new Set(["class", "style", "lang", "dir"]),
29
- bodyAttrs: /* @__PURE__ */ new Set(["class", "style"]),
30
- meta: /* @__PURE__ */ new Set(["name", "property", "charset", "content", "media"]),
31
- noscript: /* @__PURE__ */ new Set(["textContent"]),
32
- style: /* @__PURE__ */ new Set(["media", "textContent", "nonce", "title", "blocking"]),
33
- script: /* @__PURE__ */ new Set(["type", "textContent", "nonce", "blocking"]),
34
- link: /* @__PURE__ */ new Set(["color", "crossorigin", "fetchpriority", "href", "hreflang", "imagesrcset", "imagesizes", "integrity", "media", "referrerpolicy", "rel", "sizes", "type"])
35
- };
36
- function acceptDataAttrs(value) {
37
- return Object.fromEntries(
38
- Object.entries(value || {}).filter(([key]) => key === "id" || key.startsWith("data-"))
39
- );
40
- }
41
- function makeTagSafe(tag) {
42
- let next = {};
43
- const { tag: type, props: prev } = tag;
44
- switch (type) {
45
- // always safe
46
- case "title":
47
- case "titleTemplate":
48
- case "templateParams":
49
- next = prev;
50
- break;
51
- case "htmlAttrs":
52
- case "bodyAttrs":
53
- WhitelistAttributes[type].forEach((attr) => {
54
- if (prev[attr]) {
55
- next[attr] = prev[attr];
56
- }
57
- });
58
- break;
59
- case "style":
60
- next = acceptDataAttrs(prev);
61
- WhitelistAttributes.style.forEach((key) => {
62
- if (prev[key]) {
63
- next[key] = prev[key];
64
- }
65
- });
66
- break;
67
- // meta is safe, except for http-equiv
68
- case "meta":
69
- WhitelistAttributes.meta.forEach((key) => {
70
- if (prev[key]) {
71
- next[key] = prev[key];
72
- }
73
- });
74
- break;
75
- // link tags we don't allow stylesheets, scripts, preloading, prerendering, prefetching, etc
76
- case "link":
77
- WhitelistAttributes.link.forEach((key) => {
78
- const val = prev[key];
79
- if (!val) {
80
- return;
81
- }
82
- if (key === "rel" && (val === "canonical" || val === "modulepreload" || val === "prerender" || val === "preload" || val === "prefetch")) {
83
- return;
84
- }
85
- if (key === "href") {
86
- if (val.includes("javascript:") || val.includes("data:")) {
87
- return;
88
- }
89
- next[key] = val;
90
- } else if (val) {
91
- next[key] = val;
92
- }
93
- });
94
- if (!next.href && !next.imagesrcset || !next.rel) {
95
- return false;
96
- }
97
- break;
98
- case "noscript":
99
- WhitelistAttributes.noscript.forEach((key) => {
100
- if (prev[key]) {
101
- next[key] = prev[key];
102
- }
103
- });
104
- break;
105
- // we only allow JSON in scripts
106
- case "script":
107
- if (!tag.textContent || !prev.type?.endsWith("json")) {
108
- return false;
109
- }
110
- WhitelistAttributes.script.forEach((s) => {
111
- if (prev[s] === "textContent") {
112
- try {
113
- const jsonVal = typeof prev[s] === "string" ? JSON.parse(prev[s]) : prev[s];
114
- next[s] = JSON.stringify(jsonVal, null, 0);
115
- } catch {
116
- }
117
- } else if (prev[s]) {
118
- next[s] = prev[s];
119
- }
120
- });
121
- break;
122
- }
123
- if (!Object.keys(next).length && !tag.tag.endsWith("Attrs")) {
124
- return false;
125
- }
126
- tag.props = { ...acceptDataAttrs(prev), ...next };
127
- return tag;
128
- }
129
- const SafeInputPlugin = (
130
- /* @PURE */
131
- defineHeadPlugin({
132
- key: "safe",
133
- hooks: {
134
- "entries:normalize": (ctx) => {
135
- if (ctx.entry.options?._safe) {
136
- ctx.tags = ctx.tags.reduce((acc, tag) => {
137
- const safeTag = makeTagSafe(tag);
138
- if (safeTag)
139
- acc.push(safeTag);
140
- return acc;
141
- }, []);
142
- }
143
- }
144
- }
145
- })
146
- );
147
-
148
- export { FlatMetaPlugin as F, SafeInputPlugin as S, defineHeadPlugin as d };