@jaredwray/fumanchu 4.6.1 → 4.7.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.
@@ -1,24 +1,97 @@
1
1
  import { CacheableMemory } from "@cacheable/memory";
2
- import HandlebarsLib from "handlebars";
2
+ import Handlebars$1 from "handlebars";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
- import tag from "html-tag";
6
- import codeBlock from "to-gfm-code-block";
7
5
  import { parseDate } from "chrono-node";
8
6
  import dayjs from "dayjs";
9
7
  import isBetween from "dayjs/plugin/isBetween.js";
10
8
  import relativeTime from "dayjs/plugin/relativeTime.js";
11
9
  import timezone from "dayjs/plugin/timezone.js";
12
10
  import utc from "dayjs/plugin/utc.js";
13
- import isGlob from "is-glob";
14
11
  import micromatch from "micromatch";
15
- import striptags from "striptags";
16
12
  import process$1 from "node:process";
17
- import ent from "ent";
18
- import { Remarkable } from "remarkable";
19
- import kindOf from "kind-of";
13
+ import MarkdownIt from "markdown-it";
20
14
  import { escape } from "node:querystring";
21
15
  import { format, parse, resolve } from "node:url";
16
+ //#region src/helper-registry-base.ts
17
+ /**
18
+ * Base class for helper registries. Holds all state-management logic and
19
+ * leaves `init()` empty so environment-specific subclasses decide which
20
+ * helper sets to register. This lets the browser registry avoid importing
21
+ * any Node-only helper files.
22
+ */
23
+ var HelperRegistryBase = class {
24
+ _helpers = [];
25
+ constructor() {
26
+ this.init();
27
+ }
28
+ /**
29
+ * Get all registered helpers.
30
+ */
31
+ get helpers() {
32
+ return this._helpers;
33
+ }
34
+ /**
35
+ * Initialize the helper registry. Overridden by subclasses.
36
+ */
37
+ init() {}
38
+ /**
39
+ * Register a helper. Returns true if the helper was added, false if a
40
+ * helper with the same name was already present.
41
+ */
42
+ register(helper) {
43
+ if (this.has(helper.name)) return false;
44
+ this._helpers.push(helper);
45
+ return true;
46
+ }
47
+ /**
48
+ * Register multiple helpers.
49
+ */
50
+ registerHelpers(helpers) {
51
+ for (const helper of helpers) this.register(helper);
52
+ }
53
+ /**
54
+ * Check if a helper is registered.
55
+ */
56
+ has(name) {
57
+ return this._helpers.some((helper) => helper.name === name);
58
+ }
59
+ /**
60
+ * Filter helpers by the given criteria.
61
+ */
62
+ filter(filter) {
63
+ let result = this._helpers;
64
+ if (filter.names) {
65
+ const names = filter.names;
66
+ result = result.filter((helper) => names.includes(helper.name));
67
+ }
68
+ if (filter.categories) {
69
+ const categories = filter.categories;
70
+ result = result.filter((helper) => categories.includes(helper.category));
71
+ }
72
+ if (filter.compatibility) {
73
+ const compatibility = filter.compatibility;
74
+ result = result.filter((helper) => helper.compatibility?.some((c) => compatibility.includes(c)));
75
+ }
76
+ return result;
77
+ }
78
+ /**
79
+ * Load Handlebars helpers.
80
+ */
81
+ load(handlebars, filters) {
82
+ let helpers = this._helpers;
83
+ if (filters) helpers = this.filter(filters);
84
+ for (const helper of helpers) handlebars.registerHelper(helper.name, helper.fn);
85
+ }
86
+ /**
87
+ * Swap Handlebars helpers. This is used when you want to swap out legacy
88
+ * helpers for fumanchu helpers.
89
+ */
90
+ swap(handlebars) {
91
+ for (const helper of this._helpers) handlebars.registerHelper(helper.name, helper.fn);
92
+ }
93
+ };
94
+ //#endregion
22
95
  //#region src/helpers/array.ts
23
96
  const after = (array, n) => {
24
97
  if (!Array.isArray(array)) return [];
@@ -323,7 +396,7 @@ const unique = function(array, options) {
323
396
  }
324
397
  return "";
325
398
  };
326
- const helpers$20 = [
399
+ const helpers$23 = [
327
400
  {
328
401
  name: "after",
329
402
  category: "array",
@@ -488,16 +561,163 @@ const helpers$20 = [
488
561
  }
489
562
  ];
490
563
  //#endregion
491
- //#region src/helpers/code.ts
492
- const embed = (filepath, language) => {
493
- let ext = typeof language === "string" ? language : path.extname(filepath).slice(1);
494
- let code = fs.readFileSync(filepath, "utf8");
495
- if (ext === "markdown" || ext === "md") {
496
- ext = "markdown";
497
- code = code.split("`").join("&#x60");
564
+ //#region src/helpers/html.ts
565
+ const isTagStart = (ch) => ch !== void 0 && (ch === "/" || ch === "!" || ch === "?" || /[a-zA-Z]/.test(ch));
566
+ const stripTags = (str) => {
567
+ let result = "";
568
+ let inTag = false;
569
+ let inComment = false;
570
+ let quote = null;
571
+ for (let i = 0; i < str.length; i++) {
572
+ const ch = str[i];
573
+ if (inComment) {
574
+ if (ch === "-" && str[i + 1] === "-" && str[i + 2] === ">") {
575
+ inComment = false;
576
+ i += 2;
577
+ }
578
+ continue;
579
+ }
580
+ if (inTag) {
581
+ if (quote) {
582
+ if (ch === quote) quote = null;
583
+ } else if (ch === "\"" || ch === "'") quote = ch;
584
+ else if (ch === ">") inTag = false;
585
+ continue;
586
+ }
587
+ if (ch === "<") {
588
+ if (str[i + 1] === "!" && str[i + 2] === "-" && str[i + 3] === "-") {
589
+ inComment = true;
590
+ i += 3;
591
+ continue;
592
+ }
593
+ if (isTagStart(str[i + 1])) {
594
+ inTag = true;
595
+ continue;
596
+ }
597
+ }
598
+ result += ch;
498
599
  }
499
- return `${codeBlock(code, ext).trim()}\n`;
600
+ return result;
601
+ };
602
+ const VOID_ELEMENTS = /* @__PURE__ */ new Set([
603
+ "area",
604
+ "base",
605
+ "br",
606
+ "col",
607
+ "command",
608
+ "embed",
609
+ "hr",
610
+ "img",
611
+ "input",
612
+ "keygen",
613
+ "link",
614
+ "meta",
615
+ "param",
616
+ "source",
617
+ "track",
618
+ "wbr",
619
+ "circle",
620
+ "ellipse",
621
+ "line",
622
+ "path",
623
+ "polygon",
624
+ "polyline",
625
+ "rect",
626
+ "stop",
627
+ "use"
628
+ ]);
629
+ const tag = (name, attribs = {}, text = "") => {
630
+ let html = `<${name}`;
631
+ for (const [key, val] of Object.entries(attribs)) if (val === true) html += ` ${key}`;
632
+ else if (typeof val === "string" || typeof val === "number") html += ` ${key}="${escapeAttr(String(val))}"`;
633
+ if (VOID_ELEMENTS.has(name.toLowerCase())) return `${html}>`;
634
+ return `${html}>${escapeText(text)}</${name}>`;
635
+ };
636
+ const escapeAttr = (str) => str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
637
+ const escapeText = (str) => str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
638
+ const parseAttributes = (hash = {}) => Object.keys(hash).map((key) => `${key}="${String(hash[key]).replace(/^['"]|["']$/g, "")}"`).join(" ");
639
+ const attr = (options) => {
640
+ const val = parseAttributes(options?.hash ?? {});
641
+ return val.trim() ? ` ${val}` : "";
642
+ };
643
+ const sanitize = (str) => {
644
+ if (typeof str !== "string") return "";
645
+ return stripTags(str).trim();
646
+ };
647
+ const ul = (context, options) => {
648
+ const attrs = parseAttributes(options.hash ?? {});
649
+ const items = context.map((item) => typeof item === "string" ? item : options.fn(item));
650
+ return `${attrs ? `<ul ${attrs}>` : "<ul>"}${items.map((i) => `<li>${i}</li>`).join("\n")}</ul>`;
500
651
  };
652
+ const ol = (context, options) => {
653
+ const attrs = parseAttributes(options.hash ?? {});
654
+ const items = context.map((item) => typeof item === "string" ? item : options.fn(item));
655
+ return `${attrs ? `<ol ${attrs}>` : "<ol>"}${items.map((i) => `<li>${i}</li>`).join("\n")}</ol>`;
656
+ };
657
+ const thumbnailImage = (context) => {
658
+ let figure = "";
659
+ let image = "";
660
+ const link = context.full || false;
661
+ const imageAttributes = {
662
+ alt: context.alt,
663
+ src: context.thumbnail,
664
+ width: context.size.width,
665
+ height: context.size.height
666
+ };
667
+ const figureAttributes = { id: `image-${context.id}` };
668
+ const linkAttributes = {
669
+ href: link,
670
+ rel: "thumbnail"
671
+ };
672
+ /* c8 ignore next -- @preserve */
673
+ if (context.classes) {
674
+ if (context.classes.image) imageAttributes.class = context.classes.image.join(" ");
675
+ if (context.classes.figure) figureAttributes.class = context.classes.figure.join(" ");
676
+ if (context.classes.link) linkAttributes.class = context.classes.link.join(" ");
677
+ }
678
+ figure += `<figure ${parseAttributes(figureAttributes)}>\n`;
679
+ image += `<img ${parseAttributes(imageAttributes)}>\n`;
680
+ if (link) figure += `<a ${parseAttributes(linkAttributes)}>\n${image}</a>\n`;
681
+ else figure += image;
682
+ /* c8 ignore next -- @preserve */
683
+ if (context.caption) figure += `<figcaption>${context.caption}</figcaption>\n`;
684
+ figure += "</figure>";
685
+ return figure;
686
+ };
687
+ const helpers$22 = [
688
+ {
689
+ name: "attr",
690
+ category: "html",
691
+ compatibility: ["browser", "nodejs"],
692
+ fn: attr
693
+ },
694
+ {
695
+ name: "sanitize",
696
+ category: "html",
697
+ compatibility: ["browser", "nodejs"],
698
+ fn: sanitize
699
+ },
700
+ {
701
+ name: "ul",
702
+ category: "html",
703
+ compatibility: ["browser", "nodejs"],
704
+ fn: ul
705
+ },
706
+ {
707
+ name: "ol",
708
+ category: "html",
709
+ compatibility: ["browser", "nodejs"],
710
+ fn: ol
711
+ },
712
+ {
713
+ name: "thumbnailImage",
714
+ category: "html",
715
+ compatibility: ["browser", "nodejs"],
716
+ fn: thumbnailImage
717
+ }
718
+ ];
719
+ //#endregion
720
+ //#region src/helpers/code.ts
501
721
  const gist = (id) => {
502
722
  return tag("script", { src: `https://gist.github.com/${id}.js` });
503
723
  };
@@ -512,26 +732,37 @@ const jsfiddle = (options) => {
512
732
  frameborder
513
733
  });
514
734
  };
515
- const helpers$19 = [
516
- {
517
- name: "embed",
518
- category: "code",
519
- compatibility: ["nodejs"],
520
- fn: embed
521
- },
522
- {
523
- name: "gist",
524
- category: "code",
525
- compatibility: ["browser", "nodejs"],
526
- fn: gist
527
- },
528
- {
529
- name: "jsfiddle",
530
- category: "code",
531
- compatibility: ["browser", "nodejs"],
532
- fn: jsfiddle
735
+ const helpers$21 = [{
736
+ name: "gist",
737
+ category: "code",
738
+ compatibility: ["browser", "nodejs"],
739
+ fn: gist
740
+ }, {
741
+ name: "jsfiddle",
742
+ category: "code",
743
+ compatibility: ["browser", "nodejs"],
744
+ fn: jsfiddle
745
+ }];
746
+ //#endregion
747
+ //#region src/helpers/code.node.ts
748
+ const codeBlock = (code, lang) => {
749
+ return `\`\`\`${lang}\n${code}\n\`\`\`\n`;
750
+ };
751
+ const embed = (filepath, language) => {
752
+ let ext = typeof language === "string" ? language : path.extname(filepath).slice(1);
753
+ let code = fs.readFileSync(filepath, "utf8");
754
+ if (ext === "markdown" || ext === "md") {
755
+ ext = "markdown";
756
+ code = code.split("`").join("&#x60");
533
757
  }
534
- ];
758
+ return `${codeBlock(code, ext).trim()}\n`;
759
+ };
760
+ const helpers$20 = [{
761
+ name: "embed",
762
+ category: "code",
763
+ compatibility: ["nodejs"],
764
+ fn: embed
765
+ }];
535
766
  //#endregion
536
767
  //#region src/helpers/collection.ts
537
768
  const isEmpty = (collection) => {
@@ -553,7 +784,7 @@ const iterate = (collection, fn, inverse) => {
553
784
  }
554
785
  return inverse ? inverse() : "";
555
786
  };
556
- const helpers$18 = [{
787
+ const helpers$19 = [{
557
788
  name: "isEmpty",
558
789
  category: "collection",
559
790
  compatibility: ["browser", "nodejs"],
@@ -645,7 +876,7 @@ const unlessGt = (a, b) => a <= b;
645
876
  const unlessLt = (a, b) => a >= b;
646
877
  const unlessGteq = (a, b) => a < b;
647
878
  const unlessLteq = (a, b) => a > b;
648
- const helpers$17 = [
879
+ const helpers$18 = [
649
880
  {
650
881
  name: "and",
651
882
  category: "comparison",
@@ -926,7 +1157,7 @@ const dateLocale = (dateInput, locale, formatString) => {
926
1157
  if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
927
1158
  return parsed.locale(locale).format(convertedFormat);
928
1159
  };
929
- const helpers$16 = [
1160
+ const helpers$17 = [
930
1161
  {
931
1162
  name: "year",
932
1163
  category: "date",
@@ -1050,6 +1281,9 @@ const helpers$16 = [
1050
1281
  ];
1051
1282
  //#endregion
1052
1283
  //#region src/helpers/fs.ts
1284
+ const ESCAPE_SEQUENCE = /\\[\s\S]/g;
1285
+ const GLOB_PATTERN = /^!|[*?]|\[[^\]]+\]|\{[^}]+\}|[@?!+*]\([^)]*\)|\([^)]*\|[^)]*\)/;
1286
+ const isGlob = (value) => GLOB_PATTERN.test(value.replace(ESCAPE_SEQUENCE, ""));
1053
1287
  const fileSize = (value, precision) => {
1054
1288
  if (value == null) return "0 B";
1055
1289
  const number = typeof value === "number" ? value : value.length ?? 0;
@@ -1095,7 +1329,7 @@ const readdir = (dir, filter) => {
1095
1329
  }
1096
1330
  return files;
1097
1331
  };
1098
- const helpers$15 = [
1332
+ const helpers$16 = [
1099
1333
  {
1100
1334
  name: "fileSize",
1101
1335
  category: "fs",
@@ -1116,12 +1350,7 @@ const helpers$15 = [
1116
1350
  }
1117
1351
  ];
1118
1352
  //#endregion
1119
- //#region src/helpers/html.ts
1120
- const parseAttributes = (hash = {}) => Object.keys(hash).map((key) => `${key}="${String(hash[key]).replace(/^['"]|["']$/g, "")}"`).join(" ");
1121
- const attr = (options) => {
1122
- const val = parseAttributes(options?.hash ?? {});
1123
- return val.trim() ? ` ${val}` : "";
1124
- };
1353
+ //#region src/helpers/html.node.ts
1125
1354
  const css = function(list, options) {
1126
1355
  if (arguments.length < 2) {
1127
1356
  options = list;
@@ -1151,94 +1380,17 @@ const js = (context) => {
1151
1380
  src: fp
1152
1381
  }) : tag("script", { src: fp })).join("\n");
1153
1382
  };
1154
- const sanitize = (str) => {
1155
- if (typeof str !== "string") return "";
1156
- return striptags(str).trim();
1157
- };
1158
- const ul = (context, options) => {
1159
- const attrs = parseAttributes(options.hash ?? {});
1160
- const items = context.map((item) => typeof item === "string" ? item : options.fn(item));
1161
- return `${attrs ? `<ul ${attrs}>` : "<ul>"}${items.map((i) => `<li>${i}</li>`).join("\n")}</ul>`;
1162
- };
1163
- const ol = (context, options) => {
1164
- const attrs = parseAttributes(options.hash ?? {});
1165
- const items = context.map((item) => typeof item === "string" ? item : options.fn(item));
1166
- return `${attrs ? `<ol ${attrs}>` : "<ol>"}${items.map((i) => `<li>${i}</li>`).join("\n")}</ol>`;
1167
- };
1168
- const thumbnailImage = (context) => {
1169
- let figure = "";
1170
- let image = "";
1171
- const link = context.full || false;
1172
- const imageAttributes = {
1173
- alt: context.alt,
1174
- src: context.thumbnail,
1175
- width: context.size.width,
1176
- height: context.size.height
1177
- };
1178
- const figureAttributes = { id: `image-${context.id}` };
1179
- const linkAttributes = {
1180
- href: link,
1181
- rel: "thumbnail"
1182
- };
1183
- /* c8 ignore next -- @preserve */
1184
- if (context.classes) {
1185
- if (context.classes.image) imageAttributes.class = context.classes.image.join(" ");
1186
- if (context.classes.figure) figureAttributes.class = context.classes.figure.join(" ");
1187
- if (context.classes.link) linkAttributes.class = context.classes.link.join(" ");
1188
- }
1189
- figure += `<figure ${parseAttributes(figureAttributes)}>\n`;
1190
- image += `<img ${parseAttributes(imageAttributes)}>\n`;
1191
- if (link) figure += `<a ${parseAttributes(linkAttributes)}>\n${image}</a>\n`;
1192
- else figure += image;
1193
- /* c8 ignore next -- @preserve */
1194
- if (context.caption) figure += `<figcaption>${context.caption}</figcaption>\n`;
1195
- figure += "</figure>";
1196
- return figure;
1197
- };
1198
- const helpers$14 = [
1199
- {
1200
- name: "attr",
1201
- category: "html",
1202
- compatibility: ["browser", "nodejs"],
1203
- fn: attr
1204
- },
1205
- {
1206
- name: "css",
1207
- category: "html",
1208
- compatibility: ["nodejs"],
1209
- fn: css
1210
- },
1211
- {
1212
- name: "js",
1213
- category: "html",
1214
- compatibility: ["nodejs"],
1215
- fn: js
1216
- },
1217
- {
1218
- name: "sanitize",
1219
- category: "html",
1220
- compatibility: ["browser", "nodejs"],
1221
- fn: sanitize
1222
- },
1223
- {
1224
- name: "ul",
1225
- category: "html",
1226
- compatibility: ["browser", "nodejs"],
1227
- fn: ul
1228
- },
1229
- {
1230
- name: "ol",
1231
- category: "html",
1232
- compatibility: ["browser", "nodejs"],
1233
- fn: ol
1234
- },
1235
- {
1236
- name: "thumbnailImage",
1237
- category: "html",
1238
- compatibility: ["browser", "nodejs"],
1239
- fn: thumbnailImage
1240
- }
1241
- ];
1383
+ const helpers$15 = [{
1384
+ name: "css",
1385
+ category: "html",
1386
+ compatibility: ["nodejs"],
1387
+ fn: css
1388
+ }, {
1389
+ name: "js",
1390
+ category: "html",
1391
+ compatibility: ["nodejs"],
1392
+ fn: js
1393
+ }];
1242
1394
  //#endregion
1243
1395
  //#region src/utils.ts
1244
1396
  /**
@@ -1316,7 +1468,7 @@ const i18n = function(prop, locals, options) {
1316
1468
  if (typeof result === "undefined") throw new Error("{{i18n}} helper cannot find property \"" + String(prop) + "\" for language \"" + lang + "\"");
1317
1469
  return String(result);
1318
1470
  };
1319
- const helpers$13 = [{
1471
+ const helpers$14 = [{
1320
1472
  name: "i18n",
1321
1473
  category: "i18n",
1322
1474
  compatibility: ["browser", "nodejs"],
@@ -1344,7 +1496,7 @@ const ordinalize = (value) => {
1344
1496
  default: return `${str}th`;
1345
1497
  }
1346
1498
  };
1347
- const helpers$12 = [{
1499
+ const helpers$13 = [{
1348
1500
  name: "inflect",
1349
1501
  category: "inflection",
1350
1502
  compatibility: ["browser", "nodejs"],
@@ -1472,7 +1624,7 @@ function switchOutput(type, json) {
1472
1624
  default: return json;
1473
1625
  }
1474
1626
  }
1475
- const helpers$11 = [
1627
+ const helpers$12 = [
1476
1628
  {
1477
1629
  name: "log",
1478
1630
  category: "logging",
@@ -1555,7 +1707,7 @@ const mm = (...args) => {
1555
1707
  console.log("helper instead.");
1556
1708
  return match(...args);
1557
1709
  };
1558
- const helpers$10 = [
1710
+ const helpers$11 = [
1559
1711
  {
1560
1712
  name: "match",
1561
1713
  category: "match",
@@ -1637,7 +1789,7 @@ const sum = (...values) => {
1637
1789
  return total;
1638
1790
  };
1639
1791
  const times = (a, b) => multiply(a, b);
1640
- const helpers$9 = [
1792
+ const helpers$10 = [
1641
1793
  {
1642
1794
  name: "abs",
1643
1795
  category: "math",
@@ -1737,6 +1889,18 @@ const helpers$9 = [
1737
1889
  ];
1738
1890
  //#endregion
1739
1891
  //#region src/helpers/md.ts
1892
+ const md = new MarkdownIt({
1893
+ breaks: true,
1894
+ html: true,
1895
+ langPrefix: "lang-",
1896
+ typographer: false,
1897
+ xhtmlOut: false
1898
+ });
1899
+ function renderString(input) {
1900
+ return new Handlebars$1.SafeString(md.render(input));
1901
+ }
1902
+ //#endregion
1903
+ //#region src/helpers/md.node.ts
1740
1904
  const renderMarkdown = function(input, options) {
1741
1905
  if (typeof input === "object" && input && "fn" in input && typeof input.fn === "function") {
1742
1906
  options = input;
@@ -1746,21 +1910,14 @@ const renderMarkdown = function(input, options) {
1746
1910
  cwd: process$1.cwd(),
1747
1911
  ...options
1748
1912
  };
1749
- const md = new Remarkable({
1750
- breaks: true,
1751
- html: true,
1752
- langPrefix: "lang-",
1753
- typographer: false,
1754
- xhtmlOut: false
1755
- });
1756
1913
  let string_ = input;
1757
- if (string_ && string_.length > 0) {
1914
+ if (string_.length > 0) {
1758
1915
  const filepath = path.resolve(options_.cwd, string_);
1759
1916
  if (fs.existsSync(filepath) && fs.statSync(filepath).isFile()) string_ = fs.readFileSync(filepath, "utf8");
1760
1917
  }
1761
- return new HandlebarsLib.SafeString(ent.decode(md.render(string_)));
1918
+ return renderString(string_);
1762
1919
  };
1763
- const helpers$8 = [{
1920
+ const helpers$9 = [{
1764
1921
  name: "md",
1765
1922
  category: "markdown",
1766
1923
  compatibility: ["nodejs"],
@@ -1773,6 +1930,68 @@ const helpers$8 = [{
1773
1930
  }];
1774
1931
  //#endregion
1775
1932
  //#region src/helpers/misc.ts
1933
+ const objectToString = Object.prototype.toString;
1934
+ function ctorName(val) {
1935
+ return typeof val.constructor === "function" ? val.constructor.name : null;
1936
+ }
1937
+ function isGeneratorFn(val) {
1938
+ return ctorName(val) === "GeneratorFunction";
1939
+ }
1940
+ function isGeneratorObj(val) {
1941
+ return typeof val.throw === "function" && typeof val.return === "function" && typeof val.next === "function";
1942
+ }
1943
+ function isDateLike(val) {
1944
+ return typeof val.toDateString === "function" && typeof val.getDate === "function" && typeof val.setDate === "function";
1945
+ }
1946
+ function isErrorLike(val) {
1947
+ return typeof val.message === "string" && val.constructor && typeof val.constructor.stackTraceLimit === "number";
1948
+ }
1949
+ function isRegexpLike(val) {
1950
+ return typeof val.flags === "string" && typeof val.ignoreCase === "boolean" && typeof val.multiline === "boolean" && typeof val.global === "boolean";
1951
+ }
1952
+ function kindOf(val) {
1953
+ if (val === void 0) return "undefined";
1954
+ if (val === null) return "null";
1955
+ const type = typeof val;
1956
+ if (type === "boolean") return "boolean";
1957
+ if (type === "string") return "string";
1958
+ if (type === "number") return "number";
1959
+ if (type === "symbol") return "symbol";
1960
+ if (type === "bigint") return "bigint";
1961
+ if (type === "function") return isGeneratorFn(val) ? "generatorfunction" : "function";
1962
+ if (Array.isArray(val)) return "array";
1963
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer?.(val)) return "buffer";
1964
+ if (typeof val.length === "number" && objectToString.call(val) === "[object Arguments]") return "arguments";
1965
+ if (val instanceof Date || isDateLike(val)) return "date";
1966
+ if (val instanceof Error || isErrorLike(val)) return "error";
1967
+ if (val instanceof RegExp || isRegexpLike(val)) return "regexp";
1968
+ switch (ctorName(val)) {
1969
+ case "Promise": return "promise";
1970
+ case "WeakMap": return "weakmap";
1971
+ case "WeakSet": return "weakset";
1972
+ case "Map": return "map";
1973
+ case "Set": return "set";
1974
+ case "Int8Array": return "int8array";
1975
+ case "Uint8Array": return "uint8array";
1976
+ case "Uint8ClampedArray": return "uint8clampedarray";
1977
+ case "Int16Array": return "int16array";
1978
+ case "Uint16Array": return "uint16array";
1979
+ case "Int32Array": return "int32array";
1980
+ case "Uint32Array": return "uint32array";
1981
+ case "Float32Array": return "float32array";
1982
+ case "Float64Array": return "float64array";
1983
+ }
1984
+ if (isGeneratorObj(val)) return "generator";
1985
+ const tag = objectToString.call(val);
1986
+ switch (tag) {
1987
+ case "[object Object]": return "object";
1988
+ case "[object Map Iterator]": return "mapiterator";
1989
+ case "[object Set Iterator]": return "setiterator";
1990
+ case "[object String Iterator]": return "stringiterator";
1991
+ case "[object Array Iterator]": return "arrayiterator";
1992
+ }
1993
+ return tag.slice(8, -1).toLowerCase().replace(/\s/g, "");
1994
+ }
1776
1995
  /**
1777
1996
  * Block helper for exposing private `@` variables on the context
1778
1997
  * Replaces the external handlebars-helper-create-frame package
@@ -1782,7 +2001,7 @@ function frame(context, options) {
1782
2001
  options = context;
1783
2002
  context = options.data;
1784
2003
  }
1785
- const data = HandlebarsLib.createFrame(context || {});
2004
+ const data = Handlebars$1.createFrame(context || {});
1786
2005
  if (typeof options !== "object" || options === null) options = {};
1787
2006
  if (options.hash && typeof options.hash === "object") Object.assign(data, options.hash);
1788
2007
  return options.fn(this, { data });
@@ -1798,7 +2017,7 @@ function withHash(options) {
1798
2017
  if (options.hash && Object.keys(options.hash).length) return options.fn(options.hash);
1799
2018
  return options.inverse(this);
1800
2019
  }
1801
- const helpers$7 = [
2020
+ const helpers$8 = [
1802
2021
  {
1803
2022
  name: "frame",
1804
2023
  category: "misc",
@@ -1901,7 +2120,7 @@ const toPrecision = (value, precision) => {
1901
2120
  const prec = typeof precision === "number" && !Number.isNaN(precision) ? precision : 1;
1902
2121
  return Number(num).toPrecision(prec);
1903
2122
  };
1904
- const helpers$6 = [
2123
+ const helpers$7 = [
1905
2124
  {
1906
2125
  name: "bytes",
1907
2126
  category: "number",
@@ -2041,7 +2260,7 @@ const pick = (props, context, options) => {
2041
2260
  }
2042
2261
  return result;
2043
2262
  };
2044
- const helpers$5 = [
2263
+ const helpers$6 = [
2045
2264
  {
2046
2265
  name: "extend",
2047
2266
  category: "object",
@@ -2179,7 +2398,7 @@ const segments = (filepath, a, b) => {
2179
2398
  const end = Number(b);
2180
2399
  return fp.split(/[\\/]+/).slice(start, end).join("/");
2181
2400
  };
2182
- const helpers$4 = [
2401
+ const helpers$5 = [
2183
2402
  {
2184
2403
  name: "absolute",
2185
2404
  category: "path",
@@ -2240,7 +2459,7 @@ const test = (str, regex) => {
2240
2459
  if (!(regex instanceof RegExp)) throw new TypeError("expected a regular expression");
2241
2460
  return regex.test(str);
2242
2461
  };
2243
- const helpers$3 = [{
2462
+ const helpers$4 = [{
2244
2463
  name: "toRegex",
2245
2464
  category: "regex",
2246
2465
  compatibility: ["browser", "nodejs"],
@@ -2397,7 +2616,7 @@ const raw = (options) => {
2397
2616
  if (options && typeof options.fn === "function") return options.fn();
2398
2617
  return "";
2399
2618
  };
2400
- const helpers$2 = [
2619
+ const helpers$3 = [
2401
2620
  {
2402
2621
  name: "append",
2403
2622
  category: "string",
@@ -2621,9 +2840,6 @@ const all = ["browser", "nodejs"];
2621
2840
  const encodeUri = (str) => {
2622
2841
  if (typeof str === "string") return encodeURIComponent(str);
2623
2842
  };
2624
- const escapeFn = (str) => {
2625
- if (typeof str === "string") return escape(str);
2626
- };
2627
2843
  const decodeUri = (str) => {
2628
2844
  if (typeof str === "string") return decodeURIComponent(str);
2629
2845
  };
@@ -2633,31 +2849,16 @@ function urlEncode(...args) {
2633
2849
  function urlDecode(...args) {
2634
2850
  return decodeUri.apply(this, args);
2635
2851
  }
2636
- const urlResolve = (base, href) => resolve(String(base), String(href));
2637
- const urlParse = (str) => parse(String(str));
2638
2852
  const stripQuerystring = (str) => {
2639
2853
  if (typeof str === "string") return str.split("?")[0];
2640
2854
  };
2641
- const stripProtocol = (str) => {
2642
- if (typeof str === "string") {
2643
- const parsed = parse(str);
2644
- parsed.protocol = "";
2645
- return format(parsed);
2646
- }
2647
- };
2648
- const helpers$1 = [
2855
+ const helpers$2 = [
2649
2856
  {
2650
2857
  name: "encodeURI",
2651
2858
  category: "url",
2652
2859
  compatibility: all,
2653
2860
  fn: encodeUri
2654
2861
  },
2655
- {
2656
- name: "escape",
2657
- category: "url",
2658
- compatibility: ["nodejs"],
2659
- fn: escapeFn
2660
- },
2661
2862
  {
2662
2863
  name: "decodeURI",
2663
2864
  category: "url",
@@ -2676,6 +2877,34 @@ const helpers$1 = [
2676
2877
  compatibility: all,
2677
2878
  fn: urlDecode
2678
2879
  },
2880
+ {
2881
+ name: "stripQuerystring",
2882
+ category: "url",
2883
+ compatibility: all,
2884
+ fn: stripQuerystring
2885
+ }
2886
+ ];
2887
+ //#endregion
2888
+ //#region src/helpers/url.node.ts
2889
+ const escapeFn = (str) => {
2890
+ if (typeof str === "string") return escape(str);
2891
+ };
2892
+ const urlResolve = (base, href) => resolve(String(base), String(href));
2893
+ const urlParse = (str) => parse(String(str));
2894
+ const stripProtocol = (str) => {
2895
+ if (typeof str === "string") {
2896
+ const parsed = parse(str);
2897
+ parsed.protocol = "";
2898
+ return format(parsed);
2899
+ }
2900
+ };
2901
+ const helpers$1 = [
2902
+ {
2903
+ name: "escape",
2904
+ category: "url",
2905
+ compatibility: ["nodejs"],
2906
+ fn: escapeFn
2907
+ },
2679
2908
  {
2680
2909
  name: "urlResolve",
2681
2910
  category: "url",
@@ -2688,12 +2917,6 @@ const helpers$1 = [
2688
2917
  compatibility: ["nodejs"],
2689
2918
  fn: urlParse
2690
2919
  },
2691
- {
2692
- name: "stripQuerystring",
2693
- category: "url",
2694
- compatibility: all,
2695
- fn: stripQuerystring
2696
- },
2697
2920
  {
2698
2921
  name: "stripProtocol",
2699
2922
  category: "url",
@@ -2703,114 +2926,45 @@ const helpers$1 = [
2703
2926
  ];
2704
2927
  //#endregion
2705
2928
  //#region src/helper-registry.ts
2706
- var HelperRegistry = class {
2707
- _helpers = [];
2708
- constructor() {
2709
- this.init();
2710
- }
2711
- /**
2712
- * Get all registered helpers.
2713
- * @returns {Helper[]} The array of registered helpers.
2714
- */
2715
- get helpers() {
2716
- return this._helpers;
2717
- }
2718
- /**
2719
- * Initialize the helper registry. This is performed during the construction of the registry such as new HelperRegistry().
2720
- */
2929
+ var HelperRegistry = class extends HelperRegistryBase {
2721
2930
  init() {
2722
- this.registerHelpers(helpers$20);
2723
- this.registerHelpers(helpers$18);
2931
+ this.registerHelpers(helpers$23);
2932
+ this.registerHelpers(helpers$19);
2933
+ this.registerHelpers(helpers$17);
2724
2934
  this.registerHelpers(helpers$16);
2935
+ this.registerHelpers(helpers$21);
2936
+ this.registerHelpers(helpers$20);
2937
+ this.registerHelpers(helpers$9);
2938
+ this.registerHelpers(helpers$22);
2725
2939
  this.registerHelpers(helpers$15);
2726
- this.registerHelpers(helpers$19);
2727
- this.registerHelpers(helpers$8);
2940
+ this.registerHelpers(helpers$18);
2728
2941
  this.registerHelpers(helpers$14);
2729
- this.registerHelpers(helpers$17);
2730
2942
  this.registerHelpers(helpers$13);
2731
2943
  this.registerHelpers(helpers$12);
2732
2944
  this.registerHelpers(helpers$11);
2733
2945
  this.registerHelpers(helpers$10);
2734
- this.registerHelpers(helpers$9);
2946
+ this.registerHelpers(helpers$8);
2735
2947
  this.registerHelpers(helpers$7);
2736
- this.registerHelpers(helpers$6);
2948
+ this.registerHelpers(helpers$5);
2737
2949
  this.registerHelpers(helpers$4);
2738
2950
  this.registerHelpers(helpers$3);
2739
2951
  this.registerHelpers(helpers$2);
2740
2952
  this.registerHelpers(helpers$1);
2741
- this.registerHelpers(helpers$5);
2742
- }
2743
- /**
2744
- * Register a helper.
2745
- * @param {Helper} helper The helper to register.
2746
- * @returns True if the helper was registered, false otherwise.
2747
- */
2748
- register(helper) {
2749
- const result = false;
2750
- if (!this.has(helper.name)) this._helpers.push(helper);
2751
- return result;
2752
- }
2753
- /**
2754
- * Register multiple helpers.
2755
- * @param {Helper[]} helpers The array of helpers to register.
2756
- */
2757
- registerHelpers(helpers) {
2758
- for (const helper of helpers) this.register(helper);
2759
- }
2760
- /**
2761
- * Check if a helper is registered.
2762
- * @param name The name of the helper.
2763
- * @returns True if the helper is registered, false otherwise.
2764
- */
2765
- has(name) {
2766
- return this._helpers.some((helper) => helper.name === name);
2767
- }
2768
- /**
2769
- * Filter helpers by the given criteria.
2770
- * @param filter The filter criteria.
2771
- * @returns The filtered helpers.
2772
- */
2773
- filter(filter) {
2774
- let result = this._helpers;
2775
- if (filter.names) result = result.filter((helper) => filter.names?.includes(helper.name));
2776
- if (filter.categories) result = result.filter((helper) => filter.categories?.includes(helper.category));
2777
- if (filter.compatibility) result = result.filter((helper) => helper.compatibility?.some((c) => filter.compatibility?.includes(c)));
2778
- return result;
2779
- }
2780
- /**
2781
- * Load Handlebars helpers.
2782
- * @param {Handlebars} handlebars The Handlebars instance.
2783
- * @returns {void}
2784
- */
2785
- load(handlebars, filters) {
2786
- let helpers = this._helpers;
2787
- if (filters) helpers = this.filter(filters);
2788
- for (const helper of helpers) handlebars.registerHelper(helper.name, helper.fn);
2789
- }
2790
- /**
2791
- * Swap Handlebars helpers. This is used when you want to swap out legacy helpers for fumanchu helpers.
2792
- * @param handlebars The Handlebars instance.
2793
- * @returns {void}
2794
- */
2795
- swap(handlebars) {
2796
- for (const helper of this._helpers) {
2797
- handlebars.unregisterHelper(helper.name);
2798
- handlebars.registerHelper(helper.name, helper.fn);
2799
- }
2953
+ this.registerHelpers(helpers$6);
2800
2954
  }
2801
2955
  };
2802
2956
  //#endregion
2803
- //#region src/index.ts
2957
+ //#region src/index.node.ts
2804
2958
  /**
2805
2959
  * Handlebars library not initiated with helpers
2806
2960
  * @type {Handlebars}
2807
2961
  */
2808
- const Handlebars = HandlebarsLib;
2962
+ const Handlebars = Handlebars$1;
2809
2963
  /**
2810
2964
  * Fumanchu Handlebars instance not initiated with helpers
2811
2965
  * @type {Handlebars}
2812
2966
  */
2813
- const handlebars = HandlebarsLib.create();
2967
+ const handlebars = Handlebars$1.create();
2814
2968
  /**
2815
2969
  * Register Handlebars helpers
2816
2970
  * @param {HelpersOptions} options - Options for Fumanchu helpers
@@ -2825,8 +2979,8 @@ function helpers(options) {
2825
2979
  */
2826
2980
  async function createHandlebars() {
2827
2981
  const registry = new HelperRegistry();
2828
- const handlebars = HandlebarsLib.create();
2829
- if (Object.keys(HandlebarsLib.partials).length > 0) handlebars.registerPartial(HandlebarsLib.partials);
2982
+ const handlebars = Handlebars$1.create();
2983
+ if (Object.keys(Handlebars$1.partials).length > 0) handlebars.registerPartial(Handlebars$1.partials);
2830
2984
  registry.load(handlebars);
2831
2985
  /* c8 ignore next -- @preserve */
2832
2986
  if (process.env.NODE_ENV === "development") console.warn("createHandlebars will be deprecated in future versions, use `fumanchu` instead.");
@@ -2839,10 +2993,10 @@ async function createHandlebars() {
2839
2993
  */
2840
2994
  function fumanchu(options) {
2841
2995
  const registry = new HelperRegistry();
2842
- let handlebars = HandlebarsLib.create();
2996
+ let handlebars = Handlebars$1.create();
2843
2997
  /* c8 ignore next -- @preserve */
2844
2998
  if (options?.handlebars) handlebars = options.handlebars;
2845
- else if (Object.keys(HandlebarsLib.partials).length > 0) handlebars.registerPartial(HandlebarsLib.partials);
2999
+ else if (Object.keys(Handlebars$1.partials).length > 0) handlebars.registerPartial(Handlebars$1.partials);
2846
3000
  registry.load(handlebars);
2847
3001
  if (options?.caching) {
2848
3002
  let cache;
@@ -2854,7 +3008,7 @@ function fumanchu(options) {
2854
3008
  else cache = new CacheableMemory({ useClone: false });
2855
3009
  const originalCompile = handlebars.compile.bind(handlebars);
2856
3010
  handlebars.compile = (input, compileOptions) => {
2857
- const key = compileOptions ? input + JSON.stringify(compileOptions) : String(input);
3011
+ const key = compileOptions ? `${input}\0${JSON.stringify(compileOptions)}` : input;
2858
3012
  const cached = cache.get(key);
2859
3013
  if (cached) return cached;
2860
3014
  const compiled = originalCompile(input, compileOptions);