UNPKG

@jaredwray/fumanchu

Version:
2,014 lines (1,999 loc) 78.7 kB
// src/index.ts import { CacheableMemory } from "@cacheable/memory"; import 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 forEach = function(collection, options2) { if (!Array.isArray(collection) || !options2 || typeof options2.fn !== "function") { return options2?.inverse ? options2.inverse(this) ?? "" : ""; } if (collection.length === 0) { return options2.inverse ? options2.inverse(this) ?? "" : ""; } const total = collection.length; const hash = options2.hash ?? {}; const baseData = options2.data ? { ...options2.data } : void 0; let result = ""; for (let index = 0; index < total; index++) { const value = collection[index]; const meta = { index, total, isFirst: index === 0, isLast: index === total - 1 }; const data = { ...baseData ? { ...baseData } : {}, ...hash, ...meta }; let context; if (value != null && typeof value === "object") { context = { ...value, ...hash, ...meta }; } else { context = { value, ...hash, ...meta }; } result += options2.fn(context, { data }); } return result; }; var inArray = function(array, value, options2) { if (!Array.isArray(array) || !options2) { return ""; } const found = array.includes(value); if (found && options2.fn) { return options2.fn(this); } if (!found && options2.inverse) { return options2.inverse(this); } return ""; }; var isArray = (value) => { return Array.isArray(value); }; var itemAt = (array, idx) => { if (!Array.isArray(array)) return void 0; return array[idx]; }; var equalsLength = function(value, targetLength, options2) { const actualLength = length(value); const isEqual = actualLength === targetLength; if (options2 && (options2.fn || options2.inverse)) { if (isEqual && options2.fn) { return options2.fn(this); } if (!isEqual && options2.inverse) { return options2.inverse(this); } return ""; } return isEqual; }; var some = function(array, iter, options2) { if (!Array.isArray(array) || typeof iter !== "function" || !options2) { return options2?.inverse ? options2.inverse(this) ?? "" : ""; } const hasSome = array.some(iter); if (hasSome && options2.fn) { return options2.fn(this); } if (!hasSome && options2.inverse) { return options2.inverse(this); } return ""; }; var eachIndex = function(array, options2) { if (!Array.isArray(array) || !options2 || typeof options2.fn !== "function") { return options2?.inverse ? options2.inverse(this) ?? "" : ""; } if (array.length === 0) { return options2.inverse ? options2.inverse(this) ?? "" : ""; } let result = ""; for (let index = 0; index < array.length; index++) { const context = { item: array[index], index }; result += options2.fn(context); } return result; }; var withAfter = function(array, idx, options2) { if (!Array.isArray(array) || !options2 || typeof options2.fn !== "function") { return ""; } const sliced = array.slice(idx + 1); if (sliced.length === 0) { return options2.inverse ? options2.inverse(this) ?? "" : ""; } let result = ""; for (const item of sliced) { result += options2.fn(item); } return result; }; var withBefore = function(array, idx, options2) { if (!Array.isArray(array) || !options2 || typeof options2.fn !== "function") { return ""; } const sliced = array.slice(0, idx); if (sliced.length === 0) { return options2.inverse ? options2.inverse(this) ?? "" : ""; } let result = ""; for (const item of sliced) { result += options2.fn(item); } return result; }; var withFirst = function(array, n, options2) { let count = 1; let opts = options2; if (typeof n === "object" && n !== null && !Array.isArray(n)) { opts = n; } else if (typeof n === "number") { count = n; } if (!Array.isArray(array) || !opts || typeof opts.fn !== "function") { return ""; } if (array.length === 0) { return opts.inverse ? opts.inverse(this) ?? "" : ""; } const items = array.slice(0, count); let result = ""; for (const item of items) { result += opts.fn(item); } return result; }; var withLast = function(array, n, options2) { let count = 1; let opts = options2; if (typeof n === "object" && n !== null && !Array.isArray(n)) { opts = n; } else if (typeof n === "number") { count = n; } if (!Array.isArray(array) || !opts || typeof opts.fn !== "function") { return ""; } if (array.length === 0) { return opts.inverse ? opts.inverse(this) ?? "" : ""; } const items = array.slice(-count); let result = ""; for (const item of items) { result += opts.fn(item); } return result; }; var withGroup = function(array, size, options2) { if (!Array.isArray(array) || typeof size !== "number" || size <= 0 || !options2 || typeof options2.fn !== "function") { return ""; } if (array.length === 0) { return options2.inverse ? options2.inverse(this) ?? "" : ""; } let result = ""; const groups = []; for (let i = 0; i < array.length; i += size) { groups.push(array.slice(i, i + size)); } for (const group of groups) { result += options2.fn(group); } return result; }; var withSort = function(array, prop, options2) { let sortProp; let opts = options2; if (typeof prop === "object" && prop !== null && !Array.isArray(prop)) { opts = prop; } else if (typeof prop === "string") { sortProp = prop; } if (!Array.isArray(array) || !opts || typeof opts.fn !== "function") { return ""; } if (array.length === 0) { return opts.inverse ? opts.inverse(this) ?? "" : ""; } const sorted = [...array]; if (sortProp) { sorted.sort((a, b) => { const aVal = a?.[sortProp]; const bVal = b?.[sortProp]; if (aVal === bVal) return 0; if (aVal == null) return 1; if (bVal == null) return -1; if (typeof aVal === "string" && typeof bVal === "string") { return aVal.localeCompare(bVal); } return aVal < bVal ? -1 : 1; }); } else { sorted.sort((a, b) => { if (a === b) return 0; if (a == null) return 1; if (b == null) return -1; if (typeof a === "string" && typeof b === "string") { return a.localeCompare(b); } return a < b ? -1 : 1; }); } const shouldReverse = opts.hash?.reverse === true || opts.hash?.reverse === "true"; if (shouldReverse) { sorted.reverse(); } let result = ""; for (const item of sorted) { result += opts.fn(item); } return result; }; var filter = function(array, value, options2) { if (!Array.isArray(array) || !options2) { return options2?.inverse ? options2.inverse(this) ?? "" : ""; } const filtered = array.filter((item) => item === value); if (filtered.length > 0 && options2.fn) { return options2.fn(this); } if (filtered.length === 0 && options2.inverse) { return options2.inverse(this); } return ""; }; var map = (array, fn) => { if (!Array.isArray(array) || typeof fn !== "function") { return []; } return array.map(fn); }; var pluck = (collection, prop) => { if (!Array.isArray(collection) || typeof prop !== "string") { return []; } return collection.map((item) => { if (item == null || typeof item !== "object") { return void 0; } const parts = prop.split("."); let value = item; for (const part of parts) { if (value == null || typeof value !== "object") { return void 0; } value = value[part]; } return value; }); }; var reverse = (value) => { if (typeof value === "string") { return value.split("").reverse().join(""); } if (Array.isArray(value)) { return [...value].reverse(); } return void 0; }; var sort = (array, key) => { if (!Array.isArray(array)) { return []; } const sorted = [...array]; if (typeof key === "function") { sorted.sort(key); } else if (typeof key === "string") { sorted.sort((a, b) => { const aVal = a?.[key]; const bVal = b?.[key]; if (aVal === bVal) return 0; if (aVal == null) return 1; if (bVal == null) return -1; if (typeof aVal === "string" && typeof bVal === "string") { return aVal.localeCompare(bVal); } return aVal < bVal ? -1 : 1; }); } else { sorted.sort((a, b) => { if (a === b) return 0; if (a == null) return 1; if (b == null) return -1; if (typeof a === "string" && typeof b === "string") { return a.localeCompare(b); } return a < b ? -1 : 1; }); } return sorted; }; var sortBy = (array, ...props) => { if (!Array.isArray(array) || props.length === 0) { return []; } const sorted = [...array]; sorted.sort((a, b) => { for (const prop of props) { const aVal = a?.[prop]; const bVal = b?.[prop]; if (aVal === bVal) continue; if (aVal == null) return 1; if (bVal == null) return -1; if (typeof aVal === "string" && typeof bVal === "string") { const result = aVal.localeCompare(bVal); if (result !== 0) return result; } else { return aVal < bVal ? -1 : 1; } } return 0; }); return sorted; }; var unique = function(array, options2) { if (!Array.isArray(array)) { if (!options2) { return []; } return options2.inverse ? options2.inverse(this) ?? "" : ""; } const uniqueArray = [...new Set(array)]; if (!options2) { return uniqueArray; } if (uniqueArray.length === 0 && options2.inverse) { return options2.inverse(this); } if (uniqueArray.length > 0 && options2.fn) { let result = ""; for (const item of uniqueArray) { result += options2.fn(item); } return result; } return ""; }; 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 }, { name: "forEach", category: "array", compatibility: ["browser", "nodejs"], fn: forEach }, { name: "inArray", category: "array", compatibility: ["browser", "nodejs"], fn: inArray }, { name: "isArray", category: "array", compatibility: ["browser", "nodejs"], fn: isArray }, { name: "itemAt", category: "array", compatibility: ["browser", "nodejs"], fn: itemAt }, { name: "equalsLength", category: "array", compatibility: ["browser", "nodejs"], fn: equalsLength }, { name: "some", category: "array", compatibility: ["browser", "nodejs"], fn: some }, { name: "eachIndex", category: "array", compatibility: ["browser", "nodejs"], fn: eachIndex }, { name: "withAfter", category: "array", compatibility: ["browser", "nodejs"], fn: withAfter }, { name: "withBefore", category: "array", compatibility: ["browser", "nodejs"], fn: withBefore }, { name: "withFirst", category: "array", compatibility: ["browser", "nodejs"], fn: withFirst }, { name: "withLast", category: "array", compatibility: ["browser", "nodejs"], fn: withLast }, { name: "withGroup", category: "array", compatibility: ["browser", "nodejs"], fn: withGroup }, { name: "withSort", category: "array", compatibility: ["browser", "nodejs"], fn: withSort }, { name: "filter", category: "array", compatibility: ["browser", "nodejs"], fn: filter }, { name: "map", category: "array", compatibility: ["browser", "nodejs"], fn: map }, { name: "pluck", category: "array", compatibility: ["browser", "nodejs"], fn: pluck }, { name: "reverse", category: "array", compatibility: ["browser", "nodejs"], fn: reverse }, { name: "sort", category: "array", compatibility: ["browser", "nodejs"], fn: sort }, { name: "sortBy", category: "array", compatibility: ["browser", "nodejs"], fn: sortBy }, { name: "unique", category: "array", compatibility: ["browser", "nodejs"], fn: unique } ]; // 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 = (options2) => { if (!options2 || !options2.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" } = options2; 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; case "===": return a === b; case "!=": return a != b; 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"; import isBetween from "dayjs/plugin/isBetween.js"; import relativeTime from "dayjs/plugin/relativeTime.js"; import timezone from "dayjs/plugin/timezone.js"; import utc from "dayjs/plugin/utc.js"; dayjs.extend(relativeTime); dayjs.extend(isBetween); dayjs.extend(utc); dayjs.extend(timezone); var parseDateInput = (input) => { if (!input) { return dayjs(); } if (typeof input === "string") { const parsed = parseDate(input); if (parsed) { return dayjs(parsed); } return dayjs(input); } return dayjs(input); }; var year = () => (/* @__PURE__ */ new Date()).getFullYear().toString(); var convertFormatString = (format2) => { return format2.replace(/yyyy/g, "YYYY").replace(/yy/g, "YY").replace(/dd/g, "DD").replace(/mm/g, "MM").replace(/hh/g, "HH"); }; var date = (humanReadableDate, formatString) => { const defaultFormat = "YYYY-MM-DD"; const format2 = formatString || defaultFormat; const parsedDate = parseDateInput(humanReadableDate); if (!parsedDate.isValid()) { throw new Error(`Unable to parse date: ${humanReadableDate}`); } const convertedFormat = convertFormatString(format2); return parsedDate.format(convertedFormat); }; var timestamp = () => { return Date.now().toString(); }; var now = (formatString) => { const defaultFormat = "YYYY-MM-DD HH:mm:ss"; const format2 = formatString || defaultFormat; const convertedFormat = convertFormatString(format2); return dayjs().format(convertedFormat); }; var fromNow = (dateInput) => { const parsed = parseDateInput(dateInput); if (!parsed.isValid()) { throw new Error(`Unable to parse date: ${dateInput}`); } return parsed.fromNow(); }; var toNow = (dateInput) => { const parsed = parseDateInput(dateInput); if (!parsed.isValid()) { throw new Error(`Unable to parse date: ${dateInput}`); } return parsed.toNow(); }; var ago = (dateInput) => { return fromNow(dateInput); }; var dateAdd = (dateInput, amount, unit) => { const parsed = parseDateInput(dateInput); if (!parsed.isValid()) { throw new Error(`Unable to parse date: ${dateInput}`); } return parsed.add(amount, unit).format("YYYY-MM-DD HH:mm:ss"); }; var dateSubtract = (dateInput, amount, unit) => { const parsed = parseDateInput(dateInput); if (!parsed.isValid()) { throw new Error(`Unable to parse date: ${dateInput}`); } return parsed.subtract(amount, unit).format("YYYY-MM-DD HH:mm:ss"); }; var startOf = (dateInput, unit) => { const parsed = parseDateInput(dateInput); if (!parsed.isValid()) { throw new Error(`Unable to parse date: ${dateInput}`); } return parsed.startOf(unit).format("YYYY-MM-DD HH:mm:ss"); }; var endOf = (dateInput, unit) => { const parsed = parseDateInput(dateInput); if (!parsed.isValid()) { throw new Error(`Unable to parse date: ${dateInput}`); } return parsed.endOf(unit).format("YYYY-MM-DD HH:mm:ss"); }; var isBefore = (date1, date2) => { const parsed1 = parseDateInput(date1); const parsed2 = parseDateInput(date2); if (!parsed1.isValid() || !parsed2.isValid()) { throw new Error(`Unable to parse dates: ${date1}, ${date2}`); } return parsed1.isBefore(parsed2); }; var isAfter = (date1, date2) => { const parsed1 = parseDateInput(date1); const parsed2 = parseDateInput(date2); if (!parsed1.isValid() || !parsed2.isValid()) { throw new Error(`Unable to parse dates: ${date1}, ${date2}`); } return parsed1.isAfter(parsed2); }; var isSame = (date1, date2, unit) => { const parsed1 = parseDateInput(date1); const parsed2 = parseDateInput(date2); if (!parsed1.isValid() || !parsed2.isValid()) { throw new Error(`Unable to parse dates: ${date1}, ${date2}`); } return parsed1.isSame(parsed2, unit); }; var isBetweenDates = (dateInput, startDate, endDate) => { const parsed = parseDateInput(dateInput); const parsedStart = parseDateInput(startDate); const parsedEnd = parseDateInput(endDate); if (!parsed.isValid() || !parsedStart.isValid() || !parsedEnd.isValid()) { throw new Error( `Unable to parse dates: ${dateInput}, ${startDate}, ${endDate}` ); } return parsed.isBetween(parsedStart, parsedEnd, null, "[]"); }; var diff = (date1, date2, unit) => { const parsed1 = parseDateInput(date1); const parsed2 = parseDateInput(date2); if (!parsed1.isValid() || !parsed2.isValid()) { throw new Error(`Unable to parse dates: ${date1}, ${date2}`); } return parsed1.diff(parsed2, unit); }; var toISOString = (dateInput) => { const parsed = parseDateInput(dateInput); if (!parsed.isValid()) { throw new Error(`Unable to parse date: ${dateInput}`); } return parsed.toISOString(); }; var dateTimezone = (dateInput, timezoneStr, formatString) => { const defaultFormat = "YYYY-MM-DD HH:mm:ss"; const format2 = formatString || defaultFormat; const convertedFormat = convertFormatString(format2); const parsed = parseDateInput(dateInput); if (!parsed.isValid()) { throw new Error(`Unable to parse date: ${dateInput}`); } return parsed.tz(timezoneStr).format(convertedFormat); }; var dateLocale = (dateInput, locale, formatString) => { const defaultFormat = "YYYY-MM-DD HH:mm:ss"; const format2 = formatString || defaultFormat; const convertedFormat = convertFormatString(format2); const parsed = parseDateInput(dateInput); if (!parsed.isValid()) { throw new Error(`Unable to parse date: ${dateInput}`); } return parsed.locale(locale).format(convertedFormat); }; 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 }, { name: "timestamp", category: "date", compatibility: ["browser", "nodejs"], fn: timestamp }, { name: "now", category: "date", compatibility: ["browser", "nodejs"], fn: now }, { name: "fromNow", category: "date", compatibility: ["browser", "nodejs"], fn: fromNow }, { name: "toNow", category: "date", compatibility: ["browser", "nodejs"], fn: toNow }, { name: "ago", category: "date", compatibility: ["browser", "nodejs"], fn: ago }, { name: "dateAdd", category: "date", compatibility: ["browser", "nodejs"], fn: dateAdd }, { name: "dateSubtract", category: "date", compatibility: ["browser", "nodejs"], fn: dateSubtract }, { name: "startOf", category: "date", compatibility: ["browser", "nodejs"], fn: startOf }, { name: "endOf", category: "date", compatibility: ["browser", "nodejs"], fn: endOf }, { name: "isBefore", category: "date", compatibility: ["browser", "nodejs"], fn: isBefore }, { name: "isAfter", category: "date", compatibility: ["browser", "nodejs"], fn: isAfter }, { name: "isSame", category: "date", compatibility: ["browser", "nodejs"], fn: isSame }, { name: "isBetween", category: "date", compatibility: ["browser", "nodejs"], fn: isBetweenDates }, { name: "diff", category: "date", compatibility: ["browser", "nodejs"], fn: diff }, { name: "toISOString", category: "date", compatibility: ["browser", "nodejs"], fn: toISOString }, { name: "dateTimezone", category: "date", compatibility: ["browser", "nodejs"], fn: dateTimezone }, { name: "dateLocale", category: "date", compatibility: ["browser", "nodejs"], fn: dateLocale } ]; // 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, filter2) => { const files = fs2.readdirSync(dir).map((fp) => path2.join(dir, fp)); const filterArg = isOptions(filter2) ? void 0 : filter2; 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 = (options2) => { const val = parseAttributes(options2?.hash ?? {}); return val.trim() ? ` ${val}` : ""; }; var css = function(list, options2) { if (arguments.length < 2) { options2 = list; list = []; } let styles = arrayify(list ?? []); let assets = ""; if (this && this.options) { assets = this.options.assets || ""; } if (options2?.hash?.href) { styles = arrayify(options2.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, options2) => { const attrs = parseAttributes(options2.hash ?? {}); const items = context.map( (item) => typeof item === "string" ? item : options2.fn(item) ); const open = attrs ? `<ul ${attrs}>` : "<ul>"; return `${open}${items.map((i) => `<li>${i}</li>`).join("\n")}</ul>`; }; var ol = (context, options2) => { const attrs = parseAttributes(options2.hash ?? {}); const items = context.map( (item) => typeof item === "string" ? item : options2.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/utils.ts var get = (object, path6) => { if (!object || typeof object !== "object") return void 0; const keys = Array.isArray(path6) ? path6 : path6.split("."); let result = object; for (const key of keys) { if (result == null || typeof result !== "object") return void 0; result = result[key]; } return result; }; var getObject = (object, path6) => { if (!object || typeof object !== "object") return void 0; const keys = Array.isArray(path6) ? path6 : path6.split("."); if (keys.length === 0) return void 0; let current = object; for (let i = 0; i < keys.length - 1; i++) { if (current == null || typeof current !== "object") return void 0; current = current[keys[i]]; } const finalKey = keys[keys.length - 1]; if (current == null || typeof current !== "object" || !(finalKey in current)) { return void 0; } return { [finalKey]: current[finalKey] }; }; var options = (thisArg, locals, opts) => { const result = {}; if (thisArg && typeof thisArg === "object" && thisArg.options) { Object.assign(result, thisArg.options); } if (locals && typeof locals === "object") { if ("hash" in locals) { Object.assign(result, locals.hash); } else { Object.assign(result, locals); } } if (opts && typeof opts === "object") { if ("hash" in opts) { Object.assign(result, opts.hash); } else { Object.assign(result, opts); } } return result; }; // src/helpers/i18n.ts var i18n = function(prop, locals, options2) { if (locals && typeof locals === "object" && "hash" in locals && options2 === void 0) { options2 = locals; locals = {}; } if (typeof prop !== "string") { throw new Error('{{i18n}} helper expected "key" to be a string'); } const context = { ...this, ...locals, ...options2?.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 var colors = { green: (str) => `\x1B[32m${str}\x1B[0m`, cyan: (str) => `\x1B[36m${str}\x1B[0m`, yellow: (str) => `\x1B[33m${str}\x1B[0m`, red: (str) => `\x1B[31m${str}\x1B[0m`, bold: (str) => `\x1B[1m${str}\x1B[0m` }; function cleanArgs(args) { const last2 = args[args.length - 1]; if (args.length > 1 && typeof last2 === "object" && last2 !== null && "hash" in last2) { return args.slice(0, -1); } return args; } function log(...args) { const cleanedArgs = cleanArgs(args); console.log(...cleanedArgs); return ""; } function ok(...args) { const cleanedArgs = cleanArgs(args); const message = cleanedArgs.join(" "); console.log(colors.green(`\u2713 ${message}`)); return ""; } function success(...args) { const cleanedArgs = cleanArgs(args); const message = cleanedArgs.map((arg) => String(arg)).join(" "); console.log(colors.green(message)); return ""; } function info(...args) { const cleanedArgs = cleanArgs(args); const message = cleanedArgs.map((arg) => String(arg)).join(" "); console.log(colors.cyan(message)); return ""; } function warning(...args) { const cleanedArgs = cleanArgs(args); const message = cleanedArgs.map((arg) => String(arg)).join(" "); console.error(colors.yellow(message)); return ""; } var warn = warning; function error(...args) { const cleanedArgs = cleanArgs(args); const message = cleanedArgs.map((arg) => String(arg)).join(" "); console.error(colors.red(message)); return ""; } var danger = error; function bold(...args) { const cleanedArgs = cleanArgs(args); const message = cleanedArgs.map((arg) => String(arg)).join(" "); console.error(colors.bold(message)); return ""; } function _debug(...args) { const separator = "\u2500".repeat(50); console.error(colors.cyan(separator)); if (args.length > 0) { const cleanedArgs = cleanArgs(args); console.error(colors.cyan("VALUE:"), cleanedArgs[0]); } if (this && Object.keys(this).length > 0) { console.error(colors.cyan("CONTEXT:"), this); } console.error(colors.cyan(separator)); return ""; } function _inspect(context, options2) { const val = JSON.stringify(context, null, 2); const type = options2?.hash?.type || "html"; return switchOutput(type, val); } function switchOutput(type, json) { if (type[0] === ".") { type = type.slice(1); } switch (type) { case "md": return ` \`\`\`json ${json} \`\`\` `; case "html": return `<div class="highlight highlight-json"> <pre><code> ${json}</code></pre></div>`; default: return json; } } 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, options2) => { const pats = typeof patterns === "string" ? patterns.split(/, */) : patterns; return micromatch2(files, pats, options2); }; var isMatch = (filepath, pattern, options2) => { return micromatch2.isMatch(filepath, pattern, options2); }; 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 = function(input, options2) { if (typeof input === "object" && input && "fn" in input && typeof input.fn === "function") { options2 = input; input = input.fn(this); } else if (typeof input !== "string") { input = ""; } const options_ = { cwd: process2.cwd(), ...options2 }; const md = new Remarkable({ breaks: true, html: true, langPrefix: "lang-", typographer: false, xhtmlOut: false }); let string_ = input; if (string_ && string_.length > 0) { const filepath = path4.resolve(options_.cwd, string_); if (fs3.existsSync(filepath) && fs3.statSync(filepath).isFile()) { string_ = fs3.readFileSync(filepath, "utf8"); } } return new Handlebars.SafeString(ent.decode(md.render(string_))); }; var helpers13 = [ { name: "md", category: "markdown", compatibility: ["nodejs"], fn: renderMarkdown }, { name: "markdown", category: "markdown", compatibility: ["nodejs"], fn: renderMarkdown } ]; // src/helpers/misc.ts import Handlebars2 from "handlebars"; import kindOf from "kind-of"; function frame(context, options2) { if (typeof context === "object" && context !== null && "hash" in context) { options2 = context; context = options2.data; } const data = Handlebars2.createFrame(context || {}); if (typeof options2 !== "object" || options2 === null) { options2 = {}; } if (options2.hash && typeof options2.hash === "object") { Object.assign(data, options2.hash); } return options2.fn(this, { data }); } function option(prop, locals, opts) { return get(options(this, locals, opts), prop); } function noop(options2) { return options2.fn(this); } var typeOf = kindOf; function withHash(options2) { if (options2.hash && Object.keys(options2