@unhead/shared 1.10.4 → 1.11.0-beta.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
@@ -59,39 +59,11 @@ function hashTag(tag) {
59
59
  }
60
60
  let content = `${tag.tag}:${tag.textContent || tag.innerHTML || ""}:`;
61
61
  for (const key in tag.props) {
62
- content += `${key}:${tag.props[key]},`;
62
+ content += `${key}:${String(tag.props[key])},`;
63
63
  }
64
64
  return hashCode(content);
65
65
  }
66
66
 
67
- const allowedMetaProperties = ["name", "property", "http-equiv"];
68
- function tagDedupeKey(tag) {
69
- const { props, tag: tagName } = tag;
70
- if (UniqueTags.has(tagName))
71
- return tagName;
72
- if (tagName === "link" && props.rel === "canonical")
73
- return "canonical";
74
- if (props.charset)
75
- return "charset";
76
- if (props.id) {
77
- return `${tagName}:id:${props.id}`;
78
- }
79
- for (const n of allowedMetaProperties) {
80
- if (props[n] !== void 0) {
81
- return `${tagName}:${n}:${props[n]}`;
82
- }
83
- }
84
- return false;
85
- }
86
-
87
- function resolveTitleTemplate(template, title) {
88
- if (template == null)
89
- return title || null;
90
- if (typeof template === "function")
91
- return template(title);
92
- return template;
93
- }
94
-
95
67
  function asArray(input) {
96
68
  return Array.isArray(input) ? input : [input];
97
69
  }
@@ -409,111 +381,6 @@ function packMeta(inputs) {
409
381
  });
410
382
  }
411
383
 
412
- const WhitelistAttributes = {
413
- htmlAttrs: ["id", "class", "lang", "dir"],
414
- bodyAttrs: ["id", "class"],
415
- meta: ["id", "name", "property", "charset", "content"],
416
- noscript: ["id", "textContent"],
417
- script: ["id", "type", "textContent"],
418
- link: ["id", "color", "crossorigin", "fetchpriority", "href", "hreflang", "imagesrcset", "imagesizes", "integrity", "media", "referrerpolicy", "rel", "sizes", "type"]
419
- };
420
- function acceptDataAttrs(value) {
421
- const filtered = {};
422
- Object.keys(value || {}).filter((a) => a.startsWith("data-")).forEach((a) => {
423
- filtered[a] = value[a];
424
- });
425
- return filtered;
426
- }
427
- function whitelistSafeInput(input) {
428
- const filtered = {};
429
- Object.keys(input).forEach((key) => {
430
- const tagValue = input[key];
431
- if (!tagValue)
432
- return;
433
- switch (key) {
434
- case "title":
435
- case "titleTemplate":
436
- case "templateParams":
437
- filtered[key] = tagValue;
438
- break;
439
- case "htmlAttrs":
440
- case "bodyAttrs":
441
- filtered[key] = acceptDataAttrs(tagValue);
442
- WhitelistAttributes[key].forEach((a) => {
443
- if (tagValue[a])
444
- filtered[key][a] = tagValue[a];
445
- });
446
- break;
447
- case "meta":
448
- if (Array.isArray(tagValue)) {
449
- filtered[key] = tagValue.map((meta) => {
450
- const safeMeta = acceptDataAttrs(meta);
451
- WhitelistAttributes.meta.forEach((key2) => {
452
- if (meta[key2])
453
- safeMeta[key2] = meta[key2];
454
- });
455
- return safeMeta;
456
- }).filter((meta) => Object.keys(meta).length > 0);
457
- }
458
- break;
459
- case "link":
460
- if (Array.isArray(tagValue)) {
461
- filtered[key] = tagValue.map((meta) => {
462
- const link = acceptDataAttrs(meta);
463
- WhitelistAttributes.link.forEach((key2) => {
464
- const val = meta[key2];
465
- if (key2 === "rel" && (val === "stylesheet" || val === "canonical" || val === "modulepreload" || val === "prerender" || val === "preload" || val === "prefetch"))
466
- return;
467
- if (key2 === "href") {
468
- if (val.includes("javascript:") || val.includes("data:"))
469
- return;
470
- link[key2] = val;
471
- } else if (val) {
472
- link[key2] = val;
473
- }
474
- });
475
- return link;
476
- }).filter((link) => Object.keys(link).length > 1 && !!link.rel);
477
- }
478
- break;
479
- case "noscript":
480
- if (Array.isArray(tagValue)) {
481
- filtered[key] = tagValue.map((meta) => {
482
- const noscript = acceptDataAttrs(meta);
483
- WhitelistAttributes.noscript.forEach((key2) => {
484
- if (meta[key2])
485
- noscript[key2] = meta[key2];
486
- });
487
- return noscript;
488
- }).filter((meta) => Object.keys(meta).length > 0);
489
- }
490
- break;
491
- case "script":
492
- if (Array.isArray(tagValue)) {
493
- filtered[key] = tagValue.map((script) => {
494
- const safeScript = acceptDataAttrs(script);
495
- WhitelistAttributes.script.forEach((s) => {
496
- if (script[s]) {
497
- if (s === "textContent") {
498
- try {
499
- const jsonVal = typeof script[s] === "string" ? JSON.parse(script[s]) : script[s];
500
- safeScript[s] = JSON.stringify(jsonVal, null, 0);
501
- } catch (e) {
502
- }
503
- } else {
504
- safeScript[s] = script[s];
505
- }
506
- }
507
- });
508
- return safeScript;
509
- }).filter((meta) => Object.keys(meta).length > 0);
510
- }
511
- break;
512
- }
513
- });
514
- return filtered;
515
- }
516
-
517
384
  function thenable(val, thenFn) {
518
385
  if (val instanceof Promise) {
519
386
  return val.then(thenFn);
@@ -558,7 +425,7 @@ function normaliseTag(tagName, input, e, normalizedProps) {
558
425
  }
559
426
  function normaliseStyleClassProps(key, v) {
560
427
  const sep = key === "class" ? " " : ";";
561
- if (typeof v === "object" && !Array.isArray(v)) {
428
+ if (v && typeof v === "object" && !Array.isArray(v)) {
562
429
  v = Object.entries(v).filter(([, v2]) => v2).map(([k, v2]) => key === "style" ? `${k}:${v2}` : k);
563
430
  }
564
431
  return String(Array.isArray(v) ? v.join(sep) : v)?.split(sep).filter((c) => Boolean(c.trim())).join(sep);
@@ -580,7 +447,7 @@ function nestedNormaliseProps(props, virtual, keys, startIndex) {
580
447
  const v = String(props[k]);
581
448
  const isDataKey = k.startsWith("data-");
582
449
  if (v === "true" || v === "") {
583
- props[k] = isDataKey ? "true" : true;
450
+ props[k] = isDataKey ? props[k] : true;
584
451
  } else if (!props[k]) {
585
452
  if (isDataKey && v === "false")
586
453
  props[k] = "false";
@@ -645,6 +512,114 @@ function normaliseEntryTags(e) {
645
512
  }));
646
513
  }
647
514
 
515
+ const WhitelistAttributes = {
516
+ htmlAttrs: ["id", "class", "lang", "dir"],
517
+ bodyAttrs: ["id", "class"],
518
+ meta: ["id", "name", "property", "charset", "content"],
519
+ noscript: ["id", "textContent"],
520
+ script: ["id", "type", "textContent"],
521
+ link: ["id", "color", "crossorigin", "fetchpriority", "href", "hreflang", "imagesrcset", "imagesizes", "integrity", "media", "referrerpolicy", "rel", "sizes", "type"]
522
+ };
523
+ function acceptDataAttrs(value) {
524
+ const filtered = {};
525
+ Object.keys(value || {}).filter((a) => a.startsWith("data-")).forEach((a) => {
526
+ filtered[a] = value[a];
527
+ });
528
+ return filtered;
529
+ }
530
+ function whitelistSafeInput(input) {
531
+ const filtered = {};
532
+ Object.keys(input).forEach((key) => {
533
+ const tagValue = input[key];
534
+ if (!tagValue)
535
+ return;
536
+ switch (key) {
537
+ case "title":
538
+ case "titleTemplate":
539
+ case "templateParams":
540
+ filtered[key] = tagValue;
541
+ break;
542
+ case "htmlAttrs":
543
+ case "bodyAttrs":
544
+ filtered[key] = acceptDataAttrs(tagValue);
545
+ WhitelistAttributes[key].forEach((a) => {
546
+ if (tagValue[a])
547
+ filtered[key][a] = tagValue[a];
548
+ });
549
+ break;
550
+ case "meta":
551
+ if (Array.isArray(tagValue)) {
552
+ filtered[key] = tagValue.map((meta) => {
553
+ const safeMeta = acceptDataAttrs(meta);
554
+ WhitelistAttributes.meta.forEach((key2) => {
555
+ if (meta[key2])
556
+ safeMeta[key2] = meta[key2];
557
+ });
558
+ return safeMeta;
559
+ }).filter((meta) => Object.keys(meta).length > 0);
560
+ }
561
+ break;
562
+ case "link":
563
+ if (Array.isArray(tagValue)) {
564
+ filtered[key] = tagValue.map((meta) => {
565
+ const link = acceptDataAttrs(meta);
566
+ WhitelistAttributes.link.forEach((key2) => {
567
+ const val = meta[key2];
568
+ if (key2 === "rel" && (val === "stylesheet" || val === "canonical" || val === "modulepreload" || val === "prerender" || val === "preload" || val === "prefetch"))
569
+ return;
570
+ if (key2 === "href") {
571
+ if (val.includes("javascript:") || val.includes("data:"))
572
+ return;
573
+ link[key2] = val;
574
+ } else if (val) {
575
+ link[key2] = val;
576
+ }
577
+ });
578
+ return link;
579
+ }).filter((link) => Object.keys(link).length > 1 && !!link.rel);
580
+ }
581
+ break;
582
+ case "noscript":
583
+ if (Array.isArray(tagValue)) {
584
+ filtered[key] = tagValue.map((meta) => {
585
+ const noscript = acceptDataAttrs(meta);
586
+ WhitelistAttributes.noscript.forEach((key2) => {
587
+ if (meta[key2])
588
+ noscript[key2] = meta[key2];
589
+ });
590
+ return noscript;
591
+ }).filter((meta) => Object.keys(meta).length > 0);
592
+ }
593
+ break;
594
+ case "script":
595
+ if (Array.isArray(tagValue)) {
596
+ filtered[key] = tagValue.map((script) => {
597
+ const safeScript = acceptDataAttrs(script);
598
+ WhitelistAttributes.script.forEach((s) => {
599
+ if (script[s]) {
600
+ if (s === "textContent") {
601
+ try {
602
+ const jsonVal = typeof script[s] === "string" ? JSON.parse(script[s]) : script[s];
603
+ safeScript[s] = JSON.stringify(jsonVal, null, 0);
604
+ } catch (e) {
605
+ }
606
+ } else {
607
+ safeScript[s] = script[s];
608
+ }
609
+ }
610
+ });
611
+ return safeScript;
612
+ }).filter((meta) => Object.keys(meta).length > 0);
613
+ }
614
+ break;
615
+ }
616
+ });
617
+ return filtered;
618
+ }
619
+
620
+ const NetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror", "onabort", "onprogress", "onloadstart"]);
621
+ const ScriptNetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror"]);
622
+
648
623
  const TAG_WEIGHTS = {
649
624
  // tags
650
625
  base: -10,
@@ -680,8 +655,25 @@ function tagWeight(tag) {
680
655
  }
681
656
  const SortModifiers = [{ prefix: "before:", offset: -1 }, { prefix: "after:", offset: 1 }];
682
657
 
683
- const NetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror", "onabort", "onprogress", "onloadstart"]);
684
- const ScriptNetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror"]);
658
+ const allowedMetaProperties = ["name", "property", "http-equiv"];
659
+ function tagDedupeKey(tag) {
660
+ const { props, tag: tagName } = tag;
661
+ if (UniqueTags.has(tagName))
662
+ return tagName;
663
+ if (tagName === "link" && props.rel === "canonical")
664
+ return "canonical";
665
+ if (props.charset)
666
+ return "charset";
667
+ if (props.id) {
668
+ return `${tagName}:id:${props.id}`;
669
+ }
670
+ for (const n of allowedMetaProperties) {
671
+ if (props[n] !== void 0) {
672
+ return `${tagName}:${n}:${props[n]}`;
673
+ }
674
+ }
675
+ return false;
676
+ }
685
677
 
686
678
  const sepSub = "%separator";
687
679
  function sub(p, token) {
@@ -727,6 +719,14 @@ function processTemplateParams(s, p, sep) {
727
719
  return s;
728
720
  }
729
721
 
722
+ function resolveTitleTemplate(template, title) {
723
+ if (template == null)
724
+ return title || null;
725
+ if (typeof template === "function")
726
+ return template(title);
727
+ return template;
728
+ }
729
+
730
730
  exports.HasElementTags = HasElementTags;
731
731
  exports.IsBrowser = IsBrowser;
732
732
  exports.NetworkEvents = NetworkEvents;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { HeadPluginInput, HeadTag, BaseMeta, MetaFlatInput, Head, MaybeArray, HeadSafe, HeadEntry, TemplateParams } from '@unhead/schema';
1
+ import { HeadPluginInput, HeadTag, BaseMeta, MetaFlatInput, Head, HeadEntry, MaybeArray, HeadSafe, TemplateParams } from '@unhead/schema';
2
2
 
3
3
  type Arrayable<T> = T | Array<T>;
4
4
  declare function asArray<T>(value: Arrayable<T>): T[];
@@ -17,10 +17,6 @@ declare function defineHeadPlugin(plugin: HeadPluginInput): HeadPluginInput;
17
17
  declare function hashCode(s: string): string;
18
18
  declare function hashTag(tag: HeadTag): string;
19
19
 
20
- declare function tagDedupeKey<T extends HeadTag>(tag: T): string | false;
21
-
22
- declare function resolveTitleTemplate(template: string | ((title?: string) => string | null) | null, title?: string): string | null;
23
-
24
20
  declare function resolveMetaKeyType(key: string): keyof BaseMeta;
25
21
  declare function resolveMetaKeyValue(key: string): string;
26
22
  declare function resolvePackedMetaObjectValue(value: string, key: string): string;
@@ -35,8 +31,6 @@ declare function unpackMeta<T extends MetaFlatInput>(input: T): Required<Head>['
35
31
  */
36
32
  declare function packMeta<T extends Required<Head>['meta']>(inputs: T): MetaFlatInput;
37
33
 
38
- declare function whitelistSafeInput(input: Record<string, MaybeArray<Record<string, string>>>): HeadSafe;
39
-
40
34
  type Thenable<T> = Promise<T> | T;
41
35
  declare function thenable<T, R>(val: T, thenFn: (val: Awaited<T>) => R): Promise<R> | R;
42
36
 
@@ -46,6 +40,11 @@ declare function normaliseProps<T extends HeadTag>(props: T['props'], virtual?:
46
40
  declare const TagEntityBits = 10;
47
41
  declare function normaliseEntryTags<T extends object = Head>(e: HeadEntry<T>): Thenable<HeadTag[]>;
48
42
 
43
+ declare function whitelistSafeInput(input: Record<string, MaybeArray<Record<string, string>>>): HeadSafe;
44
+
45
+ declare const NetworkEvents: Set<string>;
46
+ declare const ScriptNetworkEvents: Set<string>;
47
+
49
48
  declare const TAG_WEIGHTS: {
50
49
  readonly base: -10;
51
50
  readonly title: 10;
@@ -61,9 +60,10 @@ declare const SortModifiers: {
61
60
  offset: number;
62
61
  }[];
63
62
 
64
- declare const NetworkEvents: Set<string>;
65
- declare const ScriptNetworkEvents: Set<string>;
63
+ declare function tagDedupeKey<T extends HeadTag>(tag: T): string | false;
66
64
 
67
65
  declare function processTemplateParams(s: string, p: TemplateParams, sep: string): string;
68
66
 
67
+ declare function resolveTitleTemplate(template: string | ((title?: string) => string | null) | null, title?: string): string | null;
68
+
69
69
  export { type Arrayable, HasElementTags, IsBrowser, NetworkEvents, ScriptNetworkEvents, SelfClosingTags, SortModifiers, TAG_ALIASES, TAG_WEIGHTS, TagConfigKeys, TagEntityBits, TagsWithInnerContent, type Thenable, UniqueTags, ValidHeadTags, asArray, composableNames, defineHeadPlugin, hashCode, hashTag, normaliseEntryTags, normaliseProps, normaliseStyleClassProps, normaliseTag, packMeta, processTemplateParams, resolveMetaKeyType, resolveMetaKeyValue, resolvePackedMetaObjectValue, resolveTitleTemplate, tagDedupeKey, tagWeight, thenable, unpackMeta, whitelistSafeInput };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { HeadPluginInput, HeadTag, BaseMeta, MetaFlatInput, Head, MaybeArray, HeadSafe, HeadEntry, TemplateParams } from '@unhead/schema';
1
+ import { HeadPluginInput, HeadTag, BaseMeta, MetaFlatInput, Head, HeadEntry, MaybeArray, HeadSafe, TemplateParams } from '@unhead/schema';
2
2
 
3
3
  type Arrayable<T> = T | Array<T>;
4
4
  declare function asArray<T>(value: Arrayable<T>): T[];
@@ -17,10 +17,6 @@ declare function defineHeadPlugin(plugin: HeadPluginInput): HeadPluginInput;
17
17
  declare function hashCode(s: string): string;
18
18
  declare function hashTag(tag: HeadTag): string;
19
19
 
20
- declare function tagDedupeKey<T extends HeadTag>(tag: T): string | false;
21
-
22
- declare function resolveTitleTemplate(template: string | ((title?: string) => string | null) | null, title?: string): string | null;
23
-
24
20
  declare function resolveMetaKeyType(key: string): keyof BaseMeta;
25
21
  declare function resolveMetaKeyValue(key: string): string;
26
22
  declare function resolvePackedMetaObjectValue(value: string, key: string): string;
@@ -35,8 +31,6 @@ declare function unpackMeta<T extends MetaFlatInput>(input: T): Required<Head>['
35
31
  */
36
32
  declare function packMeta<T extends Required<Head>['meta']>(inputs: T): MetaFlatInput;
37
33
 
38
- declare function whitelistSafeInput(input: Record<string, MaybeArray<Record<string, string>>>): HeadSafe;
39
-
40
34
  type Thenable<T> = Promise<T> | T;
41
35
  declare function thenable<T, R>(val: T, thenFn: (val: Awaited<T>) => R): Promise<R> | R;
42
36
 
@@ -46,6 +40,11 @@ declare function normaliseProps<T extends HeadTag>(props: T['props'], virtual?:
46
40
  declare const TagEntityBits = 10;
47
41
  declare function normaliseEntryTags<T extends object = Head>(e: HeadEntry<T>): Thenable<HeadTag[]>;
48
42
 
43
+ declare function whitelistSafeInput(input: Record<string, MaybeArray<Record<string, string>>>): HeadSafe;
44
+
45
+ declare const NetworkEvents: Set<string>;
46
+ declare const ScriptNetworkEvents: Set<string>;
47
+
49
48
  declare const TAG_WEIGHTS: {
50
49
  readonly base: -10;
51
50
  readonly title: 10;
@@ -61,9 +60,10 @@ declare const SortModifiers: {
61
60
  offset: number;
62
61
  }[];
63
62
 
64
- declare const NetworkEvents: Set<string>;
65
- declare const ScriptNetworkEvents: Set<string>;
63
+ declare function tagDedupeKey<T extends HeadTag>(tag: T): string | false;
66
64
 
67
65
  declare function processTemplateParams(s: string, p: TemplateParams, sep: string): string;
68
66
 
67
+ declare function resolveTitleTemplate(template: string | ((title?: string) => string | null) | null, title?: string): string | null;
68
+
69
69
  export { type Arrayable, HasElementTags, IsBrowser, NetworkEvents, ScriptNetworkEvents, SelfClosingTags, SortModifiers, TAG_ALIASES, TAG_WEIGHTS, TagConfigKeys, TagEntityBits, TagsWithInnerContent, type Thenable, UniqueTags, ValidHeadTags, asArray, composableNames, defineHeadPlugin, hashCode, hashTag, normaliseEntryTags, normaliseProps, normaliseStyleClassProps, normaliseTag, packMeta, processTemplateParams, resolveMetaKeyType, resolveMetaKeyValue, resolvePackedMetaObjectValue, resolveTitleTemplate, tagDedupeKey, tagWeight, thenable, unpackMeta, whitelistSafeInput };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { HeadPluginInput, HeadTag, BaseMeta, MetaFlatInput, Head, MaybeArray, HeadSafe, HeadEntry, TemplateParams } from '@unhead/schema';
1
+ import { HeadPluginInput, HeadTag, BaseMeta, MetaFlatInput, Head, HeadEntry, MaybeArray, HeadSafe, TemplateParams } from '@unhead/schema';
2
2
 
3
3
  type Arrayable<T> = T | Array<T>;
4
4
  declare function asArray<T>(value: Arrayable<T>): T[];
@@ -17,10 +17,6 @@ declare function defineHeadPlugin(plugin: HeadPluginInput): HeadPluginInput;
17
17
  declare function hashCode(s: string): string;
18
18
  declare function hashTag(tag: HeadTag): string;
19
19
 
20
- declare function tagDedupeKey<T extends HeadTag>(tag: T): string | false;
21
-
22
- declare function resolveTitleTemplate(template: string | ((title?: string) => string | null) | null, title?: string): string | null;
23
-
24
20
  declare function resolveMetaKeyType(key: string): keyof BaseMeta;
25
21
  declare function resolveMetaKeyValue(key: string): string;
26
22
  declare function resolvePackedMetaObjectValue(value: string, key: string): string;
@@ -35,8 +31,6 @@ declare function unpackMeta<T extends MetaFlatInput>(input: T): Required<Head>['
35
31
  */
36
32
  declare function packMeta<T extends Required<Head>['meta']>(inputs: T): MetaFlatInput;
37
33
 
38
- declare function whitelistSafeInput(input: Record<string, MaybeArray<Record<string, string>>>): HeadSafe;
39
-
40
34
  type Thenable<T> = Promise<T> | T;
41
35
  declare function thenable<T, R>(val: T, thenFn: (val: Awaited<T>) => R): Promise<R> | R;
42
36
 
@@ -46,6 +40,11 @@ declare function normaliseProps<T extends HeadTag>(props: T['props'], virtual?:
46
40
  declare const TagEntityBits = 10;
47
41
  declare function normaliseEntryTags<T extends object = Head>(e: HeadEntry<T>): Thenable<HeadTag[]>;
48
42
 
43
+ declare function whitelistSafeInput(input: Record<string, MaybeArray<Record<string, string>>>): HeadSafe;
44
+
45
+ declare const NetworkEvents: Set<string>;
46
+ declare const ScriptNetworkEvents: Set<string>;
47
+
49
48
  declare const TAG_WEIGHTS: {
50
49
  readonly base: -10;
51
50
  readonly title: 10;
@@ -61,9 +60,10 @@ declare const SortModifiers: {
61
60
  offset: number;
62
61
  }[];
63
62
 
64
- declare const NetworkEvents: Set<string>;
65
- declare const ScriptNetworkEvents: Set<string>;
63
+ declare function tagDedupeKey<T extends HeadTag>(tag: T): string | false;
66
64
 
67
65
  declare function processTemplateParams(s: string, p: TemplateParams, sep: string): string;
68
66
 
67
+ declare function resolveTitleTemplate(template: string | ((title?: string) => string | null) | null, title?: string): string | null;
68
+
69
69
  export { type Arrayable, HasElementTags, IsBrowser, NetworkEvents, ScriptNetworkEvents, SelfClosingTags, SortModifiers, TAG_ALIASES, TAG_WEIGHTS, TagConfigKeys, TagEntityBits, TagsWithInnerContent, type Thenable, UniqueTags, ValidHeadTags, asArray, composableNames, defineHeadPlugin, hashCode, hashTag, normaliseEntryTags, normaliseProps, normaliseStyleClassProps, normaliseTag, packMeta, processTemplateParams, resolveMetaKeyType, resolveMetaKeyValue, resolvePackedMetaObjectValue, resolveTitleTemplate, tagDedupeKey, tagWeight, thenable, unpackMeta, whitelistSafeInput };
package/dist/index.mjs CHANGED
@@ -57,39 +57,11 @@ function hashTag(tag) {
57
57
  }
58
58
  let content = `${tag.tag}:${tag.textContent || tag.innerHTML || ""}:`;
59
59
  for (const key in tag.props) {
60
- content += `${key}:${tag.props[key]},`;
60
+ content += `${key}:${String(tag.props[key])},`;
61
61
  }
62
62
  return hashCode(content);
63
63
  }
64
64
 
65
- const allowedMetaProperties = ["name", "property", "http-equiv"];
66
- function tagDedupeKey(tag) {
67
- const { props, tag: tagName } = tag;
68
- if (UniqueTags.has(tagName))
69
- return tagName;
70
- if (tagName === "link" && props.rel === "canonical")
71
- return "canonical";
72
- if (props.charset)
73
- return "charset";
74
- if (props.id) {
75
- return `${tagName}:id:${props.id}`;
76
- }
77
- for (const n of allowedMetaProperties) {
78
- if (props[n] !== void 0) {
79
- return `${tagName}:${n}:${props[n]}`;
80
- }
81
- }
82
- return false;
83
- }
84
-
85
- function resolveTitleTemplate(template, title) {
86
- if (template == null)
87
- return title || null;
88
- if (typeof template === "function")
89
- return template(title);
90
- return template;
91
- }
92
-
93
65
  function asArray(input) {
94
66
  return Array.isArray(input) ? input : [input];
95
67
  }
@@ -407,111 +379,6 @@ function packMeta(inputs) {
407
379
  });
408
380
  }
409
381
 
410
- const WhitelistAttributes = {
411
- htmlAttrs: ["id", "class", "lang", "dir"],
412
- bodyAttrs: ["id", "class"],
413
- meta: ["id", "name", "property", "charset", "content"],
414
- noscript: ["id", "textContent"],
415
- script: ["id", "type", "textContent"],
416
- link: ["id", "color", "crossorigin", "fetchpriority", "href", "hreflang", "imagesrcset", "imagesizes", "integrity", "media", "referrerpolicy", "rel", "sizes", "type"]
417
- };
418
- function acceptDataAttrs(value) {
419
- const filtered = {};
420
- Object.keys(value || {}).filter((a) => a.startsWith("data-")).forEach((a) => {
421
- filtered[a] = value[a];
422
- });
423
- return filtered;
424
- }
425
- function whitelistSafeInput(input) {
426
- const filtered = {};
427
- Object.keys(input).forEach((key) => {
428
- const tagValue = input[key];
429
- if (!tagValue)
430
- return;
431
- switch (key) {
432
- case "title":
433
- case "titleTemplate":
434
- case "templateParams":
435
- filtered[key] = tagValue;
436
- break;
437
- case "htmlAttrs":
438
- case "bodyAttrs":
439
- filtered[key] = acceptDataAttrs(tagValue);
440
- WhitelistAttributes[key].forEach((a) => {
441
- if (tagValue[a])
442
- filtered[key][a] = tagValue[a];
443
- });
444
- break;
445
- case "meta":
446
- if (Array.isArray(tagValue)) {
447
- filtered[key] = tagValue.map((meta) => {
448
- const safeMeta = acceptDataAttrs(meta);
449
- WhitelistAttributes.meta.forEach((key2) => {
450
- if (meta[key2])
451
- safeMeta[key2] = meta[key2];
452
- });
453
- return safeMeta;
454
- }).filter((meta) => Object.keys(meta).length > 0);
455
- }
456
- break;
457
- case "link":
458
- if (Array.isArray(tagValue)) {
459
- filtered[key] = tagValue.map((meta) => {
460
- const link = acceptDataAttrs(meta);
461
- WhitelistAttributes.link.forEach((key2) => {
462
- const val = meta[key2];
463
- if (key2 === "rel" && (val === "stylesheet" || val === "canonical" || val === "modulepreload" || val === "prerender" || val === "preload" || val === "prefetch"))
464
- return;
465
- if (key2 === "href") {
466
- if (val.includes("javascript:") || val.includes("data:"))
467
- return;
468
- link[key2] = val;
469
- } else if (val) {
470
- link[key2] = val;
471
- }
472
- });
473
- return link;
474
- }).filter((link) => Object.keys(link).length > 1 && !!link.rel);
475
- }
476
- break;
477
- case "noscript":
478
- if (Array.isArray(tagValue)) {
479
- filtered[key] = tagValue.map((meta) => {
480
- const noscript = acceptDataAttrs(meta);
481
- WhitelistAttributes.noscript.forEach((key2) => {
482
- if (meta[key2])
483
- noscript[key2] = meta[key2];
484
- });
485
- return noscript;
486
- }).filter((meta) => Object.keys(meta).length > 0);
487
- }
488
- break;
489
- case "script":
490
- if (Array.isArray(tagValue)) {
491
- filtered[key] = tagValue.map((script) => {
492
- const safeScript = acceptDataAttrs(script);
493
- WhitelistAttributes.script.forEach((s) => {
494
- if (script[s]) {
495
- if (s === "textContent") {
496
- try {
497
- const jsonVal = typeof script[s] === "string" ? JSON.parse(script[s]) : script[s];
498
- safeScript[s] = JSON.stringify(jsonVal, null, 0);
499
- } catch (e) {
500
- }
501
- } else {
502
- safeScript[s] = script[s];
503
- }
504
- }
505
- });
506
- return safeScript;
507
- }).filter((meta) => Object.keys(meta).length > 0);
508
- }
509
- break;
510
- }
511
- });
512
- return filtered;
513
- }
514
-
515
382
  function thenable(val, thenFn) {
516
383
  if (val instanceof Promise) {
517
384
  return val.then(thenFn);
@@ -556,7 +423,7 @@ function normaliseTag(tagName, input, e, normalizedProps) {
556
423
  }
557
424
  function normaliseStyleClassProps(key, v) {
558
425
  const sep = key === "class" ? " " : ";";
559
- if (typeof v === "object" && !Array.isArray(v)) {
426
+ if (v && typeof v === "object" && !Array.isArray(v)) {
560
427
  v = Object.entries(v).filter(([, v2]) => v2).map(([k, v2]) => key === "style" ? `${k}:${v2}` : k);
561
428
  }
562
429
  return String(Array.isArray(v) ? v.join(sep) : v)?.split(sep).filter((c) => Boolean(c.trim())).join(sep);
@@ -578,7 +445,7 @@ function nestedNormaliseProps(props, virtual, keys, startIndex) {
578
445
  const v = String(props[k]);
579
446
  const isDataKey = k.startsWith("data-");
580
447
  if (v === "true" || v === "") {
581
- props[k] = isDataKey ? "true" : true;
448
+ props[k] = isDataKey ? props[k] : true;
582
449
  } else if (!props[k]) {
583
450
  if (isDataKey && v === "false")
584
451
  props[k] = "false";
@@ -643,6 +510,114 @@ function normaliseEntryTags(e) {
643
510
  }));
644
511
  }
645
512
 
513
+ const WhitelistAttributes = {
514
+ htmlAttrs: ["id", "class", "lang", "dir"],
515
+ bodyAttrs: ["id", "class"],
516
+ meta: ["id", "name", "property", "charset", "content"],
517
+ noscript: ["id", "textContent"],
518
+ script: ["id", "type", "textContent"],
519
+ link: ["id", "color", "crossorigin", "fetchpriority", "href", "hreflang", "imagesrcset", "imagesizes", "integrity", "media", "referrerpolicy", "rel", "sizes", "type"]
520
+ };
521
+ function acceptDataAttrs(value) {
522
+ const filtered = {};
523
+ Object.keys(value || {}).filter((a) => a.startsWith("data-")).forEach((a) => {
524
+ filtered[a] = value[a];
525
+ });
526
+ return filtered;
527
+ }
528
+ function whitelistSafeInput(input) {
529
+ const filtered = {};
530
+ Object.keys(input).forEach((key) => {
531
+ const tagValue = input[key];
532
+ if (!tagValue)
533
+ return;
534
+ switch (key) {
535
+ case "title":
536
+ case "titleTemplate":
537
+ case "templateParams":
538
+ filtered[key] = tagValue;
539
+ break;
540
+ case "htmlAttrs":
541
+ case "bodyAttrs":
542
+ filtered[key] = acceptDataAttrs(tagValue);
543
+ WhitelistAttributes[key].forEach((a) => {
544
+ if (tagValue[a])
545
+ filtered[key][a] = tagValue[a];
546
+ });
547
+ break;
548
+ case "meta":
549
+ if (Array.isArray(tagValue)) {
550
+ filtered[key] = tagValue.map((meta) => {
551
+ const safeMeta = acceptDataAttrs(meta);
552
+ WhitelistAttributes.meta.forEach((key2) => {
553
+ if (meta[key2])
554
+ safeMeta[key2] = meta[key2];
555
+ });
556
+ return safeMeta;
557
+ }).filter((meta) => Object.keys(meta).length > 0);
558
+ }
559
+ break;
560
+ case "link":
561
+ if (Array.isArray(tagValue)) {
562
+ filtered[key] = tagValue.map((meta) => {
563
+ const link = acceptDataAttrs(meta);
564
+ WhitelistAttributes.link.forEach((key2) => {
565
+ const val = meta[key2];
566
+ if (key2 === "rel" && (val === "stylesheet" || val === "canonical" || val === "modulepreload" || val === "prerender" || val === "preload" || val === "prefetch"))
567
+ return;
568
+ if (key2 === "href") {
569
+ if (val.includes("javascript:") || val.includes("data:"))
570
+ return;
571
+ link[key2] = val;
572
+ } else if (val) {
573
+ link[key2] = val;
574
+ }
575
+ });
576
+ return link;
577
+ }).filter((link) => Object.keys(link).length > 1 && !!link.rel);
578
+ }
579
+ break;
580
+ case "noscript":
581
+ if (Array.isArray(tagValue)) {
582
+ filtered[key] = tagValue.map((meta) => {
583
+ const noscript = acceptDataAttrs(meta);
584
+ WhitelistAttributes.noscript.forEach((key2) => {
585
+ if (meta[key2])
586
+ noscript[key2] = meta[key2];
587
+ });
588
+ return noscript;
589
+ }).filter((meta) => Object.keys(meta).length > 0);
590
+ }
591
+ break;
592
+ case "script":
593
+ if (Array.isArray(tagValue)) {
594
+ filtered[key] = tagValue.map((script) => {
595
+ const safeScript = acceptDataAttrs(script);
596
+ WhitelistAttributes.script.forEach((s) => {
597
+ if (script[s]) {
598
+ if (s === "textContent") {
599
+ try {
600
+ const jsonVal = typeof script[s] === "string" ? JSON.parse(script[s]) : script[s];
601
+ safeScript[s] = JSON.stringify(jsonVal, null, 0);
602
+ } catch (e) {
603
+ }
604
+ } else {
605
+ safeScript[s] = script[s];
606
+ }
607
+ }
608
+ });
609
+ return safeScript;
610
+ }).filter((meta) => Object.keys(meta).length > 0);
611
+ }
612
+ break;
613
+ }
614
+ });
615
+ return filtered;
616
+ }
617
+
618
+ const NetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror", "onabort", "onprogress", "onloadstart"]);
619
+ const ScriptNetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror"]);
620
+
646
621
  const TAG_WEIGHTS = {
647
622
  // tags
648
623
  base: -10,
@@ -678,8 +653,25 @@ function tagWeight(tag) {
678
653
  }
679
654
  const SortModifiers = [{ prefix: "before:", offset: -1 }, { prefix: "after:", offset: 1 }];
680
655
 
681
- const NetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror", "onabort", "onprogress", "onloadstart"]);
682
- const ScriptNetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror"]);
656
+ const allowedMetaProperties = ["name", "property", "http-equiv"];
657
+ function tagDedupeKey(tag) {
658
+ const { props, tag: tagName } = tag;
659
+ if (UniqueTags.has(tagName))
660
+ return tagName;
661
+ if (tagName === "link" && props.rel === "canonical")
662
+ return "canonical";
663
+ if (props.charset)
664
+ return "charset";
665
+ if (props.id) {
666
+ return `${tagName}:id:${props.id}`;
667
+ }
668
+ for (const n of allowedMetaProperties) {
669
+ if (props[n] !== void 0) {
670
+ return `${tagName}:${n}:${props[n]}`;
671
+ }
672
+ }
673
+ return false;
674
+ }
683
675
 
684
676
  const sepSub = "%separator";
685
677
  function sub(p, token) {
@@ -725,4 +717,12 @@ function processTemplateParams(s, p, sep) {
725
717
  return s;
726
718
  }
727
719
 
720
+ function resolveTitleTemplate(template, title) {
721
+ if (template == null)
722
+ return title || null;
723
+ if (typeof template === "function")
724
+ return template(title);
725
+ return template;
726
+ }
727
+
728
728
  export { HasElementTags, IsBrowser, NetworkEvents, ScriptNetworkEvents, SelfClosingTags, SortModifiers, TAG_ALIASES, TAG_WEIGHTS, TagConfigKeys, TagEntityBits, TagsWithInnerContent, UniqueTags, ValidHeadTags, asArray$1 as asArray, composableNames, defineHeadPlugin, hashCode, hashTag, normaliseEntryTags, normaliseProps, normaliseStyleClassProps, normaliseTag, packMeta, processTemplateParams, resolveMetaKeyType, resolveMetaKeyValue, resolvePackedMetaObjectValue, resolveTitleTemplate, tagDedupeKey, tagWeight, thenable, unpackMeta, whitelistSafeInput };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unhead/shared",
3
3
  "type": "module",
4
- "version": "1.10.4",
4
+ "version": "1.11.0-beta.1",
5
5
  "author": "Harlan Wilton <harlan@harlanzw.com>",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/harlan-zw",
@@ -34,7 +34,7 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "@unhead/schema": "1.10.4"
37
+ "@unhead/schema": "1.11.0-beta.1"
38
38
  },
39
39
  "devDependencies": {
40
40
  "packrup": "^0.1.2"