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