@ztimson/utils 0.28.2 → 0.28.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +137 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +137 -53
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -424,6 +424,8 @@ function sortByProp(prop, reverse = false) {
|
|
|
424
424
|
return function(a, b) {
|
|
425
425
|
const aVal = dotNotation(a, prop);
|
|
426
426
|
const bVal = dotNotation(b, prop);
|
|
427
|
+
if (aVal === void 0) return 1;
|
|
428
|
+
if (bVal === void 0) return -1;
|
|
427
429
|
if (typeof aVal == "number" && typeof bVal == "number")
|
|
428
430
|
return (reverse ? -1 : 1) * (aVal - bVal);
|
|
429
431
|
if (aVal > bVal) return reverse ? -1 : 1;
|
|
@@ -2350,8 +2352,11 @@ function findTemplateVars(html) {
|
|
|
2350
2352
|
return result;
|
|
2351
2353
|
}
|
|
2352
2354
|
async function renderTemplate(template, data, fetch2) {
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
+
if (!fetch2) fetch2 = (file) => {
|
|
2356
|
+
throw new TemplateError(`Unable to fetch template: ${file}`);
|
|
2357
|
+
};
|
|
2358
|
+
const now = /* @__PURE__ */ new Date();
|
|
2359
|
+
const d = {
|
|
2355
2360
|
date: {
|
|
2356
2361
|
day: now.getDate(),
|
|
2357
2362
|
month: now.toLocaleString("default", { month: "long" }),
|
|
@@ -2361,65 +2366,144 @@ async function renderTemplate(template, data, fetch2) {
|
|
|
2361
2366
|
},
|
|
2362
2367
|
...data || {}
|
|
2363
2368
|
};
|
|
2364
|
-
if (!fetch2) fetch2 = (file) => {
|
|
2365
|
-
throw new TemplateError(`Unable to fetch template: ${file}`);
|
|
2366
|
-
};
|
|
2367
2369
|
const evaluate = (code, data2, fatal = true) => {
|
|
2368
2370
|
try {
|
|
2369
2371
|
return Function("data", `with(data) { return ${code}; }`)(data2);
|
|
2370
|
-
} catch {
|
|
2371
|
-
if (fatal) throw new TemplateError(`Failed to evaluate: ${code}
|
|
2372
|
-
|
|
2372
|
+
} catch (err) {
|
|
2373
|
+
if (fatal) throw new TemplateError(`Failed to evaluate: ${code}
|
|
2374
|
+
${err.message || err.toString()}`);
|
|
2375
|
+
return false;
|
|
2373
2376
|
}
|
|
2374
2377
|
};
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2378
|
+
async function process(content, ctx = d) {
|
|
2379
|
+
let result = content;
|
|
2380
|
+
const extendsMatch = result.match(/\{\{\s*>\s*(.+?):(.+?)\s*}}([\s\S]*?)\{\{\s*\/>\s*}}/);
|
|
2381
|
+
if (extendsMatch) {
|
|
2382
|
+
const parentTemplate = await fetch2(extendsMatch[1].trim());
|
|
2383
|
+
if (!parentTemplate) throw new TemplateError(`Unknown extended template: ${extendsMatch[1].trim()}`);
|
|
2384
|
+
const slotName = extendsMatch[2].trim();
|
|
2385
|
+
const slotContent = await process(extendsMatch[3], ctx);
|
|
2386
|
+
return process(parentTemplate, { ...ctx, [slotName]: slotContent });
|
|
2387
|
+
}
|
|
2388
|
+
let changed = true;
|
|
2389
|
+
while (changed) {
|
|
2390
|
+
changed = false;
|
|
2391
|
+
const before = result;
|
|
2392
|
+
const importMatch = result.match(/\{\{\s*<\s*(.+?)\s*}}/);
|
|
2393
|
+
if (importMatch) {
|
|
2394
|
+
const t = await fetch2(importMatch[1].trim());
|
|
2395
|
+
if (!t) throw new TemplateError(`Unknown imported template: ${importMatch[1].trim()}`);
|
|
2396
|
+
const rendered = await process(t, ctx);
|
|
2397
|
+
result = result.replace(importMatch[0], rendered);
|
|
2398
|
+
changed = true;
|
|
2399
|
+
continue;
|
|
2400
|
+
}
|
|
2401
|
+
const forMatch = findInnermostFor(result);
|
|
2402
|
+
if (forMatch) {
|
|
2403
|
+
const { full, vars, array, body, start } = forMatch;
|
|
2404
|
+
const [element, index = "index"] = vars.split(",").map((v) => v.trim());
|
|
2405
|
+
const arr = dotNotation(ctx, array);
|
|
2406
|
+
if (!arr || typeof arr != "object") throw new TemplateError(`Cannot iterate: ${array}`);
|
|
2407
|
+
let output = [];
|
|
2408
|
+
for (let i = 0; i < arr.length; i++)
|
|
2409
|
+
output.push(await process(body, { ...ctx, [element]: arr[i], [index]: i }));
|
|
2410
|
+
result = result.slice(0, start) + output.join("\n") + result.slice(start + full.length);
|
|
2411
|
+
changed = true;
|
|
2412
|
+
continue;
|
|
2413
|
+
}
|
|
2414
|
+
const ifMatch = findInnermostIf(result);
|
|
2415
|
+
if (ifMatch) {
|
|
2416
|
+
const { full, condition, body, start } = ifMatch;
|
|
2417
|
+
const branches = parseIfBranches(body);
|
|
2418
|
+
let output = "";
|
|
2419
|
+
if (evaluate(condition, ctx, false)) {
|
|
2420
|
+
output = branches.if;
|
|
2421
|
+
} else {
|
|
2422
|
+
for (const branch of branches.elseIf) {
|
|
2423
|
+
if (evaluate(branch.condition, ctx, false)) {
|
|
2424
|
+
output = branch.body;
|
|
2425
|
+
break;
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
if (!output && branches.else) output = branches.else;
|
|
2390
2429
|
}
|
|
2430
|
+
result = result.slice(0, start) + output + result.slice(start + full.length);
|
|
2431
|
+
changed = true;
|
|
2432
|
+
continue;
|
|
2433
|
+
}
|
|
2434
|
+
if (before === result) changed = false;
|
|
2435
|
+
}
|
|
2436
|
+
return processVariables(result, ctx);
|
|
2437
|
+
}
|
|
2438
|
+
function processVariables(content, data2) {
|
|
2439
|
+
return content.replace(/\{\{\s*([^<>\*\?!/}\s][^{}]*?)\s*}}/g, (match, code) => {
|
|
2440
|
+
return evaluate(code.trim(), data2) ?? "";
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
function findInnermostIf(content) {
|
|
2444
|
+
const regex = /\{\{\s*\?\s*(.+?)\s*}}/g;
|
|
2445
|
+
let match, lastMatch = null;
|
|
2446
|
+
while ((match = regex.exec(content)) !== null) {
|
|
2447
|
+
const start = match.index;
|
|
2448
|
+
const condition = match[1];
|
|
2449
|
+
const bodyStart = match.index + match[0].length;
|
|
2450
|
+
const end = findMatchingClose(content, bodyStart, /\{\{\s*\?\s*/, /\{\{\s*\/\?\s*}}/);
|
|
2451
|
+
if (end === -1) throw new TemplateError(`Unmatched if-statement at position ${start}`);
|
|
2452
|
+
const closeTag = content.slice(end).match(/\{\{\s*\/\?\s*}}/);
|
|
2453
|
+
const full = content.slice(start, end + closeTag[0].length);
|
|
2454
|
+
const body = content.slice(bodyStart, end);
|
|
2455
|
+
lastMatch = { full, condition, body, start };
|
|
2456
|
+
}
|
|
2457
|
+
return lastMatch;
|
|
2458
|
+
}
|
|
2459
|
+
function findInnermostFor(content) {
|
|
2460
|
+
const regex = /\{\{\s*\*\s*(.+?)\s+in\s+(.+?)\s*}}/g;
|
|
2461
|
+
let match, lastMatch = null;
|
|
2462
|
+
while ((match = regex.exec(content)) !== null) {
|
|
2463
|
+
const start = match.index;
|
|
2464
|
+
const vars = match[1].replaceAll(/[()\s]/g, "");
|
|
2465
|
+
const array = match[2];
|
|
2466
|
+
const bodyStart = match.index + match[0].length;
|
|
2467
|
+
const end = findMatchingClose(content, bodyStart, /\{\{\s*\*\s*/, /\{\{\s*\/\*\s*}}/);
|
|
2468
|
+
if (end === -1) throw new TemplateError(`Unmatched for-loop at position ${start}`);
|
|
2469
|
+
const closeTag = content.slice(end).match(/\{\{\s*\/\*\s*}}/);
|
|
2470
|
+
const full = content.slice(start, end + closeTag[0].length);
|
|
2471
|
+
const body = content.slice(bodyStart, end);
|
|
2472
|
+
lastMatch = { full, vars, array, body, start };
|
|
2473
|
+
}
|
|
2474
|
+
return lastMatch;
|
|
2475
|
+
}
|
|
2476
|
+
function findMatchingClose(content, startIndex, openTag, closeTag) {
|
|
2477
|
+
let depth = 1, pos = startIndex;
|
|
2478
|
+
while (depth > 0 && pos < content.length) {
|
|
2479
|
+
const remaining = content.slice(pos);
|
|
2480
|
+
const nextOpen = remaining.search(openTag);
|
|
2481
|
+
const nextClose = remaining.search(closeTag);
|
|
2482
|
+
if (nextClose === -1) return -1;
|
|
2483
|
+
if (nextOpen !== -1 && nextOpen < nextClose) {
|
|
2484
|
+
depth++;
|
|
2485
|
+
pos += nextOpen + 1;
|
|
2486
|
+
} else {
|
|
2487
|
+
depth--;
|
|
2488
|
+
if (depth === 0) return pos + nextClose;
|
|
2489
|
+
pos += nextClose + 1;
|
|
2391
2490
|
}
|
|
2392
2491
|
}
|
|
2393
|
-
|
|
2394
|
-
}
|
|
2395
|
-
|
|
2396
|
-
const
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
compiled.push(await renderTemplate(found[3], { ...d, [element]: array[i], [index]: i }, fetch2));
|
|
2409
|
-
content = content.replace(found[0], compiled.join("\n"));
|
|
2410
|
-
}
|
|
2411
|
-
while (!!(found = /\{\{\s*([^<>\*\?!/}\s][^}]*?)\s*}}/g.exec(content))) {
|
|
2412
|
-
content = content.replace(found[0], evaluate(found[1].trim(), d) ?? "");
|
|
2413
|
-
}
|
|
2414
|
-
while (!!(found = /\{\{\s*?>\s*?(.+?):(.+?)\s*?}}([\s\S]*?)\{\{\s*?\/>\s*?}}/g.exec(content))) {
|
|
2415
|
-
const t = await fetch2(found[1].trim());
|
|
2416
|
-
if (!t) throw new TemplateError(`Unknown extended templated: ${found[1].trim()}`);
|
|
2417
|
-
content = content.replace(found[0], await renderTemplate(t, {
|
|
2418
|
-
...d,
|
|
2419
|
-
[found[2].trim()]: found[3]
|
|
2420
|
-
}, fetch2));
|
|
2421
|
-
}
|
|
2422
|
-
return content;
|
|
2492
|
+
return -1;
|
|
2493
|
+
}
|
|
2494
|
+
function parseIfBranches(body) {
|
|
2495
|
+
const parts = body.split(/\{\{\s*!\?\s*/);
|
|
2496
|
+
const result = { if: parts[0], elseIf: [], else: "" };
|
|
2497
|
+
for (let i = 1; i < parts.length; i++) {
|
|
2498
|
+
const closeBrace = parts[i].indexOf("}}");
|
|
2499
|
+
const condition = parts[i].slice(0, closeBrace).trim();
|
|
2500
|
+
const branchBody = parts[i].slice(closeBrace + 2);
|
|
2501
|
+
if (!condition) result.else = branchBody;
|
|
2502
|
+
else result.elseIf.push({ condition, body: branchBody });
|
|
2503
|
+
}
|
|
2504
|
+
return result;
|
|
2505
|
+
}
|
|
2506
|
+
return process(template);
|
|
2423
2507
|
}
|
|
2424
2508
|
var dist = {};
|
|
2425
2509
|
var persist = {};
|