UNPKG

@jaredwray/fumanchu

Version:
2,096 lines (2,075 loc) 53 kB
// src/index.ts import * as HandlebarsLib from "handlebars"; // src/helpers/array.ts var after = (array, n) => { if (!Array.isArray(array)) return []; return array.slice(n); }; var before = (array, n) => { if (!Array.isArray(array)) return []; return array.slice(0, -n); }; var arrayify = (value) => { if (value == null) return []; return Array.isArray(value) ? value : [value]; }; var first = (array, n) => { if (!Array.isArray(array)) return void 0; if (typeof n !== "number") { return array[0]; } return array.slice(0, n); }; var last = (value, n) => { if (!Array.isArray(value) && typeof value !== "string") { return void 0; } if (typeof n !== "number") { const array = value; return array[array.length - 1]; } const start = Math.abs(n); if (typeof value === "string") { return value.slice(-start); } return value.slice(-start); }; var length = (value) => { if (value && typeof value === "object" && !Array.isArray(value)) { return Object.keys(value).length; } if (typeof value === "string" || Array.isArray(value)) { return value.length; } return 0; }; var join = (array, separator = ", ") => { if (typeof array === "string") return array; if (!Array.isArray(array)) return ""; return array.join(separator); }; var helpers = [ { name: "after", category: "array", compatibility: ["browser", "nodejs"], fn: after }, { name: "before", category: "array", compatibility: ["browser", "nodejs"], fn: before }, { name: "arrayify", category: "array", compatibility: ["browser", "nodejs"], fn: arrayify }, { name: "first", category: "array", compatibility: ["browser", "nodejs"], fn: first }, { name: "last", category: "array", compatibility: ["browser", "nodejs"], fn: last }, { name: "length", category: "array", compatibility: ["browser", "nodejs"], fn: length }, { name: "join", category: "array", compatibility: ["browser", "nodejs"], fn: join } ]; // src/helpers/code.ts import fs from "fs"; import path from "path"; import tag from "html-tag"; import codeBlock from "to-gfm-code-block"; var embed = (filepath, language) => { let ext = typeof language === "string" ? language : path.extname(filepath).slice(1); let code = fs.readFileSync(filepath, "utf8"); if (ext === "markdown" || ext === "md") { ext = "markdown"; code = code.split("`").join("&#x60"); } return `${codeBlock(code, ext).trim()} `; }; var gist = (id) => { return tag("script", { src: `https://gist.github.com/${id}.js` }); }; var jsfiddle = (options) => { if (!options || !options.id) { throw new Error("jsfiddle helper expects an `id`"); } const { id, width = "100%", height = "300", skin = "/presentation/", tabs = "result,js,html,css", allowfullscreen = "allowfullscreen", frameborder = "0" } = options; const src = `http://jsfiddle.net/${id}/embedded/${tabs}${skin}`; return tag("iframe", { width, height, src, allowfullscreen, frameborder }); }; var helpers2 = [ { name: "embed", category: "code", compatibility: ["nodejs"], fn: embed }, { name: "gist", category: "code", compatibility: ["browser", "nodejs"], fn: gist }, { name: "jsfiddle", category: "code", compatibility: ["browser", "nodejs"], fn: jsfiddle } ]; // src/helpers/collection.ts var isEmpty = (collection) => { if (collection == null) return true; if (Array.isArray(collection)) return collection.length === 0; if (typeof collection === "object") return Object.keys(collection).length === 0; return false; }; var iterate = (collection, fn, inverse) => { if (Array.isArray(collection)) { let result = ""; for (let i = 0; i < collection.length; i++) { result += fn(collection[i], i); } return result; } if (collection && typeof collection === "object") { let result = ""; for (const key of Object.keys(collection)) { result += fn(collection[key], key); } return result; } return inverse ? inverse() : ""; }; var helpers3 = [ { name: "isEmpty", category: "collection", compatibility: ["browser", "nodejs"], fn: isEmpty }, { name: "iterate", category: "collection", compatibility: ["browser", "nodejs"], fn: iterate } ]; // src/helpers/comparison.ts var and = (...values) => values.every(Boolean); var compare = (a, operator, b) => { switch (operator) { case "==": return a == b; // eslint-disable-line eqeqeq case "===": return a === b; case "!=": return a != b; // eslint-disable-line eqeqeq case "!==": return a !== b; case "<": return a < b; case ">": return a > b; case "<=": return a <= b; case ">=": return a >= b; case "typeof": return typeof a === b; default: throw new Error(`helper {{compare}}: invalid operator: '${operator}'`); } }; var contains = (collection, value, startIndex = 0) => { if (collection == null) return false; if (typeof collection === "string" || Array.isArray(collection)) { return collection.indexOf(value, startIndex) > -1; } if (typeof collection === "object") { return value in collection; } return false; }; var defaultHelper = (...values) => { for (const val of values) { if (val != null) return val; } return ""; }; var eq = (a, b) => a === b; var gt = (a, b) => a > b; var gte = (a, b) => a >= b; var has = (value, pattern) => { if (value == null || pattern == null) return false; if (Array.isArray(value) || typeof value === "string") { return value.indexOf(pattern) > -1; } if (typeof value === "object") { return pattern in value; } return false; }; var falsey = (val, keywords) => { if (!val) return true; const words = Array.isArray(keywords) ? keywords : keywords != null ? [keywords] : [ "0", "false", "nada", "nil", "nay", "nah", "negative", "no", "none", "nope", "nul", "null", "nix", "nyet", "uh-uh", "veto", "zero" ]; const lower = typeof val === "string" ? val.toLowerCase() : null; for (const word of words) { if (word === val) return true; if (lower != null && word === lower) return true; } return false; }; var isFalsey = (val) => falsey(val); var isTruthy = (val) => !falsey(val); var ifEven = (num) => typeof num === "number" && num % 2 === 0; var ifNth = (a, b) => typeof a === "number" && typeof b === "number" && b % a === 0; var ifOdd = (val) => typeof val === "number" && Math.abs(val % 2) === 1; var is = (a, b) => a == b; var isnt = (a, b) => a != b; var lt = (a, b) => a < b; var lte = (a, b) => a <= b; var neither = (a, b) => !a && !b; var not = (val) => !val; var or = (...values) => values.some(Boolean); var unlessEq = (a, b) => a !== b; var unlessGt = (a, b) => a <= b; var unlessLt = (a, b) => a >= b; var unlessGteq = (a, b) => a < b; var unlessLteq = (a, b) => a > b; var helpers4 = [ { name: "and", category: "comparison", compatibility: ["browser", "nodejs"], fn: and }, { name: "compare", category: "comparison", compatibility: ["browser", "nodejs"], fn: compare }, { name: "contains", category: "comparison", compatibility: ["browser", "nodejs"], fn: contains }, { name: "default", category: "comparison", compatibility: ["browser", "nodejs"], fn: defaultHelper }, { name: "eq", category: "comparison", compatibility: ["browser", "nodejs"], fn: eq }, { name: "gt", category: "comparison", compatibility: ["browser", "nodejs"], fn: gt }, { name: "gte", category: "comparison", compatibility: ["browser", "nodejs"], fn: gte }, { name: "has", category: "comparison", compatibility: ["browser", "nodejs"], fn: has }, { name: "isFalsey", category: "comparison", compatibility: ["browser", "nodejs"], fn: isFalsey }, { name: "isTruthy", category: "comparison", compatibility: ["browser", "nodejs"], fn: isTruthy }, { name: "ifEven", category: "comparison", compatibility: ["browser", "nodejs"], fn: ifEven }, { name: "ifNth", category: "comparison", compatibility: ["browser", "nodejs"], fn: ifNth }, { name: "ifOdd", category: "comparison", compatibility: ["browser", "nodejs"], fn: ifOdd }, { name: "is", category: "comparison", compatibility: ["browser", "nodejs"], fn: is }, { name: "isnt", category: "comparison", compatibility: ["browser", "nodejs"], fn: isnt }, { name: "lt", category: "comparison", compatibility: ["browser", "nodejs"], fn: lt }, { name: "lte", category: "comparison", compatibility: ["browser", "nodejs"], fn: lte }, { name: "neither", category: "comparison", compatibility: ["browser", "nodejs"], fn: neither }, { name: "not", category: "comparison", compatibility: ["browser", "nodejs"], fn: not }, { name: "or", category: "comparison", compatibility: ["browser", "nodejs"], fn: or }, { name: "unlessEq", category: "comparison", compatibility: ["browser", "nodejs"], fn: unlessEq }, { name: "unlessGt", category: "comparison", compatibility: ["browser", "nodejs"], fn: unlessGt }, { name: "unlessLt", category: "comparison", compatibility: ["browser", "nodejs"], fn: unlessLt }, { name: "unlessGteq", category: "comparison", compatibility: ["browser", "nodejs"], fn: unlessGteq }, { name: "unlessLteq", category: "comparison", compatibility: ["browser", "nodejs"], fn: unlessLteq } ]; // src/helpers/date.ts import { parseDate } from "chrono-node"; import dayjs from "dayjs"; var year = () => (/* @__PURE__ */ new Date()).getFullYear().toString(); var date = (humanReadableDate, formatString) => { const parsedDate = parseDate(humanReadableDate); if (!parsedDate) { throw new Error(`Unable to parse date: ${humanReadableDate}`); } return dayjs(parsedDate).format(formatString); }; var helpers5 = [ { name: "year", category: "date", compatibility: ["browser", "nodejs"], fn: year }, { name: "date", category: "date", compatibility: ["browser", "nodejs"], fn: date }, { /* Adding this in for legacy support */ name: "moment", category: "date", compatibility: ["browser", "nodejs"], fn: date } ]; // src/helpers/fs.ts import fs2 from "fs"; import path2 from "path"; import isGlob from "is-glob"; import micromatch from "micromatch"; var fileSize = (value, precision) => { if (value == null) return "0 B"; const number = typeof value === "number" ? value : value.length ?? 0; if (!number) return "0 B"; const precisionNumber = typeof precision === "number" ? precision : 2; const abbr = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; const factor = 10 ** precisionNumber; let i = abbr.length - 1; do { const size = 10 ** (i * 3); if (size <= number + 1) { const num = Math.round(number * factor / size) / factor; return `${num} ${abbr[i]}`; } } while (--i >= 0); return `${number} B`; }; var read = (filepath) => { return fs2.readFileSync(filepath, "utf8"); }; var isOptions = (value) => { return Boolean( value && typeof value === "object" && "hash" in value ); }; var readdir = (dir, filter) => { const files = fs2.readdirSync(dir).map((fp) => path2.join(dir, fp)); const filterArg = isOptions(filter) ? void 0 : filter; if (typeof filterArg === "function") { return filterArg(files); } if (filterArg instanceof RegExp) { return files.filter((fp) => filterArg.test(fp)); } if (typeof filterArg === "string") { if (isGlob(filterArg)) { return micromatch(files, filterArg); } if (filterArg === "isFile" || filterArg === "isDirectory") { return files.filter((fp) => { const stat = fs2.statSync(fp); return filterArg === "isFile" ? stat.isFile() : stat.isDirectory(); }); } } return files; }; var helpers6 = [ { name: "fileSize", category: "fs", compatibility: ["nodejs"], fn: fileSize }, { name: "read", category: "fs", compatibility: ["nodejs"], fn: read }, { name: "readdir", category: "fs", compatibility: ["nodejs"], fn: readdir } ]; // src/helpers/html.ts import path3 from "path"; import tag2 from "html-tag"; import striptags from "striptags"; var parseAttributes = (hash = {}) => Object.keys(hash).map((key) => `${key}="${String(hash[key]).replace(/^['"]|["']$/g, "")}"`).join(" "); var attr = (options) => { const val = parseAttributes(options?.hash ?? {}); return val.trim() ? ` ${val}` : ""; }; var css = function(list, options) { if (arguments.length < 2) { options = list; list = []; } let styles = arrayify(list ?? []); let assets = ""; if (this && this.options) { assets = this.options.assets || ""; } if (options?.hash?.href) { styles = arrayify(options.hash.href); } return styles.map((item) => { const ext = path3.extname(item); let fp = item; if (!/(^\/\/)|(:\/\/)/.test(item)) { fp = path3.posix.join(assets, item); } if (ext === ".less") { return `<link type="text/css" rel="stylesheet/less" href="${fp}">`; } return `<link type="text/css" rel="stylesheet" href="${fp}">`; }).join("\n"); }; var js = (context) => { if (context && typeof context === "object" && !Array.isArray(context)) { const attrStr = parseAttributes(context.hash ?? {}); return `<script${attrStr ? ` ${attrStr}` : ""}></script>`; } if (typeof context === "string") { return `<script src="${context}"></script>`; } const items = arrayify(context); return items.map( (fp) => path3.extname(fp) === ".coffee" ? tag2("script", { type: "text/coffeescript", src: fp }) : tag2("script", { src: fp }) ).join("\n"); }; var sanitize = (str) => { if (typeof str !== "string") return ""; return striptags(str).trim(); }; var ul = (context, options) => { const attrs = parseAttributes(options.hash ?? {}); const items = context.map( (item) => typeof item === "string" ? item : options.fn(item) ); const open = attrs ? `<ul ${attrs}>` : "<ul>"; return `${open}${items.map((i) => `<li>${i}</li>`).join("\n")}</ul>`; }; var ol = (context, options) => { const attrs = parseAttributes(options.hash ?? {}); const items = context.map( (item) => typeof item === "string" ? item : options.fn(item) ); const open = attrs ? `<ol ${attrs}>` : "<ol>"; return `${open}${items.map((i) => `<li>${i}</li>`).join("\n")}</ol>`; }; var thumbnailImage = (context) => { let figure = ""; let image = ""; const link = context.full || false; const imageAttributes = { alt: context.alt, src: context.thumbnail, width: context.size.width, height: context.size.height }; const figureAttributes = { id: `image-${context.id}` }; const linkAttributes = { href: link, rel: "thumbnail" }; if (context.classes) { if (context.classes.image) { imageAttributes.class = context.classes.image.join(" "); } if (context.classes.figure) { figureAttributes.class = context.classes.figure.join(" "); } if (context.classes.link) { linkAttributes.class = context.classes.link.join(" "); } } figure += `<figure ${parseAttributes(figureAttributes)}> `; image += `<img ${parseAttributes(imageAttributes)}> `; if (link) { figure += `<a ${parseAttributes(linkAttributes)}> ${image}</a> `; } else { figure += image; } if (context.caption) { figure += `<figcaption>${context.caption}</figcaption> `; } figure += "</figure>"; return figure; }; var helpers7 = [ { name: "attr", category: "html", compatibility: ["browser", "nodejs"], fn: attr }, { name: "css", category: "html", compatibility: ["nodejs"], fn: css }, { name: "js", category: "html", compatibility: ["nodejs"], fn: js }, { name: "sanitize", category: "html", compatibility: ["browser", "nodejs"], fn: sanitize }, { name: "ul", category: "html", compatibility: ["browser", "nodejs"], fn: ul }, { name: "ol", category: "html", compatibility: ["browser", "nodejs"], fn: ol }, { name: "thumbnailImage", category: "html", compatibility: ["browser", "nodejs"], fn: thumbnailImage } ]; // src/helpers/i18n.ts import get from "get-value"; var i18n = function(prop, locals, options) { if (locals && typeof locals === "object" && "hash" in locals && options === void 0) { options = locals; locals = {}; } if (typeof prop !== "string") { throw new Error('{{i18n}} helper expected "key" to be a string'); } const context = { ...this, ...locals, ...options?.hash ?? {} }; const lang = context.language || context.lang; if (typeof lang !== "string") { throw new TypeError('{{i18n}} helper expected "language" to be a string'); } const cache = context[lang]; if (typeof cache === "undefined") { throw new Error(`{{i18n}} helper cannot find language "${lang}"`); } const result = get(cache, prop); if (typeof result === "undefined") { throw new Error( '{{i18n}} helper cannot find property "' + String(prop) + '" for language "' + lang + '"' ); } return String(result); }; var helpers8 = [ { name: "i18n", category: "i18n", compatibility: ["browser", "nodejs"], fn: i18n } ]; // src/helpers/inflection.ts var inflect = (count, singular, plural, includeCount) => { const word = count > 1 || count === 0 ? plural : singular; return includeCount ? `${count} ${word}` : word; }; var ordinalize = (value) => { const num = Math.abs(Math.round(Number(value))); const str = String(value); const res = num % 100; if ([11, 12, 13].includes(res)) { return `${str}th`; } switch (num % 10) { case 1: return `${str}st`; case 2: return `${str}nd`; case 3: return `${str}rd`; default: return `${str}th`; } }; var helpers9 = [ { name: "inflect", category: "inflection", compatibility: ["browser", "nodejs"], fn: inflect }, { name: "ordinalize", category: "inflection", compatibility: ["browser", "nodejs"], fn: ordinalize } ]; // src/helpers/logging.ts import loggingHelpers from "logging-helpers"; var { log, ok, success, info, warning, warn, error, danger, bold, _debug, _inspect } = loggingHelpers; var helpers10 = [ { name: "log", category: "logging", compatibility: ["nodejs"], fn: log }, { name: "ok", category: "logging", compatibility: ["nodejs"], fn: ok }, { name: "success", category: "logging", compatibility: ["nodejs"], fn: success }, { name: "info", category: "logging", compatibility: ["nodejs"], fn: info }, { name: "warning", category: "logging", compatibility: ["nodejs"], fn: warning }, { name: "warn", category: "logging", compatibility: ["nodejs"], fn: warn }, { name: "error", category: "logging", compatibility: ["nodejs"], fn: error }, { name: "danger", category: "logging", compatibility: ["nodejs"], fn: danger }, { name: "bold", category: "logging", compatibility: ["nodejs"], fn: bold }, { name: "_debug", category: "logging", compatibility: ["nodejs"], fn: _debug }, { name: "_inspect", category: "logging", compatibility: ["nodejs"], fn: _inspect } ]; // src/helpers/match.ts import micromatch2 from "micromatch"; var match = (files, patterns, options) => { const pats = typeof patterns === "string" ? patterns.split(/, */) : patterns; return micromatch2(files, pats, options); }; var isMatch = (filepath, pattern, options) => { return micromatch2.isMatch(filepath, pattern, options); }; var mm = (...args) => { console.log("the {{mm}} helper is depcrecated and will be removed"); console.log("in handlebars-helpers v1.0.0, please use the {{match}}"); console.log("helper instead."); return match(...args); }; var helpers11 = [ { name: "match", category: "match", compatibility: ["browser", "nodejs"], fn: match }, { name: "isMatch", category: "match", compatibility: ["browser", "nodejs"], fn: isMatch }, { name: "mm", category: "match", compatibility: ["browser", "nodejs"], fn: mm } ]; // src/helpers/math.ts var isNumeric = (value) => typeof value === "number" && !Number.isNaN(value) || typeof value === "string" && value.trim() !== "" && !Number.isNaN(Number(value)); var toNumber = (value, message = "expected a number") => { if (isNumeric(value)) { return Number(value); } throw new TypeError(message); }; var abs = (value) => { const num = toNumber(value); return Math.abs(num); }; var add = (a, b) => { if (isNumeric(a) && isNumeric(b)) { return Number(a) + Number(b); } return ""; }; var avg = (...values) => { const flat = values.flat().map((n) => Number(n)); return sum(flat) / flat.length; }; var ceil = (value) => { const num = toNumber(value); return Math.ceil(num); }; var divide = (a, b) => { const numA = toNumber(a, "expected the first argument to be a number"); const numB = toNumber(b, "expected the second argument to be a number"); return numA / numB; }; var floor = (value) => { const num = toNumber(value); return Math.floor(num); }; var minus = (a, b) => { const numA = toNumber(a, "expected the first argument to be a number"); const numB = toNumber(b, "expected the second argument to be a number"); return numA - numB; }; var modulo = (a, b) => { const numA = toNumber(a, "expected the first argument to be a number"); const numB = toNumber(b, "expected the second argument to be a number"); return numA % numB; }; var multiply = (a, b) => { const numA = toNumber(a, "expected the first argument to be a number"); const numB = toNumber(b, "expected the second argument to be a number"); return numA * numB; }; var plus = (a, b) => { const numA = toNumber(a, "expected the first argument to be a number"); const numB = toNumber(b, "expected the second argument to be a number"); return numA + numB; }; var random = (min, max) => { const minNum = toNumber(min, "expected minimum to be a number"); const maxNum = toNumber(max, "expected maximum to be a number"); return minNum + Math.floor(Math.random() * (maxNum - minNum + 1)); }; var remainder = (a, b) => a % b; var round = (value) => { const num = toNumber(value); return Math.round(num); }; var subtract = (a, b) => { const numA = toNumber(a, "expected the first argument to be a number"); const numB = toNumber(b, "expected the second argument to be a number"); return numA - numB; }; var sum = (...values) => { const flat = values.flat(); let total = 0; for (const value of flat) { if (isNumeric(value)) { total += Number(value); } } return total; }; var times = (a, b) => multiply(a, b); var helpers12 = [ { name: "abs", category: "math", compatibility: ["browser", "nodejs"], fn: abs }, { name: "add", category: "math", compatibility: ["browser", "nodejs"], fn: add }, { name: "avg", category: "math", compatibility: ["browser", "nodejs"], fn: avg }, { name: "ceil", category: "math", compatibility: ["browser", "nodejs"], fn: ceil }, { name: "divide", category: "math", compatibility: ["browser", "nodejs"], fn: divide }, { name: "floor", category: "math", compatibility: ["browser", "nodejs"], fn: floor }, { name: "minus", category: "math", compatibility: ["browser", "nodejs"], fn: minus }, { name: "modulo", category: "math", compatibility: ["browser", "nodejs"], fn: modulo }, { name: "multiply", category: "math", compatibility: ["browser", "nodejs"], fn: multiply }, { name: "plus", category: "math", compatibility: ["browser", "nodejs"], fn: plus }, { name: "random", category: "math", compatibility: ["browser", "nodejs"], fn: random }, { name: "remainder", category: "math", compatibility: ["browser", "nodejs"], fn: remainder }, { name: "round", category: "math", compatibility: ["browser", "nodejs"], fn: round }, { name: "subtract", category: "math", compatibility: ["browser", "nodejs"], fn: subtract }, { name: "sum", category: "math", compatibility: ["browser", "nodejs"], fn: sum }, { name: "times", category: "math", compatibility: ["browser", "nodejs"], fn: times } ]; // src/helpers/md.ts import fs3 from "fs"; import path4 from "path"; import process2 from "process"; import ent from "ent"; import Handlebars from "handlebars"; import { Remarkable } from "remarkable"; var renderMarkdown = (input, options = {}) => { const options_ = { cwd: process2.cwd(), ...options }; const md = new Remarkable({ breaks: true, html: true, langPrefix: "lang-", typographer: false, xhtmlOut: false }); const filepath = path4.resolve(options_.cwd, input); let string_ = input; if (fs3.existsSync(filepath)) { string_ = fs3.readFileSync(filepath, "utf8"); } return new Handlebars.SafeString(ent.decode(md.render(string_))); }; var helpers13 = [ { name: "md", category: "markdown", compatibility: ["nodejs"], fn: renderMarkdown } ]; // src/helpers/misc.ts import get2 from "get-value"; import createFrame from "handlebars-helper-create-frame"; import util from "handlebars-utils"; import kindOf from "kind-of"; var frame = createFrame; function option(prop, locals, options) { return get2(util.options(this, locals, options), prop); } function noop(options) { return options.fn(this); } var typeOf = kindOf; function withHash(options) { if (options.hash && Object.keys(options.hash).length) { return options.fn(options.hash); } return options.inverse(this); } var helpers14 = [ { name: "frame", category: "misc", compatibility: ["browser", "nodejs"], fn: frame }, { name: "option", category: "misc", compatibility: ["browser", "nodejs"], fn: option }, { name: "noop", category: "misc", compatibility: ["browser", "nodejs"], fn: noop }, { name: "typeOf", category: "misc", compatibility: ["browser", "nodejs"], fn: typeOf }, { name: "withHash", category: "misc", compatibility: ["browser", "nodejs"], fn: withHash } ]; // src/helpers/number.ts var isNumeric2 = (value) => typeof value === "number" && !Number.isNaN(value); var bytes = (value, precision) => { if (value == null) return "0 B"; let num; if (typeof value === "string") { num = value.length; if (!num) return "0 B"; } else if (isNumeric2(value)) { num = Number(value); } else if (typeof value.length === "number") { num = value.length; if (!num) return "0 B"; } else { return "0 B"; } const prec = typeof precision === "number" && !Number.isNaN(precision) ? precision : 2; const abbr = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; const factor = 10 ** prec; for (let i = abbr.length - 1; i >= 0; i--) { const size = 10 ** (i * 3); if (size <= num + 1) { const val = Math.round(num * factor / size) / factor; return `${val} ${abbr[i]}`; } } return `${num}`; }; var addCommas = (num) => num.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"); var phoneNumber = (num) => { const str = num.toString(); return `(${str.substr(0, 3)}) ${str.substr(3, 3)}-${str.substr(6, 4)}`; }; var toAbbr = (value, precision) => { const num = isNumeric2(value) ? Number(value) : 0; const prec = typeof precision === "number" && !Number.isNaN(precision) ? precision : 2; const factor = 10 ** prec; const abbr = ["k", "m", "b", "t", "q"]; for (let i = abbr.length - 1; i >= 0; i--) { const size = 10 ** ((i + 1) * 3); if (size <= num + 1) { const val = Math.round(num * factor / size) / factor; return `${val}${abbr[i]}`; } } return `${num}`; }; var toExponential = (value, digits) => { const num = isNumeric2(value) ? Number(value) : 0; const d = typeof digits === "number" && !Number.isNaN(digits) ? digits : 0; return Number(num).toExponential(d); }; var toFixed = (value, digits) => { const num = isNumeric2(value) ? Number(value) : 0; const d = typeof digits === "number" && !Number.isNaN(digits) ? digits : 0; return Number(num).toFixed(d); }; var toFloat = (value) => parseFloat(String(value)); var toInt = (value) => parseInt(String(value), 10); var toPrecision = (value, precision) => { const num = isNumeric2(value) ? Number(value) : 0; const prec = typeof precision === "number" && !Number.isNaN(precision) ? precision : 1; return Number(num).toPrecision(prec); }; var helpers15 = [ { name: "bytes", category: "number", compatibility: ["browser", "nodejs"], fn: bytes }, { name: "addCommas", category: "number", compatibility: ["browser", "nodejs"], fn: addCommas }, { name: "phoneNumber", category: "number", compatibility: ["browser", "nodejs"], fn: phoneNumber }, { name: "toAbbr", category: "number", compatibility: ["browser", "nodejs"], fn: toAbbr }, { name: "toExponential", category: "number", compatibility: ["browser", "nodejs"], fn: toExponential }, { name: "toFixed", category: "number", compatibility: ["browser", "nodejs"], fn: toFixed }, { name: "toFloat", category: "number", compatibility: ["browser", "nodejs"], fn: toFloat }, { name: "toInt", category: "number", compatibility: ["browser", "nodejs"], fn: toInt }, { name: "toPrecision", category: "number", compatibility: ["browser", "nodejs"], fn: toPrecision } ]; // src/helpers/object.ts import getObject from "get-object"; import get3 from "get-value"; var isOptions2 = (value) => { return Boolean(value && typeof value === "object" && "hash" in value); }; var createFrame2 = (options, hash) => ({ ...options?.data || {}, ...hash }); var extend = (...objects) => { const args = [...objects]; if (args.length && isOptions2(args[args.length - 1])) { const opts = args.pop(); args.push(opts.hash); } const result = {}; for (const obj of args) { if (obj && typeof obj === "object" && !Array.isArray(obj)) { Object.assign(result, obj); } } return result; }; var forIn = (obj, options) => { if (!isOptions2(options)) { return options?.inverse?.(obj) ?? ""; } const data = createFrame2(options, options.hash || {}); let result = ""; for (const key in obj) { data.key = key; result += options.fn(obj[key], { data }); } return result; }; var forOwn = (obj, options) => { if (!isOptions2(options)) { return options?.inverse?.(obj) ?? ""; } const data = createFrame2(options, options.hash || {}); let result = ""; for (const key in obj) { if (Object.hasOwn(obj, key)) { data.key = key; result += options.fn(obj[key], { data }); } } return result; }; var toPath = (...prop) => { const parts = []; for (const p of prop) { if (typeof p === "string" || typeof p === "number") { parts.push(p); } } return parts.join("."); }; var getHelper = (prop, context, options) => { const val = get3(context, prop); if (options && typeof options.fn === "function") { return val ? options.fn(val) : options.inverse?.(context); } return val; }; var getObjectHelper = (prop, context) => { return getObject(context, prop); }; var hasOwn = (context, key) => { return Object.hasOwn(context, key); }; var isObject = (value) => { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }; var JSONparse = (str) => { return JSON.parse(str); }; var JSONstringify = (obj, indent) => { const ind = typeof indent === "number" ? indent : 0; return JSON.stringify(obj, null, ind); }; var merge = (context, ...objects) => { const args = [context, ...objects]; if (args.length && isOptions2(args[args.length - 1])) { const opts = args.pop(); args.push(opts.hash); } return Object.assign(...args); }; var parseJSON = JSONparse; var stringify = JSONstringify; var pick = (props, context, options) => { const keys = arrayify(props); let result = {}; for (const key of keys) { result = Object.assign({}, result, getObject(context, key)); } if (options && typeof options.fn === "function") { if (Object.keys(result).length) { return options.fn(result); } return options.inverse?.(context); } return result; }; var helpers16 = [ { name: "extend", category: "object", compatibility: ["browser", "nodejs"], fn: extend }, { name: "forIn", category: "object", compatibility: ["browser", "nodejs"], fn: forIn }, { name: "forOwn", category: "object", compatibility: ["browser", "nodejs"], fn: forOwn }, { name: "toPath", category: "object", compatibility: ["browser", "nodejs"], fn: toPath }, { name: "get", category: "object", compatibility: ["browser", "nodejs"], fn: getHelper }, { name: "getObject", category: "object", compatibility: ["browser", "nodejs"], fn: getObjectHelper }, { name: "hasOwn", category: "object", compatibility: ["browser", "nodejs"], fn: hasOwn }, { name: "isObject", category: "object", compatibility: ["browser", "nodejs"], fn: isObject }, { name: "JSONparse", category: "object", compatibility: ["browser", "nodejs"], fn: JSONparse }, { name: "JSONstringify", category: "object", compatibility: ["browser", "nodejs"], fn: JSONstringify }, { name: "merge", category: "object", compatibility: ["browser", "nodejs"], fn: merge }, { name: "parseJSON", category: "object", compatibility: ["browser", "nodejs"], fn: parseJSON }, { name: "pick", category: "object", compatibility: ["browser", "nodejs"], fn: pick }, { name: "stringify", category: "object", compatibility: ["browser", "nodejs"], fn: stringify } ]; // src/helpers/path.ts import path5 from "path"; var expectedType = (variableName, expectedType2, variableValue) => { const actualType = Object.prototype.toString.call(variableValue).slice(8, -1).toLowerCase(); return `Expected ${variableName} to be of type ${expectedType2}, but got ${actualType}`; }; var assertString = (name, value) => { if (typeof value !== "string") { throw new TypeError(expectedType(name, "string", value)); } return value; }; var absolute = function(filepath, options) { const fp = assertString("filepath", filepath); const cwd = options?.data?.root?.cwd ?? this?.cwd ?? process.cwd(); return path5.resolve(cwd, fp); }; var dirname = (filepath) => { const fp = assertString("filepath", filepath); return path5.dirname(fp); }; var relative = (a, b) => { const from = assertString("first path", a); const to = assertString("second path", b); return path5.relative(path5.dirname(from), to); }; var basename = (filepath) => { const fp = assertString("filepath", filepath); return path5.basename(fp); }; var stem = (filepath) => { const fp = assertString("filepath", filepath); return path5.basename(fp, path5.extname(fp)); }; var extname = (filepath) => { const fp = assertString("filepath", filepath); return path5.extname(fp); }; var resolveFn = function(filepath, ...paths) { let options; const last2 = paths[paths.length - 1]; if (typeof last2 === "object" && last2 && "data" in last2) { options = paths.pop(); } const cwd = options?.data?.root?.cwd ?? this?.cwd ?? process.cwd(); const all2 = [ cwd, assertString("filepath", filepath), ...paths.map((p, i) => assertString(`path${i}`, p)) ]; return path5.resolve(...all2); }; var segments = (filepath, a, b) => { const fp = assertString("filepath", filepath); const start = Number(a); const end = Number(b); const segmentsArr = fp.split(/[\\/]+/); return segmentsArr.slice(start, end).join("/"); }; var helpers17 = [ { name: "absolute", category: "path", compatibility: ["nodejs"], fn: absolute }, { name: "dirname", category: "path", compatibility: ["nodejs"], fn: dirname }, { name: "relative", category: "path", compatibility: ["nodejs"], fn: relative }, { name: "basename", category: "path", compatibility: ["nodejs"], fn: basename }, { name: "stem", category: "path", compatibility: ["nodejs"], fn: stem }, { name: "extname", category: "path", compatibility: ["nodejs"], fn: extname }, { name: "resolve", category: "path", compatibility: ["nodejs"], fn: resolveFn }, { name: "segments", category: "path", compatibility: ["nodejs"], fn: segments } ]; // src/helpers/regex.ts import util2 from "handlebars-utils"; var toRegex = (str, locals, options) => { const opts = util2.options({}, locals, options); return new RegExp(str, opts.flags); }; var test = (str, regex) => { if (typeof str !== "string") { return false; } if (!(regex instanceof RegExp)) { throw new TypeError("expected a regular expression"); } return regex.test(str); }; var helpers18 = [ { name: "toRegex", category: "regex", compatibility: ["browser", "nodejs"], fn: toRegex }, { name: "test", category: "regex", compatibility: ["browser", "nodejs"], fn: test } ]; // src/helpers/string.ts var chop = (str) => str.trim().replace(/^[-_.\W\s]+|[-_.\W\s]+$/g, ""); var changecase = (str, fn) => { if (str.length === 1) return str.toLowerCase(); const res = chop(str).toLowerCase(); return res.replace(/[-_.\W\s]+(\w|$)/g, (_, ch) => fn(ch)); }; var append = (str, suffix) => { if (typeof str === "string" && typeof suffix === "string") { return str + suffix; } return str; }; var camelcase = (str) => typeof str === "string" ? changecase(str, (ch) => ch.toUpperCase()) : ""; var capitalize = (str) => { if (typeof str !== "string" || str.length === 0) return ""; return str.charAt(0).toUpperCase() + str.slice(1); }; var capitalizeAll = (str) => { if (typeof str !== "string") return ""; return str.replace(/\w\S*/g, (word) => capitalize(word)); }; var center = (str, spaces) => { if (typeof str !== "string") return ""; let space = ""; for (let i = 0; i < spaces; i++) { space += "&nbsp;"; } return space + str + space; }; var chopStr = (str) => typeof str === "string" ? chop(str) : ""; var dashcase = (str) => typeof str === "string" ? changecase(str, (ch) => `-${ch}`) : ""; var dotcase = (str) => typeof str === "string" ? changecase(str, (ch) => `.${ch}`) : ""; var truncate = (str, limit, suffix) => { if (typeof str === "string") { if (typeof suffix !== "string") { suffix = ""; } if (str.length > limit) { return str.slice(0, limit - suffix.length) + suffix; } return str; } return ""; }; var ellipsis = (str, limit) => { if (typeof str === "string") { if (str.length <= limit) { return str; } return `${truncate(str, limit)}\u2026`; } return ""; }; var hyphenate = (str) => typeof str === "string" ? str.split(" ").join("-") : ""; var isString = (value) => typeof value === "string"; var lowercase = (str) => { if (typeof str === "object" && str !== null && "fn" in str && typeof str.fn === "function") { return String(str.fn()).toLowerCase(); } if (typeof str !== "string") return ""; return str.toLowerCase(); }; var downcase = (...args) => lowercase(...args); var occurrences = (str, substring) => { if (typeof str !== "string") return ""; const len = substring.length; let pos = str.indexOf(substring, 0); let n = 0; while (pos > -1) { n++; pos = str.indexOf(substring, pos + len); } return n; }; var pascalcase = (str) => { if (typeof str !== "string") return ""; const res = changecase(str, (ch) => ch.toUpperCase()); return res.charAt(0).toUpperCase() + res.slice(1); }; var pathcase = (str) => typeof str === "string" ? changecase(str, (ch) => `/${ch}`) : ""; var plusify = (str, ch) => { if (typeof str !== "string") return ""; if (typeof ch !== "string") ch = " "; return str.split(ch).join("+"); }; var prepend = (str, prefix) => { return typeof str === "string" && typeof prefix === "string" ? prefix + str : str; }; var remove = (str, ch) => { if (typeof str !== "string") return ""; if (typeof ch !== "string") return str; return str.split(ch).join(""); }; var removeFirst = (str, ch) => { if (typeof str !== "string") return ""; if (typeof ch !== "string") return str; return str.replace(ch, ""); }; var replace = (str, a, b) => { if (typeof str !== "string") return ""; if (typeof a !== "string") return str; if (typeof b !== "string") b = ""; return str.split(a).join(b); }; var replaceFirst = (str, a, b) => { if (typeof str !== "string") return ""; if (typeof a !== "string") return str; if (typeof b !== "string") b = ""; return str.replace(a, b); }; var reverse = (str) => typeof str === "string" ? str.split("").reverse().join("") : ""; var sentence = (str) => { if (typeof str !== "string") return ""; return str.replace( /((?:\S[^.?!]*)[.?!]*)/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase() ); }; var snakecase = (str) => typeof str === "string" ? changecase(str, (ch) => `_${ch}`) : ""; var trim = (str) => typeof str === "string" ? str.trim() : ""; var trimLeft = (str) => typeof str === "string" ? str.replace(/^\s+/, "") : ""; var trimRight = (str) => typeof str === "string" ? str.replace(/\s+$/, "") : ""; var truncateWords = (str, count, suffix) => { if (typeof str === "string" && typeof count === "number" && Number.isFinite(count)) { if (typeof suffix !== "string") { suffix = "..."; } const num = Math.trunc(count); let arr = str.split(" "); if (num < arr.length) { arr = arr.slice(0, num); } const val = arr.join(" ").trim(); return val + suffix; } return ""; }; var truncateWordsAlias = truncateWords; var uppercase = (str) => { if (typeof str === "object" && str !== null && "fn" in str && typeof str.fn === "function") { return String(str.fn()).toUpperCase(); } if (typeof str !== "string") return ""; return str.toUpperCase(); }; var upcase = (...args) => uppercase(...args); var helpers19 = [ { name: "append", category: "string", compatibility: ["browser", "nodejs"], fn: append }, { name: "camelcase", category: "string", compatibility: ["browser", "nodejs"], fn: camelcase }, { name: "capitalize", category: "string", compatibility: ["browser", "nodejs"], fn: capitalize }, { name: "capitalizeAll", category: "string", compatibility: ["browser", "nodejs"], fn: capitalizeAll }, { name: "center", category: "string", compatibility: ["browser", "nodejs"], fn: center }, { name: "chop", category: "string", compatibility: ["browser", "nodejs"], fn: chopStr }, { name: "dashcase", category: "string", compatibility: ["browser", "nodejs"], fn: dashcase }, { name: "dotcase", category: "string", compatibility: ["browser", "nodejs"], fn: dotcase }, { name: "ellipsis", category: "string", compatibility: ["browser", "nodejs"], fn: ellipsis }, { name: "hyphenate", category: "string", compatibility: ["browser", "nodejs"], fn: hyphenate }, { name: "isString", category: "string", compatibility: ["browser", "nodejs"], fn: isString }, { name: "lowercase", category: "string", compatibility: ["browser", "nodejs"], fn: lowercase }, { name: "downcase", category: "string", compatibility: ["browser", "nodejs"], fn: downcase }, { name: "occurrences", category: "string", compatibility: ["browser", "nodejs"], fn: occurrences }, { name: "pascalcase", category: "string", compatibility: ["browser", "nodejs"], fn: pascalcase }, { name: "pathcase", category: "string", compatibility: ["browser", "nodejs"], fn: pathcase }, { name: "plusify", category: "string", compatibility: ["browser", "nodejs"], fn: plusify }, { name: "prepend", category: "string", compatibility: ["browser", "nodejs"], fn: prepend }, { name: "remove", category: "string", compatibility: ["browser", "nodejs"], fn: remove }, { name: "removeFirst", category: "string", compatibility: ["browser", "nodejs"], fn: removeFirst }, { name: "replace", category: "string", compatibility: ["browser", "nodejs"], fn: replace }, { name: "replaceFirst", category: "string", compatibility: ["browser", "nodejs"], fn: replaceFirst }, { name: "reverse", category: "string", compatibility: ["browser", "nodejs"], fn: reverse }, { name: "sentence", category: "string", compatibility: ["browser", "nodejs"], fn: sentence }, { name: "snakecase", category: "string", compatibility: ["browser", "nodejs"], fn: snakecase }, { name: "trim", category: "string", compatibility: ["browser", "nodejs"], fn: trim }, { name: "trimLeft", category: "string", compatibility: ["browser", "nodejs"], fn: trimLeft }, { name: "trimRight", category: "string", compatibility: ["browser", "nodejs"], fn: trimRight }, { name: "truncate", category: "string", compatibility: ["browser", "nodejs"], fn: truncate }, { name: "truncateWords", category: "string", compatibility: ["browser", "nodejs"], fn: truncateWordsAlias }, { name: "uppercase", category: "string", compatibility: ["browser", "nodejs"], fn: uppercase }, { name: "upcase", category: "string", compatibility: ["browser", "nodejs"], fn: upcase } ]; // src/helpers/url.ts import { escape as qsEscape } from "querystring"; import { format, parse, resolve } from "url"; var all = ["browser", "nodejs"]; var encodeUri = (str) => { if (typeof str === "string") { return encodeURIComponent(str); } }; var escapeFn = (str) => { if (typeof str === "string") { return qsEscape(str); } }; var decodeUri = (str) => { if (typeof str === "string") { return decodeURIComponent(str); } }; function urlEncode(...args) { return encodeUri.apply(this, args); } function urlDecode(...args) { return decodeUri.apply(this, args); } var urlResolve = (base, href) => resolve(String(base), String(href)); var urlParse = (str) => parse(String(str)); var stripQuerystring = (str) => { if (typeof str === "string") { return str.split("?")[0]; } }; var stripProtocol = (str) => { if (typeof str === "string") { const parsed = parse(str); parsed.protocol = ""; return format(parsed); } }; var helpers20 = [ { name: "encodeURI", category: "url", compatibility: all, fn: encodeUri }, { name: "escape", category: "url", compatibility: ["nodejs"], fn: escapeFn }, { name: "decodeURI", category: "url", compatibility: all, fn: decodeUri }, { name: "url_encode", category: "url", compatibility: all, fn: urlEncode }, { name: "url_decode", category: "url", compatibility: all, fn: urlDecode }, { name: "urlResolve", category: "url", compatibility: ["nodejs"], fn: urlResolve }, { name: "urlParse", category: "url", compatibility: ["nodejs"], fn: urlParse }, { name: "stripQuerystring", category: "url", compatibility: all, fn: stripQuerystring }, { name: "stripProtocol", category: "url", compatibility: ["nodejs"], fn: stripProtocol } ]; // src/helper-registry.ts var HelperRegistry = class { _helpers = []; constructor() { this.init(); } /** * Get all registered helpers. * @returns {Helper[]} The array of registered helpers. */ get helpers() { return this._helpers; } /** * Initialize the helper registry. This is performed during the construction of the registry such as new HelperRegistry(). */ init() { this.registerHelpers(helpers); this.registerHelpers(helpers3); this.registerHelpers(helpers5); this.registerHelpers(helpers6); this.registerHelpers(helpers2); this.registerHelpers(helpers13); this.registerHelpers(helpers7); this.registerHelpers(helpers4); this.registerHelpers(helpers8); this.registerHelpers(helpers9); this.registerHelpers(helpers10); this.registerHelpers(helpers11); this.registerHelpers(helpers12); this.registerHelpers(helpers14); this.registerHelpers(helpers15); this.registerHelpers(helpers17); this.registerHelpers(helpers18); this.registerHelpers(helpers19); this.registerHelpers(helpers20); this.registerHelpers(hel