@jaredwray/fumanchu 4.6.1 → 4.7.0

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.
@@ -28,10 +28,6 @@ let node_fs = require("node:fs");
28
28
  node_fs = __toESM(node_fs);
29
29
  let node_path = require("node:path");
30
30
  node_path = __toESM(node_path);
31
- let html_tag = require("html-tag");
32
- html_tag = __toESM(html_tag);
33
- let to_gfm_code_block = require("to-gfm-code-block");
34
- to_gfm_code_block = __toESM(to_gfm_code_block);
35
31
  let chrono_node = require("chrono-node");
36
32
  let dayjs = require("dayjs");
37
33
  dayjs = __toESM(dayjs);
@@ -43,21 +39,95 @@ let dayjs_plugin_timezone_js = require("dayjs/plugin/timezone.js");
43
39
  dayjs_plugin_timezone_js = __toESM(dayjs_plugin_timezone_js);
44
40
  let dayjs_plugin_utc_js = require("dayjs/plugin/utc.js");
45
41
  dayjs_plugin_utc_js = __toESM(dayjs_plugin_utc_js);
46
- let is_glob = require("is-glob");
47
- is_glob = __toESM(is_glob);
48
42
  let micromatch = require("micromatch");
49
43
  micromatch = __toESM(micromatch);
50
- let striptags = require("striptags");
51
- striptags = __toESM(striptags);
52
44
  let node_process = require("node:process");
53
45
  node_process = __toESM(node_process);
54
46
  let ent = require("ent");
55
47
  ent = __toESM(ent);
56
- let remarkable = require("remarkable");
57
- let kind_of = require("kind-of");
58
- kind_of = __toESM(kind_of);
48
+ let markdown_it = require("markdown-it");
49
+ markdown_it = __toESM(markdown_it);
59
50
  let node_querystring = require("node:querystring");
60
51
  let node_url = require("node:url");
52
+ //#region src/helper-registry-base.ts
53
+ /**
54
+ * Base class for helper registries. Holds all state-management logic and
55
+ * leaves `init()` empty so environment-specific subclasses decide which
56
+ * helper sets to register. This lets the browser registry avoid importing
57
+ * any Node-only helper files.
58
+ */
59
+ var HelperRegistryBase = class {
60
+ _helpers = [];
61
+ constructor() {
62
+ this.init();
63
+ }
64
+ /**
65
+ * Get all registered helpers.
66
+ */
67
+ get helpers() {
68
+ return this._helpers;
69
+ }
70
+ /**
71
+ * Initialize the helper registry. Overridden by subclasses.
72
+ */
73
+ init() {}
74
+ /**
75
+ * Register a helper. Returns true if the helper was added, false if a
76
+ * helper with the same name was already present.
77
+ */
78
+ register(helper) {
79
+ if (this.has(helper.name)) return false;
80
+ this._helpers.push(helper);
81
+ return true;
82
+ }
83
+ /**
84
+ * Register multiple helpers.
85
+ */
86
+ registerHelpers(helpers) {
87
+ for (const helper of helpers) this.register(helper);
88
+ }
89
+ /**
90
+ * Check if a helper is registered.
91
+ */
92
+ has(name) {
93
+ return this._helpers.some((helper) => helper.name === name);
94
+ }
95
+ /**
96
+ * Filter helpers by the given criteria.
97
+ */
98
+ filter(filter) {
99
+ let result = this._helpers;
100
+ if (filter.names) {
101
+ const names = filter.names;
102
+ result = result.filter((helper) => names.includes(helper.name));
103
+ }
104
+ if (filter.categories) {
105
+ const categories = filter.categories;
106
+ result = result.filter((helper) => categories.includes(helper.category));
107
+ }
108
+ if (filter.compatibility) {
109
+ const compatibility = filter.compatibility;
110
+ result = result.filter((helper) => helper.compatibility?.some((c) => compatibility.includes(c)));
111
+ }
112
+ return result;
113
+ }
114
+ /**
115
+ * Load Handlebars helpers.
116
+ */
117
+ load(handlebars, filters) {
118
+ let helpers = this._helpers;
119
+ if (filters) helpers = this.filter(filters);
120
+ for (const helper of helpers) handlebars.registerHelper(helper.name, helper.fn);
121
+ }
122
+ /**
123
+ * Swap Handlebars helpers. This is used when you want to swap out legacy
124
+ * helpers for fumanchu helpers.
125
+ */
126
+ swap(handlebars) {
127
+ for (const helper of this._helpers) handlebars.registerHelper(helper.name, helper.fn);
128
+ }
129
+ };
130
+ //#endregion
61
131
  //#region src/helpers/array.ts
62
132
  const after = (array, n) => {
63
133
  if (!Array.isArray(array)) return [];
@@ -362,7 +432,7 @@ const unique = function(array, options) {
362
432
  }
363
433
  return "";
364
434
  };
365
- const helpers$20 = [
435
+ const helpers$23 = [
366
436
  {
367
437
  name: "after",
368
438
  category: "array",
@@ -527,23 +597,170 @@ const helpers$20 = [
527
597
  }
528
598
  ];
529
599
  //#endregion
530
- //#region src/helpers/code.ts
531
- const embed = (filepath, language) => {
532
- let ext = typeof language === "string" ? language : node_path.default.extname(filepath).slice(1);
533
- let code = node_fs.default.readFileSync(filepath, "utf8");
534
- if (ext === "markdown" || ext === "md") {
535
- ext = "markdown";
536
- code = code.split("`").join("&#x60");
600
+ //#region src/helpers/html.ts
601
+ const isTagStart = (ch) => ch !== void 0 && (ch === "/" || ch === "!" || ch === "?" || /[a-zA-Z]/.test(ch));
602
+ const stripTags = (str) => {
603
+ let result = "";
604
+ let inTag = false;
605
+ let inComment = false;
606
+ let quote = null;
607
+ for (let i = 0; i < str.length; i++) {
608
+ const ch = str[i];
609
+ if (inComment) {
610
+ if (ch === "-" && str[i + 1] === "-" && str[i + 2] === ">") {
611
+ inComment = false;
612
+ i += 2;
613
+ }
614
+ continue;
615
+ }
616
+ if (inTag) {
617
+ if (quote) {
618
+ if (ch === quote) quote = null;
619
+ } else if (ch === "\"" || ch === "'") quote = ch;
620
+ else if (ch === ">") inTag = false;
621
+ continue;
622
+ }
623
+ if (ch === "<") {
624
+ if (str[i + 1] === "!" && str[i + 2] === "-" && str[i + 3] === "-") {
625
+ inComment = true;
626
+ i += 3;
627
+ continue;
628
+ }
629
+ if (isTagStart(str[i + 1])) {
630
+ inTag = true;
631
+ continue;
632
+ }
633
+ }
634
+ result += ch;
635
+ }
636
+ return result;
637
+ };
638
+ const VOID_ELEMENTS = new Set([
639
+ "area",
640
+ "base",
641
+ "br",
642
+ "col",
643
+ "command",
644
+ "embed",
645
+ "hr",
646
+ "img",
647
+ "input",
648
+ "keygen",
649
+ "link",
650
+ "meta",
651
+ "param",
652
+ "source",
653
+ "track",
654
+ "wbr",
655
+ "circle",
656
+ "ellipse",
657
+ "line",
658
+ "path",
659
+ "polygon",
660
+ "polyline",
661
+ "rect",
662
+ "stop",
663
+ "use"
664
+ ]);
665
+ const tag = (name, attribs = {}, text = "") => {
666
+ let html = `<${name}`;
667
+ for (const [key, val] of Object.entries(attribs)) if (val === true) html += ` ${key}`;
668
+ else if (typeof val === "string" || typeof val === "number") html += ` ${key}="${escapeAttr(String(val))}"`;
669
+ if (VOID_ELEMENTS.has(name.toLowerCase())) return `${html}>`;
670
+ return `${html}>${escapeText(text)}</${name}>`;
671
+ };
672
+ const escapeAttr = (str) => str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
673
+ const escapeText = (str) => str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
674
+ const parseAttributes = (hash = {}) => Object.keys(hash).map((key) => `${key}="${String(hash[key]).replace(/^['"]|["']$/g, "")}"`).join(" ");
675
+ const attr = (options) => {
676
+ const val = parseAttributes(options?.hash ?? {});
677
+ return val.trim() ? ` ${val}` : "";
678
+ };
679
+ const sanitize = (str) => {
680
+ if (typeof str !== "string") return "";
681
+ return stripTags(str).trim();
682
+ };
683
+ const ul = (context, options) => {
684
+ const attrs = parseAttributes(options.hash ?? {});
685
+ const items = context.map((item) => typeof item === "string" ? item : options.fn(item));
686
+ return `${attrs ? `<ul ${attrs}>` : "<ul>"}${items.map((i) => `<li>${i}</li>`).join("\n")}</ul>`;
687
+ };
688
+ const ol = (context, options) => {
689
+ const attrs = parseAttributes(options.hash ?? {});
690
+ const items = context.map((item) => typeof item === "string" ? item : options.fn(item));
691
+ return `${attrs ? `<ol ${attrs}>` : "<ol>"}${items.map((i) => `<li>${i}</li>`).join("\n")}</ol>`;
692
+ };
693
+ const thumbnailImage = (context) => {
694
+ let figure = "";
695
+ let image = "";
696
+ const link = context.full || false;
697
+ const imageAttributes = {
698
+ alt: context.alt,
699
+ src: context.thumbnail,
700
+ width: context.size.width,
701
+ height: context.size.height
702
+ };
703
+ const figureAttributes = { id: `image-${context.id}` };
704
+ const linkAttributes = {
705
+ href: link,
706
+ rel: "thumbnail"
707
+ };
708
+ /* c8 ignore next -- @preserve */
709
+ if (context.classes) {
710
+ if (context.classes.image) imageAttributes.class = context.classes.image.join(" ");
711
+ if (context.classes.figure) figureAttributes.class = context.classes.figure.join(" ");
712
+ if (context.classes.link) linkAttributes.class = context.classes.link.join(" ");
537
713
  }
538
- return `${(0, to_gfm_code_block.default)(code, ext).trim()}\n`;
714
+ figure += `<figure ${parseAttributes(figureAttributes)}>\n`;
715
+ image += `<img ${parseAttributes(imageAttributes)}>\n`;
716
+ if (link) figure += `<a ${parseAttributes(linkAttributes)}>\n${image}</a>\n`;
717
+ else figure += image;
718
+ /* c8 ignore next -- @preserve */
719
+ if (context.caption) figure += `<figcaption>${context.caption}</figcaption>\n`;
720
+ figure += "</figure>";
721
+ return figure;
539
722
  };
723
+ const helpers$22 = [
724
+ {
725
+ name: "attr",
726
+ category: "html",
727
+ compatibility: ["browser", "nodejs"],
728
+ fn: attr
729
+ },
730
+ {
731
+ name: "sanitize",
732
+ category: "html",
733
+ compatibility: ["browser", "nodejs"],
734
+ fn: sanitize
735
+ },
736
+ {
737
+ name: "ul",
738
+ category: "html",
739
+ compatibility: ["browser", "nodejs"],
740
+ fn: ul
741
+ },
742
+ {
743
+ name: "ol",
744
+ category: "html",
745
+ compatibility: ["browser", "nodejs"],
746
+ fn: ol
747
+ },
748
+ {
749
+ name: "thumbnailImage",
750
+ category: "html",
751
+ compatibility: ["browser", "nodejs"],
752
+ fn: thumbnailImage
753
+ }
754
+ ];
755
+ //#endregion
756
+ //#region src/helpers/code.ts
540
757
  const gist = (id) => {
541
- return (0, html_tag.default)("script", { src: `https://gist.github.com/${id}.js` });
758
+ return tag("script", { src: `https://gist.github.com/${id}.js` });
542
759
  };
543
760
  const jsfiddle = (options) => {
544
761
  if (!options?.id) throw new Error("jsfiddle helper expects an `id`");
545
762
  const { id, width = "100%", height = "300", skin = "/presentation/", tabs = "result,js,html,css", allowfullscreen = "allowfullscreen", frameborder = "0" } = options;
546
- return (0, html_tag.default)("iframe", {
763
+ return tag("iframe", {
547
764
  width,
548
765
  height,
549
766
  src: `http://jsfiddle.net/${id}/embedded/${tabs}${skin}`,
@@ -551,26 +768,37 @@ const jsfiddle = (options) => {
551
768
  frameborder
552
769
  });
553
770
  };
554
- const helpers$19 = [
555
- {
556
- name: "embed",
557
- category: "code",
558
- compatibility: ["nodejs"],
559
- fn: embed
560
- },
561
- {
562
- name: "gist",
563
- category: "code",
564
- compatibility: ["browser", "nodejs"],
565
- fn: gist
566
- },
567
- {
568
- name: "jsfiddle",
569
- category: "code",
570
- compatibility: ["browser", "nodejs"],
571
- fn: jsfiddle
771
+ const helpers$21 = [{
772
+ name: "gist",
773
+ category: "code",
774
+ compatibility: ["browser", "nodejs"],
775
+ fn: gist
776
+ }, {
777
+ name: "jsfiddle",
778
+ category: "code",
779
+ compatibility: ["browser", "nodejs"],
780
+ fn: jsfiddle
781
+ }];
782
+ //#endregion
783
+ //#region src/helpers/code.node.ts
784
+ const codeBlock = (code, lang) => {
785
+ return `\`\`\`${lang}\n${code}\n\`\`\`\n`;
786
+ };
787
+ const embed = (filepath, language) => {
788
+ let ext = typeof language === "string" ? language : node_path.default.extname(filepath).slice(1);
789
+ let code = node_fs.default.readFileSync(filepath, "utf8");
790
+ if (ext === "markdown" || ext === "md") {
791
+ ext = "markdown";
792
+ code = code.split("`").join("&#x60");
572
793
  }
573
- ];
794
+ return `${codeBlock(code, ext).trim()}\n`;
795
+ };
796
+ const helpers$20 = [{
797
+ name: "embed",
798
+ category: "code",
799
+ compatibility: ["nodejs"],
800
+ fn: embed
801
+ }];
574
802
  //#endregion
575
803
  //#region src/helpers/collection.ts
576
804
  const isEmpty = (collection) => {
@@ -592,7 +820,7 @@ const iterate = (collection, fn, inverse) => {
592
820
  }
593
821
  return inverse ? inverse() : "";
594
822
  };
595
- const helpers$18 = [{
823
+ const helpers$19 = [{
596
824
  name: "isEmpty",
597
825
  category: "collection",
598
826
  compatibility: ["browser", "nodejs"],
@@ -684,7 +912,7 @@ const unlessGt = (a, b) => a <= b;
684
912
  const unlessLt = (a, b) => a >= b;
685
913
  const unlessGteq = (a, b) => a < b;
686
914
  const unlessLteq = (a, b) => a > b;
687
- const helpers$17 = [
915
+ const helpers$18 = [
688
916
  {
689
917
  name: "and",
690
918
  category: "comparison",
@@ -965,7 +1193,7 @@ const dateLocale = (dateInput, locale, formatString) => {
965
1193
  if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
966
1194
  return parsed.locale(locale).format(convertedFormat);
967
1195
  };
968
- const helpers$16 = [
1196
+ const helpers$17 = [
969
1197
  {
970
1198
  name: "year",
971
1199
  category: "date",
@@ -1089,6 +1317,9 @@ const helpers$16 = [
1089
1317
  ];
1090
1318
  //#endregion
1091
1319
  //#region src/helpers/fs.ts
1320
+ const ESCAPE_SEQUENCE = /\\[\s\S]/g;
1321
+ const GLOB_PATTERN = /^!|[*?]|\[[^\]]+\]|\{[^}]+\}|[@?!+*]\([^)]*\)|\([^)]*\|[^)]*\)/;
1322
+ const isGlob = (value) => GLOB_PATTERN.test(value.replace(ESCAPE_SEQUENCE, ""));
1092
1323
  const fileSize = (value, precision) => {
1093
1324
  if (value == null) return "0 B";
1094
1325
  const number = typeof value === "number" ? value : value.length ?? 0;
@@ -1126,7 +1357,7 @@ const readdir = (dir, filter) => {
1126
1357
  if (typeof filterArg === "function") return filterArg(files);
1127
1358
  if (filterArg instanceof RegExp) return files.filter((fp) => filterArg.test(fp));
1128
1359
  if (typeof filterArg === "string") {
1129
- if ((0, is_glob.default)(filterArg)) return (0, micromatch.default)(files, filterArg);
1360
+ if (isGlob(filterArg)) return (0, micromatch.default)(files, filterArg);
1130
1361
  if (filterArg === "isFile" || filterArg === "isDirectory") return files.filter((fp) => {
1131
1362
  const stat = node_fs.default.statSync(fp);
1132
1363
  return filterArg === "isFile" ? stat.isFile() : stat.isDirectory();
@@ -1134,7 +1365,7 @@ const readdir = (dir, filter) => {
1134
1365
  }
1135
1366
  return files;
1136
1367
  };
1137
- const helpers$15 = [
1368
+ const helpers$16 = [
1138
1369
  {
1139
1370
  name: "fileSize",
1140
1371
  category: "fs",
@@ -1155,12 +1386,7 @@ const helpers$15 = [
1155
1386
  }
1156
1387
  ];
1157
1388
  //#endregion
1158
- //#region src/helpers/html.ts
1159
- const parseAttributes = (hash = {}) => Object.keys(hash).map((key) => `${key}="${String(hash[key]).replace(/^['"]|["']$/g, "")}"`).join(" ");
1160
- const attr = (options) => {
1161
- const val = parseAttributes(options?.hash ?? {});
1162
- return val.trim() ? ` ${val}` : "";
1163
- };
1389
+ //#region src/helpers/html.node.ts
1164
1390
  const css = function(list, options) {
1165
1391
  if (arguments.length < 2) {
1166
1392
  options = list;
@@ -1185,99 +1411,22 @@ const js = (context) => {
1185
1411
  return `<script${attrStr ? ` ${attrStr}` : ""}><\/script>`;
1186
1412
  }
1187
1413
  if (typeof context === "string") return `<script src="${context}"><\/script>`;
1188
- return arrayify(context).map((fp) => node_path.default.extname(fp) === ".coffee" ? (0, html_tag.default)("script", {
1414
+ return arrayify(context).map((fp) => node_path.default.extname(fp) === ".coffee" ? tag("script", {
1189
1415
  type: "text/coffeescript",
1190
1416
  src: fp
1191
- }) : (0, html_tag.default)("script", { src: fp })).join("\n");
1192
- };
1193
- const sanitize = (str) => {
1194
- if (typeof str !== "string") return "";
1195
- return (0, striptags.default)(str).trim();
1196
- };
1197
- const ul = (context, options) => {
1198
- const attrs = parseAttributes(options.hash ?? {});
1199
- const items = context.map((item) => typeof item === "string" ? item : options.fn(item));
1200
- return `${attrs ? `<ul ${attrs}>` : "<ul>"}${items.map((i) => `<li>${i}</li>`).join("\n")}</ul>`;
1201
- };
1202
- const ol = (context, options) => {
1203
- const attrs = parseAttributes(options.hash ?? {});
1204
- const items = context.map((item) => typeof item === "string" ? item : options.fn(item));
1205
- return `${attrs ? `<ol ${attrs}>` : "<ol>"}${items.map((i) => `<li>${i}</li>`).join("\n")}</ol>`;
1206
- };
1207
- const thumbnailImage = (context) => {
1208
- let figure = "";
1209
- let image = "";
1210
- const link = context.full || false;
1211
- const imageAttributes = {
1212
- alt: context.alt,
1213
- src: context.thumbnail,
1214
- width: context.size.width,
1215
- height: context.size.height
1216
- };
1217
- const figureAttributes = { id: `image-${context.id}` };
1218
- const linkAttributes = {
1219
- href: link,
1220
- rel: "thumbnail"
1221
- };
1222
- /* c8 ignore next -- @preserve */
1223
- if (context.classes) {
1224
- if (context.classes.image) imageAttributes.class = context.classes.image.join(" ");
1225
- if (context.classes.figure) figureAttributes.class = context.classes.figure.join(" ");
1226
- if (context.classes.link) linkAttributes.class = context.classes.link.join(" ");
1227
- }
1228
- figure += `<figure ${parseAttributes(figureAttributes)}>\n`;
1229
- image += `<img ${parseAttributes(imageAttributes)}>\n`;
1230
- if (link) figure += `<a ${parseAttributes(linkAttributes)}>\n${image}</a>\n`;
1231
- else figure += image;
1232
- /* c8 ignore next -- @preserve */
1233
- if (context.caption) figure += `<figcaption>${context.caption}</figcaption>\n`;
1234
- figure += "</figure>";
1235
- return figure;
1417
+ }) : tag("script", { src: fp })).join("\n");
1236
1418
  };
1237
- const helpers$14 = [
1238
- {
1239
- name: "attr",
1240
- category: "html",
1241
- compatibility: ["browser", "nodejs"],
1242
- fn: attr
1243
- },
1244
- {
1245
- name: "css",
1246
- category: "html",
1247
- compatibility: ["nodejs"],
1248
- fn: css
1249
- },
1250
- {
1251
- name: "js",
1252
- category: "html",
1253
- compatibility: ["nodejs"],
1254
- fn: js
1255
- },
1256
- {
1257
- name: "sanitize",
1258
- category: "html",
1259
- compatibility: ["browser", "nodejs"],
1260
- fn: sanitize
1261
- },
1262
- {
1263
- name: "ul",
1264
- category: "html",
1265
- compatibility: ["browser", "nodejs"],
1266
- fn: ul
1267
- },
1268
- {
1269
- name: "ol",
1270
- category: "html",
1271
- compatibility: ["browser", "nodejs"],
1272
- fn: ol
1273
- },
1274
- {
1275
- name: "thumbnailImage",
1276
- category: "html",
1277
- compatibility: ["browser", "nodejs"],
1278
- fn: thumbnailImage
1279
- }
1280
- ];
1419
+ const helpers$15 = [{
1420
+ name: "css",
1421
+ category: "html",
1422
+ compatibility: ["nodejs"],
1423
+ fn: css
1424
+ }, {
1425
+ name: "js",
1426
+ category: "html",
1427
+ compatibility: ["nodejs"],
1428
+ fn: js
1429
+ }];
1281
1430
  //#endregion
1282
1431
  //#region src/utils.ts
1283
1432
  /**
@@ -1355,7 +1504,7 @@ const i18n = function(prop, locals, options) {
1355
1504
  if (typeof result === "undefined") throw new Error("{{i18n}} helper cannot find property \"" + String(prop) + "\" for language \"" + lang + "\"");
1356
1505
  return String(result);
1357
1506
  };
1358
- const helpers$13 = [{
1507
+ const helpers$14 = [{
1359
1508
  name: "i18n",
1360
1509
  category: "i18n",
1361
1510
  compatibility: ["browser", "nodejs"],
@@ -1383,7 +1532,7 @@ const ordinalize = (value) => {
1383
1532
  default: return `${str}th`;
1384
1533
  }
1385
1534
  };
1386
- const helpers$12 = [{
1535
+ const helpers$13 = [{
1387
1536
  name: "inflect",
1388
1537
  category: "inflection",
1389
1538
  compatibility: ["browser", "nodejs"],
@@ -1511,7 +1660,7 @@ function switchOutput(type, json) {
1511
1660
  default: return json;
1512
1661
  }
1513
1662
  }
1514
- const helpers$11 = [
1663
+ const helpers$12 = [
1515
1664
  {
1516
1665
  name: "log",
1517
1666
  category: "logging",
@@ -1594,7 +1743,7 @@ const mm = (...args) => {
1594
1743
  console.log("helper instead.");
1595
1744
  return match(...args);
1596
1745
  };
1597
- const helpers$10 = [
1746
+ const helpers$11 = [
1598
1747
  {
1599
1748
  name: "match",
1600
1749
  category: "match",
@@ -1676,7 +1825,7 @@ const sum = (...values) => {
1676
1825
  return total;
1677
1826
  };
1678
1827
  const times = (a, b) => multiply(a, b);
1679
- const helpers$9 = [
1828
+ const helpers$10 = [
1680
1829
  {
1681
1830
  name: "abs",
1682
1831
  category: "math",
@@ -1776,6 +1925,18 @@ const helpers$9 = [
1776
1925
  ];
1777
1926
  //#endregion
1778
1927
  //#region src/helpers/md.ts
1928
+ const md = new markdown_it.default({
1929
+ breaks: true,
1930
+ html: true,
1931
+ langPrefix: "lang-",
1932
+ typographer: false,
1933
+ xhtmlOut: false
1934
+ });
1935
+ function renderString(input) {
1936
+ return new handlebars.default.SafeString(ent.default.decode(md.render(input)));
1937
+ }
1938
+ //#endregion
1939
+ //#region src/helpers/md.node.ts
1779
1940
  const renderMarkdown = function(input, options) {
1780
1941
  if (typeof input === "object" && input && "fn" in input && typeof input.fn === "function") {
1781
1942
  options = input;
@@ -1785,21 +1946,14 @@ const renderMarkdown = function(input, options) {
1785
1946
  cwd: node_process.default.cwd(),
1786
1947
  ...options
1787
1948
  };
1788
- const md = new remarkable.Remarkable({
1789
- breaks: true,
1790
- html: true,
1791
- langPrefix: "lang-",
1792
- typographer: false,
1793
- xhtmlOut: false
1794
- });
1795
1949
  let string_ = input;
1796
- if (string_ && string_.length > 0) {
1950
+ if (string_.length > 0) {
1797
1951
  const filepath = node_path.default.resolve(options_.cwd, string_);
1798
1952
  if (node_fs.default.existsSync(filepath) && node_fs.default.statSync(filepath).isFile()) string_ = node_fs.default.readFileSync(filepath, "utf8");
1799
1953
  }
1800
- return new handlebars.default.SafeString(ent.default.decode(md.render(string_)));
1954
+ return renderString(string_);
1801
1955
  };
1802
- const helpers$8 = [{
1956
+ const helpers$9 = [{
1803
1957
  name: "md",
1804
1958
  category: "markdown",
1805
1959
  compatibility: ["nodejs"],
@@ -1812,6 +1966,68 @@ const helpers$8 = [{
1812
1966
  }];
1813
1967
  //#endregion
1814
1968
  //#region src/helpers/misc.ts
1969
+ const objectToString = Object.prototype.toString;
1970
+ function ctorName(val) {
1971
+ return typeof val.constructor === "function" ? val.constructor.name : null;
1972
+ }
1973
+ function isGeneratorFn(val) {
1974
+ return ctorName(val) === "GeneratorFunction";
1975
+ }
1976
+ function isGeneratorObj(val) {
1977
+ return typeof val.throw === "function" && typeof val.return === "function" && typeof val.next === "function";
1978
+ }
1979
+ function isDateLike(val) {
1980
+ return typeof val.toDateString === "function" && typeof val.getDate === "function" && typeof val.setDate === "function";
1981
+ }
1982
+ function isErrorLike(val) {
1983
+ return typeof val.message === "string" && val.constructor && typeof val.constructor.stackTraceLimit === "number";
1984
+ }
1985
+ function isRegexpLike(val) {
1986
+ return typeof val.flags === "string" && typeof val.ignoreCase === "boolean" && typeof val.multiline === "boolean" && typeof val.global === "boolean";
1987
+ }
1988
+ function kindOf(val) {
1989
+ if (val === void 0) return "undefined";
1990
+ if (val === null) return "null";
1991
+ const type = typeof val;
1992
+ if (type === "boolean") return "boolean";
1993
+ if (type === "string") return "string";
1994
+ if (type === "number") return "number";
1995
+ if (type === "symbol") return "symbol";
1996
+ if (type === "bigint") return "bigint";
1997
+ if (type === "function") return isGeneratorFn(val) ? "generatorfunction" : "function";
1998
+ if (Array.isArray(val)) return "array";
1999
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer?.(val)) return "buffer";
2000
+ if (typeof val.length === "number" && objectToString.call(val) === "[object Arguments]") return "arguments";
2001
+ if (val instanceof Date || isDateLike(val)) return "date";
2002
+ if (val instanceof Error || isErrorLike(val)) return "error";
2003
+ if (val instanceof RegExp || isRegexpLike(val)) return "regexp";
2004
+ switch (ctorName(val)) {
2005
+ case "Promise": return "promise";
2006
+ case "WeakMap": return "weakmap";
2007
+ case "WeakSet": return "weakset";
2008
+ case "Map": return "map";
2009
+ case "Set": return "set";
2010
+ case "Int8Array": return "int8array";
2011
+ case "Uint8Array": return "uint8array";
2012
+ case "Uint8ClampedArray": return "uint8clampedarray";
2013
+ case "Int16Array": return "int16array";
2014
+ case "Uint16Array": return "uint16array";
2015
+ case "Int32Array": return "int32array";
2016
+ case "Uint32Array": return "uint32array";
2017
+ case "Float32Array": return "float32array";
2018
+ case "Float64Array": return "float64array";
2019
+ }
2020
+ if (isGeneratorObj(val)) return "generator";
2021
+ const tag = objectToString.call(val);
2022
+ switch (tag) {
2023
+ case "[object Object]": return "object";
2024
+ case "[object Map Iterator]": return "mapiterator";
2025
+ case "[object Set Iterator]": return "setiterator";
2026
+ case "[object String Iterator]": return "stringiterator";
2027
+ case "[object Array Iterator]": return "arrayiterator";
2028
+ }
2029
+ return tag.slice(8, -1).toLowerCase().replace(/\s/g, "");
2030
+ }
1815
2031
  /**
1816
2032
  * Block helper for exposing private `@` variables on the context
1817
2033
  * Replaces the external handlebars-helper-create-frame package
@@ -1832,12 +2048,12 @@ function option(prop, locals, opts) {
1832
2048
  function noop(options) {
1833
2049
  return options.fn(this);
1834
2050
  }
1835
- const typeOf = kind_of.default;
2051
+ const typeOf = kindOf;
1836
2052
  function withHash(options) {
1837
2053
  if (options.hash && Object.keys(options.hash).length) return options.fn(options.hash);
1838
2054
  return options.inverse(this);
1839
2055
  }
1840
- const helpers$7 = [
2056
+ const helpers$8 = [
1841
2057
  {
1842
2058
  name: "frame",
1843
2059
  category: "misc",
@@ -1940,7 +2156,7 @@ const toPrecision = (value, precision) => {
1940
2156
  const prec = typeof precision === "number" && !Number.isNaN(precision) ? precision : 1;
1941
2157
  return Number(num).toPrecision(prec);
1942
2158
  };
1943
- const helpers$6 = [
2159
+ const helpers$7 = [
1944
2160
  {
1945
2161
  name: "bytes",
1946
2162
  category: "number",
@@ -2080,7 +2296,7 @@ const pick = (props, context, options) => {
2080
2296
  }
2081
2297
  return result;
2082
2298
  };
2083
- const helpers$5 = [
2299
+ const helpers$6 = [
2084
2300
  {
2085
2301
  name: "extend",
2086
2302
  category: "object",
@@ -2218,7 +2434,7 @@ const segments = (filepath, a, b) => {
2218
2434
  const end = Number(b);
2219
2435
  return fp.split(/[\\/]+/).slice(start, end).join("/");
2220
2436
  };
2221
- const helpers$4 = [
2437
+ const helpers$5 = [
2222
2438
  {
2223
2439
  name: "absolute",
2224
2440
  category: "path",
@@ -2279,7 +2495,7 @@ const test = (str, regex) => {
2279
2495
  if (!(regex instanceof RegExp)) throw new TypeError("expected a regular expression");
2280
2496
  return regex.test(str);
2281
2497
  };
2282
- const helpers$3 = [{
2498
+ const helpers$4 = [{
2283
2499
  name: "toRegex",
2284
2500
  category: "regex",
2285
2501
  compatibility: ["browser", "nodejs"],
@@ -2436,7 +2652,7 @@ const raw = (options) => {
2436
2652
  if (options && typeof options.fn === "function") return options.fn();
2437
2653
  return "";
2438
2654
  };
2439
- const helpers$2 = [
2655
+ const helpers$3 = [
2440
2656
  {
2441
2657
  name: "append",
2442
2658
  category: "string",
@@ -2660,9 +2876,6 @@ const all = ["browser", "nodejs"];
2660
2876
  const encodeUri = (str) => {
2661
2877
  if (typeof str === "string") return encodeURIComponent(str);
2662
2878
  };
2663
- const escapeFn = (str) => {
2664
- if (typeof str === "string") return (0, node_querystring.escape)(str);
2665
- };
2666
2879
  const decodeUri = (str) => {
2667
2880
  if (typeof str === "string") return decodeURIComponent(str);
2668
2881
  };
@@ -2672,31 +2885,16 @@ function urlEncode(...args) {
2672
2885
  function urlDecode(...args) {
2673
2886
  return decodeUri.apply(this, args);
2674
2887
  }
2675
- const urlResolve = (base, href) => (0, node_url.resolve)(String(base), String(href));
2676
- const urlParse = (str) => (0, node_url.parse)(String(str));
2677
2888
  const stripQuerystring = (str) => {
2678
2889
  if (typeof str === "string") return str.split("?")[0];
2679
2890
  };
2680
- const stripProtocol = (str) => {
2681
- if (typeof str === "string") {
2682
- const parsed = (0, node_url.parse)(str);
2683
- parsed.protocol = "";
2684
- return (0, node_url.format)(parsed);
2685
- }
2686
- };
2687
- const helpers$1 = [
2891
+ const helpers$2 = [
2688
2892
  {
2689
2893
  name: "encodeURI",
2690
2894
  category: "url",
2691
2895
  compatibility: all,
2692
2896
  fn: encodeUri
2693
2897
  },
2694
- {
2695
- name: "escape",
2696
- category: "url",
2697
- compatibility: ["nodejs"],
2698
- fn: escapeFn
2699
- },
2700
2898
  {
2701
2899
  name: "decodeURI",
2702
2900
  category: "url",
@@ -2715,6 +2913,34 @@ const helpers$1 = [
2715
2913
  compatibility: all,
2716
2914
  fn: urlDecode
2717
2915
  },
2916
+ {
2917
+ name: "stripQuerystring",
2918
+ category: "url",
2919
+ compatibility: all,
2920
+ fn: stripQuerystring
2921
+ }
2922
+ ];
2923
+ //#endregion
2924
+ //#region src/helpers/url.node.ts
2925
+ const escapeFn = (str) => {
2926
+ if (typeof str === "string") return (0, node_querystring.escape)(str);
2927
+ };
2928
+ const urlResolve = (base, href) => (0, node_url.resolve)(String(base), String(href));
2929
+ const urlParse = (str) => (0, node_url.parse)(String(str));
2930
+ const stripProtocol = (str) => {
2931
+ if (typeof str === "string") {
2932
+ const parsed = (0, node_url.parse)(str);
2933
+ parsed.protocol = "";
2934
+ return (0, node_url.format)(parsed);
2935
+ }
2936
+ };
2937
+ const helpers$1 = [
2938
+ {
2939
+ name: "escape",
2940
+ category: "url",
2941
+ compatibility: ["nodejs"],
2942
+ fn: escapeFn
2943
+ },
2718
2944
  {
2719
2945
  name: "urlResolve",
2720
2946
  category: "url",
@@ -2727,12 +2953,6 @@ const helpers$1 = [
2727
2953
  compatibility: ["nodejs"],
2728
2954
  fn: urlParse
2729
2955
  },
2730
- {
2731
- name: "stripQuerystring",
2732
- category: "url",
2733
- compatibility: all,
2734
- fn: stripQuerystring
2735
- },
2736
2956
  {
2737
2957
  name: "stripProtocol",
2738
2958
  category: "url",
@@ -2742,104 +2962,35 @@ const helpers$1 = [
2742
2962
  ];
2743
2963
  //#endregion
2744
2964
  //#region src/helper-registry.ts
2745
- var HelperRegistry = class {
2746
- _helpers = [];
2747
- constructor() {
2748
- this.init();
2749
- }
2750
- /**
2751
- * Get all registered helpers.
2752
- * @returns {Helper[]} The array of registered helpers.
2753
- */
2754
- get helpers() {
2755
- return this._helpers;
2756
- }
2757
- /**
2758
- * Initialize the helper registry. This is performed during the construction of the registry such as new HelperRegistry().
2759
- */
2965
+ var HelperRegistry = class extends HelperRegistryBase {
2760
2966
  init() {
2761
- this.registerHelpers(helpers$20);
2762
- this.registerHelpers(helpers$18);
2967
+ this.registerHelpers(helpers$23);
2968
+ this.registerHelpers(helpers$19);
2969
+ this.registerHelpers(helpers$17);
2763
2970
  this.registerHelpers(helpers$16);
2971
+ this.registerHelpers(helpers$21);
2972
+ this.registerHelpers(helpers$20);
2973
+ this.registerHelpers(helpers$9);
2974
+ this.registerHelpers(helpers$22);
2764
2975
  this.registerHelpers(helpers$15);
2765
- this.registerHelpers(helpers$19);
2766
- this.registerHelpers(helpers$8);
2976
+ this.registerHelpers(helpers$18);
2767
2977
  this.registerHelpers(helpers$14);
2768
- this.registerHelpers(helpers$17);
2769
2978
  this.registerHelpers(helpers$13);
2770
2979
  this.registerHelpers(helpers$12);
2771
2980
  this.registerHelpers(helpers$11);
2772
2981
  this.registerHelpers(helpers$10);
2773
- this.registerHelpers(helpers$9);
2982
+ this.registerHelpers(helpers$8);
2774
2983
  this.registerHelpers(helpers$7);
2775
- this.registerHelpers(helpers$6);
2984
+ this.registerHelpers(helpers$5);
2776
2985
  this.registerHelpers(helpers$4);
2777
2986
  this.registerHelpers(helpers$3);
2778
2987
  this.registerHelpers(helpers$2);
2779
2988
  this.registerHelpers(helpers$1);
2780
- this.registerHelpers(helpers$5);
2781
- }
2782
- /**
2783
- * Register a helper.
2784
- * @param {Helper} helper The helper to register.
2785
- * @returns True if the helper was registered, false otherwise.
2786
- */
2787
- register(helper) {
2788
- const result = false;
2789
- if (!this.has(helper.name)) this._helpers.push(helper);
2790
- return result;
2791
- }
2792
- /**
2793
- * Register multiple helpers.
2794
- * @param {Helper[]} helpers The array of helpers to register.
2795
- */
2796
- registerHelpers(helpers) {
2797
- for (const helper of helpers) this.register(helper);
2798
- }
2799
- /**
2800
- * Check if a helper is registered.
2801
- * @param name The name of the helper.
2802
- * @returns True if the helper is registered, false otherwise.
2803
- */
2804
- has(name) {
2805
- return this._helpers.some((helper) => helper.name === name);
2806
- }
2807
- /**
2808
- * Filter helpers by the given criteria.
2809
- * @param filter The filter criteria.
2810
- * @returns The filtered helpers.
2811
- */
2812
- filter(filter) {
2813
- let result = this._helpers;
2814
- if (filter.names) result = result.filter((helper) => filter.names?.includes(helper.name));
2815
- if (filter.categories) result = result.filter((helper) => filter.categories?.includes(helper.category));
2816
- if (filter.compatibility) result = result.filter((helper) => helper.compatibility?.some((c) => filter.compatibility?.includes(c)));
2817
- return result;
2818
- }
2819
- /**
2820
- * Load Handlebars helpers.
2821
- * @param {Handlebars} handlebars The Handlebars instance.
2822
- * @returns {void}
2823
- */
2824
- load(handlebars, filters) {
2825
- let helpers = this._helpers;
2826
- if (filters) helpers = this.filter(filters);
2827
- for (const helper of helpers) handlebars.registerHelper(helper.name, helper.fn);
2828
- }
2829
- /**
2830
- * Swap Handlebars helpers. This is used when you want to swap out legacy helpers for fumanchu helpers.
2831
- * @param handlebars The Handlebars instance.
2832
- * @returns {void}
2833
- */
2834
- swap(handlebars) {
2835
- for (const helper of this._helpers) {
2836
- handlebars.unregisterHelper(helper.name);
2837
- handlebars.registerHelper(helper.name, helper.fn);
2838
- }
2989
+ this.registerHelpers(helpers$6);
2839
2990
  }
2840
2991
  };
2841
2992
  //#endregion
2842
- //#region src/index.ts
2993
+ //#region src/index.node.ts
2843
2994
  /**
2844
2995
  * Handlebars library not initiated with helpers
2845
2996
  * @type {Handlebars}
@@ -2893,7 +3044,7 @@ function fumanchu(options) {
2893
3044
  else cache = new _cacheable_memory.CacheableMemory({ useClone: false });
2894
3045
  const originalCompile = handlebars$3.compile.bind(handlebars$3);
2895
3046
  handlebars$3.compile = (input, compileOptions) => {
2896
- const key = compileOptions ? input + JSON.stringify(compileOptions) : String(input);
3047
+ const key = compileOptions ? `${input}\0${JSON.stringify(compileOptions)}` : input;
2897
3048
  const cached = cache.get(key);
2898
3049
  if (cached) return cached;
2899
3050
  const compiled = originalCompile(input, compileOptions);