@jaredwray/fumanchu
Version:
Handlebars + Helpers = Fumanchu
2,014 lines (1,999 loc) • 78.7 kB
JavaScript
// 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("`");
}
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