UNPKG

tarkine

Version:

Tarkine - A lightweight and high-performance template engine for Node.js, designed for speed and simplicity.

1,363 lines (1,345 loc) 40.2 kB
var __getOwnPropNames = Object.getOwnPropertyNames; var __commonJS = (cb, mod) => function __require() { try { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; } catch (e) { throw mod = 0, e; } }; // packages/tarkine/src/utils/scope.js var require_scope = __commonJS({ "packages/tarkine/src/utils/scope.js"(exports2, module2) { function buildRootScope(globals, scope) { return globals ? Object.assign(Object.create(globals), scope) : scope; } function createGlobalsHandle(store) { return { get(key) { return store[key]; }, getAll() { return store; }, set(key, value) { store[key] = value; return value; }, delete(key) { delete store[key]; }, clear() { for (const key in store) { delete store[key]; } } }; } module2.exports = { buildRootScope, createGlobalsHandle }; } }); // packages/tarkine/src/compiler/templateLexer.js var require_templateLexer = __commonJS({ "packages/tarkine/src/compiler/templateLexer.js"(exports2, module2) { var HTML_BOOLEAN_ATTRIBUTES = /* @__PURE__ */ new Set([ "checked", "disabled", "readonly", "required", "autofocus", "multiple", "selected", "hidden", "open", "ismap", "defer", "async", "novalidate", "formnovalidate", "allowfullscreen", "itemscope", "reversed", "autoplay", "controls", "loop", "muted", "default" ]); var TEMPLATE_OP = { TEXT: "text", ESCAPE: "escape", RAW: "raw", BOOL_ATTR: "boolAttr", IF: ":if", ELSE: ":else", FOR: ":for", LAYOUT: ":layout", SLOT: ":slot", INCLUDE: ":include", END_IF: "/if", END_FOR: "/for", END_SLOT: "/slot", END_LAYOUT: "/layout" }; var BOOL_ATTR_PATTERN = [...HTML_BOOLEAN_ATTRIBUTES].join("|"); var TEMPLATE_TOKEN_REGEX = new RegExp( `(${BOOL_ATTR_PATTERN})\\s*=\\s*(["'])\\s*\\{\\{\\s*([\\s\\S]*?)\\s*\\}\\}\\s*\\2|\\{\\{(#|-|\\/if|\\/for|\\/slot|\\/layout|:if|:else|:for|:layout|:slot|:include)?\\s*([\\s\\S]*?)\\s*\\}\\}`, "gi" ); function lexTemplate(template) { const tokens = []; let offset = 0; TEMPLATE_TOKEN_REGEX.lastIndex = 0; for (const match of template.matchAll(TEMPLATE_TOKEN_REGEX)) { const text = template.slice(offset, match.index); const isBooleanAttribute = match[1] !== void 0; if (isBooleanAttribute) { const trimmedText = text.replace(/\s+$/, ""); if (trimmedText) { tokens.push({ type: TEMPLATE_OP.TEXT, value: trimmedText }); } tokens.push({ type: TEMPLATE_OP.BOOL_ATTR, attribute: match[1].toLowerCase(), expression: match[3].trim() }); } else { if (text) { tokens.push({ type: TEMPLATE_OP.TEXT, value: text }); } const directive = match[4]?.trim() ?? null; const expression = match[5].trim(); switch (directive) { case null: tokens.push({ type: TEMPLATE_OP.ESCAPE, expression }); break; case "-": tokens.push({ type: TEMPLATE_OP.RAW, expression }); break; case "#": break; default: tokens.push({ type: directive, expression }); break; } } offset = match.index + match[0].length; } const remainingText = template.slice(offset); if (remainingText) { tokens.push({ type: TEMPLATE_OP.TEXT, value: remainingText }); } return tokens; } module2.exports = { lexTemplate, TEMPLATE_OP }; } }); // packages/tarkine/src/compiler/expressionLexer.js var require_expressionLexer = __commonJS({ "packages/tarkine/src/compiler/expressionLexer.js"(exports2, module2) { var KEYWORD_LITERALS = { true: true, false: false, null: null, undefined: void 0 }; var MULTI_CHAR_OPERATORS = [ "===", "!==", ">=", "<=", "==", "!=", "&&", "||", "**", "++", "--" ]; var ESCAPE_SEQUENCES = { n: "\n", t: " ", r: "\r", b: "\b", f: "\f", v: "\v", 0: "\0" }; function syntaxError(message, position) { const err = new SyntaxError( position == null ? message : `${message} (at index ${position})` ); err.position = position; return err; } function lexExpression(source) { const tokens = []; let index = 0; while (index < source.length) { const start = index; if (/\s/.test(source[index])) { index++; continue; } if (source[index] === '"' || source[index] === "'") { const quote = source[index++]; let value = ""; while (index < source.length && source[index] !== quote) { if (source[index] === "\\") { index++; const escaped = source[index]; value += ESCAPE_SEQUENCES[escaped] ?? escaped; } else { value += source[index]; } index++; } if (index >= source.length) { throw syntaxError("Unterminated string literal", start); } index++; tokens.push({ type: "str", value, position: start }); continue; } if (source[index] === "`") { index++; tokens.push({ type: "tplStart", position: start }); let textSegment = ""; while (index < source.length && source[index] !== "`") { if (source[index] === "$" && source[index + 1] === "{") { if (textSegment) { tokens.push({ type: "str", value: textSegment, position: index }); textSegment = ""; } const exprStart = index; index += 2; tokens.push({ type: "tplExpr", position: exprStart }); let braceDepth = 1; let expression = ""; let inString = null; while (index < source.length && braceDepth > 0) { const ch = source[index]; if (inString) { expression += ch; if (ch === "\\" && index + 1 < source.length) { index++; expression += source[index]; } else if (ch === inString) { inString = null; } index++; continue; } if (ch === '"' || ch === "'" || ch === "`") { inString = ch; expression += ch; index++; continue; } if (ch === "{") { braceDepth++; } else if (ch === "}") { if (--braceDepth === 0) { index++; break; } } expression += ch; index++; } if (braceDepth > 0) { throw syntaxError( "Unterminated template expression", exprStart ); } tokens.push({ type: "tplExprEnd", expr: expression, position: index }); } else { textSegment += source[index++]; } } if (index >= source.length) { throw syntaxError("Unterminated template literal", start); } if (textSegment) { tokens.push({ type: "str", value: textSegment, position: index }); } index++; tokens.push({ type: "tplEnd", position: index }); continue; } if (/[0-9]/.test(source[index]) || source[index] === "." && /[0-9]/.test(source[index + 1])) { let numericText = ""; while (index < source.length && /[0-9._]/.test(source[index])) { numericText += source[index++]; } if ((numericText.match(/\./g) ?? []).length > 1) { throw syntaxError( `Invalid number literal "${numericText}"`, start ); } tokens.push({ type: "num", value: Number(numericText.replace(/_/g, "")), position: start }); continue; } let operatorMatched = false; for (const operator of MULTI_CHAR_OPERATORS) { if (source.slice(index, index + operator.length) === operator) { tokens.push({ type: "op", value: operator, position: start }); index += operator.length; operatorMatched = true; break; } } if (operatorMatched) continue; if ("+-*/%<>!?:.,[](){}=".includes(source[index])) { tokens.push({ type: "op", value: source[index++], position: start }); continue; } if (/[a-zA-Z_$]/.test(source[index])) { let identifier = ""; while (index < source.length && /[a-zA-Z0-9_$]/.test(source[index])) { identifier += source[index++]; } if (identifier in KEYWORD_LITERALS) { tokens.push({ type: "lit", value: KEYWORD_LITERALS[identifier], position: start }); } else if (identifier === "typeof" || identifier === "in") { tokens.push({ type: "op", value: identifier, position: start }); } else { tokens.push({ type: "id", value: identifier, position: start }); } continue; } throw syntaxError(`Unexpected character "${source[index]}"`, start); } return tokens; } module2.exports = { lexExpression, syntaxError }; } }); // packages/tarkine/src/compiler/expressionParser.js var require_expressionParser = __commonJS({ "packages/tarkine/src/compiler/expressionParser.js"(exports2, module2) { var { syntaxError } = require_expressionLexer(); function parseExpression(tokens) { let pos = 0; function peek() { return tokens[pos]; } function expect(value) { const token = tokens[pos]; if (token?.value !== value) { throw syntaxError( `Expected "${value}"${token ? ` but got "${token.value ?? token.type}"` : ""}`, token?.position ); } pos++; return token; } function primary() { const token = tokens[pos++]; if (!token) { throw syntaxError("Unexpected end of expression"); } if (token.type === "tplStart") { const parts = []; while (peek() && peek().type !== "tplEnd") { const part = tokens[pos++]; if (part.type === "str") { parts.push(part); continue; } if (part.type === "tplExpr") { const expr = tokens[pos++]; if (!expr || expr.type !== "tplExprEnd") { throw syntaxError( "Expected template expression", expr?.position ); } parts.push({ type: "templateExpr", expr: expr.expr }); continue; } throw syntaxError( `Unexpected token "${part.type}" in template literal`, part.position ); } if (peek()?.type !== "tplEnd") { throw syntaxError("Expected template literal end"); } pos++; return { type: "template", parts }; } if (token.type === "num" || token.type === "str" || token.type === "lit") { return token; } if (token.type === "id") { return { type: "id", value: token.value }; } if (token.value === "(") { const expr = ternary(); expect(")"); return expr; } throw syntaxError( `Unexpected token "${token.value ?? token.type}"`, token.position ); } function postfix() { let expr = primary(); while (true) { if (peek()?.value === ".") { pos++; const propertyToken = tokens[pos]; if (propertyToken?.type !== "id") { throw syntaxError( 'Expected property name after "."', propertyToken?.position ); } pos++; expr = { type: "member", object: expr, property: propertyToken.value, computed: false }; continue; } if (peek()?.value === "[") { pos++; const property = ternary(); expect("]"); expr = { type: "member", object: expr, property, computed: true }; continue; } if (peek()?.value === "(") { pos++; const args = []; if (peek()?.value !== ")") { args.push(ternary()); while (peek()?.value === ",") { pos++; args.push(ternary()); } } expect(")"); expr = { type: "call", callee: expr, args }; continue; } break; } return expr; } function unary() { if (peek()?.value === "!" || peek()?.value === "-" || peek()?.value === "typeof") { const operator = tokens[pos++].value; return { type: "unary", operator, expr: unary() }; } return postfix(); } function exponent() { let left = unary(); if (peek()?.value === "**") { pos++; return { type: "binary", operator: "**", left, right: exponent() }; } return left; } function multiply() { let left = exponent(); while (peek()?.value === "*" || peek()?.value === "/" || peek()?.value === "%") { const operator = tokens[pos++].value; left = { type: "binary", operator, left, right: exponent() }; } return left; } function add() { let left = multiply(); while (peek()?.value === "+" || peek()?.value === "-") { const operator = tokens[pos++].value; left = { type: "binary", operator, left, right: multiply() }; } return left; } function relational() { let left = add(); while (peek()?.value === "<" || peek()?.value === "<=" || peek()?.value === ">" || peek()?.value === ">=" || peek()?.value === "in") { const operator = tokens[pos++].value; left = { type: "binary", operator, left, right: add() }; } return left; } function equality() { let left = relational(); while (peek()?.value === "==" || peek()?.value === "!=" || peek()?.value === "===" || peek()?.value === "!==") { const operator = tokens[pos++].value; left = { type: "binary", operator, left, right: relational() }; } return left; } function and() { let left = equality(); while (peek()?.value === "&&") { pos++; left = { type: "binary", operator: "&&", left, right: equality() }; } return left; } function or() { let left = and(); while (peek()?.value === "||") { pos++; left = { type: "binary", operator: "||", left, right: and() }; } return left; } function ternary() { const test = or(); if (peek()?.value !== "?") { return test; } pos++; const yes = ternary(); expect(":"); const no = ternary(); return { type: "ternary", test, yes, no }; } const result = ternary(); if (pos < tokens.length) { const trailing = tokens[pos]; throw syntaxError( `Unexpected token "${trailing.value ?? trailing.type}" after expression`, trailing.position ); } return result; } module2.exports = { parseExpression }; } }); // packages/tarkine/src/compiler/evaluator.js var require_evaluator = __commonJS({ "packages/tarkine/src/compiler/evaluator.js"(exports2, module2) { var { parseExpression } = require_expressionParser(); var { lexExpression } = require_expressionLexer(); function createEvaluatorFromAst(node) { switch (node?.type) { case "id": { const key = node.value; return (scope) => scope[key]; } case "member": { const object = createEvaluatorFromAst(node.object); if (node.computed) { const property2 = createEvaluatorFromAst(node.property); return (scope) => object(scope)?.[property2(scope)]; } const property = node.property; return (scope) => object(scope)?.[property]; } case "call": { const args = node.args.map(createEvaluatorFromAst); if (node.callee.type === "member") { const object = createEvaluatorFromAst(node.callee.object); const computed = node.callee.computed; const property = computed ? createEvaluatorFromAst(node.callee.property) : node.callee.property; return (scope) => { const receiver = object(scope); const key = computed ? property(scope) : property; const fn = receiver?.[key]; if (typeof fn !== "function") { return void 0; } return fn.apply( receiver, args.map((arg) => arg(scope)) ); }; } const callee = createEvaluatorFromAst(node.callee); return (scope) => { const fn = callee(scope); if (typeof fn !== "function") { return void 0; } return fn(...args.map((arg) => arg(scope))); }; } case "str": case "num": case "lit": { const value = node.value; return () => value; } case "unary": { const expr = createEvaluatorFromAst(node.expr); switch (node.operator) { case "!": return (scope) => !expr(scope); case "-": return (scope) => -expr(scope); case "typeof": return (scope) => typeof expr(scope); } return () => void 0; } case "binary": { const left = createEvaluatorFromAst(node.left); const right = createEvaluatorFromAst(node.right); switch (node.operator) { case "===": return (scope) => left(scope) === right(scope); case "!==": return (scope) => left(scope) !== right(scope); case "==": return (scope) => left(scope) == right(scope); case "!=": return (scope) => left(scope) != right(scope); case ">": return (scope) => left(scope) > right(scope); case ">=": return (scope) => left(scope) >= right(scope); case "<": return (scope) => left(scope) < right(scope); case "<=": return (scope) => left(scope) <= right(scope); case "&&": return (scope) => left(scope) && right(scope); case "||": return (scope) => left(scope) || right(scope); case "+": return (scope) => left(scope) + right(scope); case "-": return (scope) => left(scope) - right(scope); case "*": return (scope) => left(scope) * right(scope); case "/": return (scope) => left(scope) / right(scope); case "%": return (scope) => left(scope) % right(scope); case "**": return (scope) => left(scope) ** right(scope); case "in": return (scope) => left(scope) in Object(right(scope)); } return () => void 0; } case "template": { const parts = node.parts.map((part) => { if (part.type === "str") { return () => part.value; } if (part.type === "templateExpr") { const evaluator = createEvaluator(part.expr); return evaluator; } return () => ""; }); return (scope) => parts.map((part) => String(part(scope) ?? "")).join(""); } case "ternary": { const test = createEvaluatorFromAst(node.test); const yes = createEvaluatorFromAst(node.yes); const no = createEvaluatorFromAst(node.no); return (scope) => test(scope) ? yes(scope) : no(scope); } default: return () => void 0; } } function createEvaluator(expression) { const nodes = parseExpression(lexExpression(expression)); return createEvaluatorFromAst(nodes); } module2.exports = { createEvaluator }; } }); // packages/tarkine/src/compiler/templateParser.js var require_templateParser = __commonJS({ "packages/tarkine/src/compiler/templateParser.js"(exports2, module2) { var { TEMPLATE_OP } = require_templateLexer(); var { createEvaluator } = require_evaluator(); var FOR_PATTERN = /^\s*([A-Za-z_$][\w$]*)(?:\s*,\s*([A-Za-z_$][\w$]*))?\s+in\s+([\s\S]+)$/; var ELSE_IF_PATTERN = /^if\s+([\s\S]+)$/; function parseTemplate(tokens) { const root = []; const stack = [ { type: "root", body: root } ]; function currentBody() { return stack[stack.length - 1].body; } for (const token of tokens) { switch (token.type) { case TEMPLATE_OP.TEXT: { currentBody().push(token); break; } case TEMPLATE_OP.RAW: case TEMPLATE_OP.ESCAPE: { currentBody().push({ type: token.type, evaluate: createEvaluator(token.expression) }); break; } case TEMPLATE_OP.BOOL_ATTR: { currentBody().push({ type: token.type, attribute: token.attribute, evaluate: createEvaluator(token.expression) }); break; } case TEMPLATE_OP.IF: { const node = { type: TEMPLATE_OP.IF, branches: [ { evaluate: createEvaluator(token.expression), body: [] } ] }; currentBody().push(node); stack.push({ type: TEMPLATE_OP.IF, node, body: node.branches[0].body }); break; } case TEMPLATE_OP.ELSE: { const frame = stack[stack.length - 1]; if (frame.type !== TEMPLATE_OP.IF) { throw new Error("Unexpected {{:else}} or {{:else if}}"); } const match = token.expression.match(ELSE_IF_PATTERN); const branch = { evaluate: match ? createEvaluator(match[1].trim()) : null, body: [] }; frame.node.branches.push(branch); frame.body = branch.body; break; } case TEMPLATE_OP.END_IF: { const frame = stack.pop(); if (!frame || frame.type !== TEMPLATE_OP.IF) { throw new Error("Unexpected {{/if}}"); } break; } case TEMPLATE_OP.FOR: { const match = token.expression.match(FOR_PATTERN); if (!match) { throw new Error( `Invalid for expression: ${token.expression}` ); } const [, value, key, iterable] = match; const node = { type: TEMPLATE_OP.FOR, value, key: key || null, evaluate: createEvaluator(iterable.trim()), body: [] }; currentBody().push(node); stack.push({ type: TEMPLATE_OP.FOR, body: node.body }); break; } case TEMPLATE_OP.END_FOR: { const frame = stack.pop(); if (!frame || frame.type !== TEMPLATE_OP.FOR) { throw new Error("Unexpected {{/for}}"); } break; } case TEMPLATE_OP.INCLUDE: { currentBody().push({ type: TEMPLATE_OP.INCLUDE, evaluate: createEvaluator(token.expression) }); break; } case TEMPLATE_OP.LAYOUT: { const node = { type: TEMPLATE_OP.LAYOUT, evaluate: createEvaluator(token.expression), slots: {} }; currentBody().push(node); stack.push({ type: TEMPLATE_OP.LAYOUT, node, body: [] // layout has no body of its own, only slots }); break; } case TEMPLATE_OP.END_LAYOUT: { const frame = stack.pop(); if (!frame || frame.type !== TEMPLATE_OP.LAYOUT) { throw new Error("Unexpected {{/layout}}"); } break; } case TEMPLATE_OP.SLOT: { const node = { type: TEMPLATE_OP.SLOT, name: token.expression, body: [] }; const parent = stack[stack.length - 1]; if (parent.type === TEMPLATE_OP.LAYOUT) { parent.node.slots[node.name] = node.body; } else { currentBody().push(node); } stack.push({ type: TEMPLATE_OP.SLOT, body: node.body }); break; } case TEMPLATE_OP.END_SLOT: { const frame = stack.pop(); if (!frame || frame.type !== TEMPLATE_OP.SLOT) { throw new Error("Unexpected {{/slot}}"); } break; } default: throw new Error(`Unknown template token: ${token.type}`); } } if (stack.length !== 1) { throw new Error("Unclosed template block"); } return root; } module2.exports = { parseTemplate }; } }); // packages/tarkine/src/compiler/compiler.js var require_compiler = __commonJS({ "packages/tarkine/src/compiler/compiler.js"(exports2, module2) { var { lexTemplate, TEMPLATE_OP } = require_templateLexer(); var { parseTemplate } = require_templateParser(); function compile(template) { return parseTemplate(lexTemplate(template)); } module2.exports = { compile, TEMPLATE_OP }; } }); // packages/tarkine/src/renderer.js var require_renderer = __commonJS({ "packages/tarkine/src/renderer.js"(exports2, module2) { var { TEMPLATE_OP } = require_compiler(); function renderNodes(nodes, scope, ctx, out) { for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; switch (node.type) { case TEMPLATE_OP.TEXT: out.push(node.value); break; case TEMPLATE_OP.ESCAPE: { const value = node.evaluate(scope); if (value != null) { out.push(ctx.escape(value)); } break; } case TEMPLATE_OP.RAW: { const value = node.evaluate(scope); if (value != null) { out.push(String(value)); } break; } case TEMPLATE_OP.BOOL_ATTR: { const value = node.evaluate(scope); if (value) { out.push(` ${node.attribute}`); } break; } case TEMPLATE_OP.IF: { for (const branch of node.branches) { if (branch.evaluate(scope)) { renderNodes(branch.body, scope, ctx, out); break; } } break; } case TEMPLATE_OP.FOR: { const iterable = node.evaluate(scope) || []; if (Array.isArray(iterable)) { for (let i2 = 0; i2 < iterable.length; i2++) { const child = Object.create(scope); child[node.value] = iterable[i2]; if (node.key) { child[node.key] = i2; } renderNodes(node.body, child, ctx, out); } } else { for (const key in iterable) { const child = Object.create(scope); child[node.value] = iterable[key]; if (node.key) { child[node.key] = key; } renderNodes(node.body, child, ctx, out); } } break; } case TEMPLATE_OP.INCLUDE: { const includePath = node.evaluate(scope); const { output, filePath } = ctx.include( ctx.parentFilePath, includePath, ctx ); renderNodes( output, scope, { ...ctx, parentFilePath: filePath }, out ); break; } case TEMPLATE_OP.LAYOUT: { const layoutPath = node.evaluate(scope); const { output, filePath } = ctx.include( ctx.parentFilePath, layoutPath, ctx ); renderNodes( output, scope, { ...ctx, parentFilePath: filePath, slots: node.slots }, out ); break; } case TEMPLATE_OP.SLOT: { const slotName = node.name; if (ctx.slots && ctx.slots[slotName]) { renderNodes(ctx.slots[slotName], scope, ctx, out); } else { renderNodes(node.body, scope, ctx, out); } break; } } } } function renderer(compiledTokens, scope, ctx = {}) { const out = []; renderNodes(compiledTokens, scope, ctx, out); return out.join(""); } module2.exports = { renderer }; } }); // packages/tarkine/src/utils/escape.js var require_escape = __commonJS({ "packages/tarkine/src/utils/escape.js"(exports2, module2) { var ESCAPE_CHARS = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }; function escapeHtml(v) { if (v == null || v === false) return ""; if (typeof v === "object") { return JSON.stringify(v).replace(/[&<>"']/g, (c) => ESCAPE_CHARS[c]); } return String(v).replace(/[&<>"']/g, (c) => ESCAPE_CHARS[c]); } module2.exports = { escapeHtml }; } }); // packages/tarkine/src/templateLoader.js var require_templateLoader = __commonJS({ "packages/tarkine/src/templateLoader.js"(exports2, module2) { var path2 = require("node:path"); var fs = require("node:fs"); var { buildRootScope } = require_scope(); var { compile } = require_compiler(); var { renderer } = require_renderer(); var { escapeHtml } = require_escape(); function resolveFilePath2(filePath, ext) { if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) { return path2.resolve(filePath, `index.${ext}`); } return path2.extname(filePath) ? filePath : `${filePath}.${ext}`; } function compileFile2(filePath, cache) { if (cache) { const cached = cache.get(filePath); if (cached) { return cached; } } const content = fs.readFileSync(filePath, "utf-8"); const compiled = compile(content); if (cache) { cache.set(filePath, compiled); } return compiled; } function includeFile2(parentFilePath, includePath, { cache, ext }) { const filePath = resolveFilePath2( path2.resolve( parentFilePath ? path2.dirname(parentFilePath) : "", includePath ), ext ); if (!fs.existsSync(filePath)) { throw new Error(`File not found: ${filePath}`); } return { output: compileFile2(filePath, cache), filePath }; } function renderFile2(filepath, scope, engine) { const { cache, globals } = engine; const compiled = compileFile2(filepath, cache); const rootScope = buildRootScope(globals, scope); const include = engine.include ?? includeFile2; const escape = engine.escape ?? escapeHtml; return renderer(compiled, rootScope, { parentFilePath: filepath, cache, include, escape }); } module2.exports = { resolveFilePath: resolveFilePath2, compileFile: compileFile2, includeFile: includeFile2, renderFile: renderFile2 }; } }); // packages/tarkine/src/utils/cache.js var require_cache = __commonJS({ "packages/tarkine/src/utils/cache.js"(exports2, module2) { function createCacheStore(maxSize) { const store = /* @__PURE__ */ new Map(); return { get(key) { if (!store.has(key)) { return void 0; } const value = store.get(key); store.delete(key); store.set(key, value); return value; }, set(key, value) { store.delete(key); store.set(key, value); if (maxSize && store.size > maxSize) { store.delete(store.keys().next().value); } } }; } module2.exports = { createCacheStore }; } }); // packages/tarkine/src/utils/util.js var require_util = __commonJS({ "packages/tarkine/src/utils/util.js"(exports2, module2) { module2.exports = { isFunction: (value) => typeof value === "function" }; } }); // packages/tarkine/src/engine.js var require_engine = __commonJS({ "packages/tarkine/src/engine.js"(exports2, module2) { var { buildRootScope, createGlobalsHandle } = require_scope(); var { createCacheStore } = require_cache(); var { compile } = require_compiler(); var { escapeHtml } = require_escape(); var { isFunction } = require_util(); var { renderer } = require_renderer(); function createEngine2({ cache, include, globals, escape, maxCacheSize, ext: extension } = {}) { const compiledCache = cache === false ? null : cache ?? createCacheStore(maxCacheSize); const globalsStore = globals ?? {}; const globalsHandle = createGlobalsHandle(globalsStore); const escapeFn = isFunction(escape) ? escape : escapeHtml; const ext = extension ?? "tark"; function cachedCompile(template) { if (!compiledCache) { return compile(template); } let compiled = compiledCache.get(template); if (!compiled) { compiled = compile(template); compiledCache.set(template, compiled); } return compiled; } function render(template, scope, ctx = {}) { return renderCompiled(cachedCompile(template), scope, ctx); } function renderCompiled(compiledTokens, scope, ctx = {}) { const rootScope = buildRootScope(globals, scope); return renderer(compiledTokens, rootScope, { parentFilePath: null, escape: escapeFn, cache: compiledCache, include, ext, ...ctx }); } return { cache: compiledCache, globals: globalsHandle, compile: cachedCompile, renderCompiled, render, escape, include, ext }; } module2.exports = { createEngine: createEngine2 }; } }); // packages/tarkine/src/adapters/express.js var require_express = __commonJS({ "packages/tarkine/src/adapters/express.js"(exports2, module2) { var { renderFile: renderFile2 } = require_templateLoader(); var { isFunction } = require_util(); function express(engine, adapterOptions = {}) { return function(filepath, scope = {}, callback) { let output; try { output = renderFile2(filepath, scope, engine); } catch (error) { if (isFunction(callback)) { return callback(error); } throw error; } if (isFunction(callback)) { return callback(null, output); } return output; }; } module2.exports = { express }; } }); // packages/tarkine/src/adapters/hono.js var require_hono = __commonJS({ "packages/tarkine/src/adapters/hono.js"(exports2, module2) { var { renderFile: renderFile2 } = require_templateLoader(); function hono(engine, adapterOptions = {}) { const viewsDir = adapterOptions.views || "views"; return async (c, next) => { c.setRenderer((viewName, scope) => { const filepath = path.resolve(viewsDir, viewName); const output = renderFile2(filepath, scope, engine); return c.html(output); }); return await next(); }; } module2.exports = { hono }; } }); // packages/tarkine/src/adapters/index.js var require_adapters = __commonJS({ "packages/tarkine/src/adapters/index.js"(exports2, module2) { var { express } = require_express(); var { hono } = require_hono(); module2.exports = { express, hono }; } }); // packages/tarkine/src/index.js var { renderFile, compileFile, includeFile, resolveFilePath } = require_templateLoader(); var { createEngine } = require_engine(); var adapters = require_adapters(); module.exports = { adapters, renderFile, compileFile, includeFile, createEngine, resolveFilePath };