eta
Version:
Lightweight, fast, and powerful embedded JS template engine
1 lines • 41.4 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","names":["path","path","err: any","config: EtaConfig","compileBody","buffer: Array<AstObject>","leftTrim: string | false","rightTrim: string | false","escMap: { [key: string]: string }","defaultConfig: EtaConfig","buffer: Array<AstObject>","trimLeftOfNextStr: string | false","m: RegExpExecArray | null","closeTag: RegExpExecArray | null","currentObj: AstObject | false","templateFn: TemplateFunction","cache: Record<string, T>","Eta","EtaCore"],"sources":["../src/err.ts","../src/file-handling.ts","../src/compile.ts","../src/compile-string.ts","../src/utils.ts","../src/config.ts","../src/parse.ts","../src/render.ts","../src/storage.ts","../src/internal.ts","../src/index.ts"],"sourcesContent":["export class EtaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"Eta Error\";\n }\n}\n\nexport class EtaParseError extends EtaError {\n constructor(message: string) {\n super(message);\n this.name = \"EtaParser Error\";\n }\n}\n\nexport class EtaRuntimeError extends EtaError {\n constructor(message: string) {\n super(message);\n this.name = \"EtaRuntime Error\";\n }\n}\n\nexport class EtaFileResolutionError extends EtaError {\n constructor(message: string) {\n super(message);\n this.name = \"EtaFileResolution Error\";\n }\n}\n\nexport class EtaNameResolutionError extends EtaError {\n constructor(message: string) {\n super(message);\n this.name = \"EtaNameResolution Error\";\n }\n}\n\n/**\n * Throws an EtaError with a nicely formatted error and message showing where in the template the error occurred.\n */\n\nexport function ParseErr(message: string, str: string, indx: number): never {\n const whitespace = str.slice(0, indx).split(/\\n/);\n\n const lineNo = whitespace.length;\n const colNo = whitespace[lineNo - 1].length + 1;\n message +=\n \" at line \" +\n lineNo +\n \" col \" +\n colNo +\n \":\\n\\n\" +\n \" \" +\n str.split(/\\n/)[lineNo - 1] +\n \"\\n\" +\n \" \" +\n Array(colNo).join(\" \") +\n \"^\";\n throw new EtaParseError(message);\n}\n\nexport function RuntimeErr(\n originalError: Error,\n str: string,\n lineNo: number,\n path: string,\n): never {\n // code gratefully taken from https://github.com/mde/ejs and adapted\n\n const lines = str.split(\"\\n\");\n const start = Math.max(lineNo - 3, 0);\n const end = Math.min(lines.length, lineNo + 3);\n const filename = path;\n // Error context\n const context = lines\n .slice(start, end)\n .map((line, i) => {\n const curr = i + start + 1;\n return (curr === lineNo ? \" >> \" : \" \") + curr + \"| \" + line;\n })\n .join(\"\\n\");\n\n const header = filename\n ? filename + \":\" + lineNo + \"\\n\"\n : \"line \" + lineNo + \"\\n\";\n\n const err = new EtaRuntimeError(\n header + context + \"\\n\\n\" + originalError.message,\n );\n\n err.name = originalError.name; // the original name (e.g. ReferenceError) may be useful\n err.cause = originalError;\n\n throw err;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport type { Options } from \"./config.ts\";\nimport { EtaFileResolutionError } from \"./err.ts\";\nimport type { Eta as EtaCore } from \"./internal.ts\";\n\nexport function readFile(this: EtaCore, path: string): string {\n let res = \"\";\n\n try {\n res = fs.readFileSync(path, \"utf8\");\n // biome-ignore lint/suspicious/noExplicitAny: it's an error\n } catch (err: any) {\n if (err?.code === \"ENOENT\") {\n throw new EtaFileResolutionError(`Could not find template: ${path}`);\n } else {\n throw err;\n }\n }\n\n return res;\n}\n\nexport function resolvePath(\n this: EtaCore,\n templatePath: string,\n options?: Partial<Options>,\n): string {\n let resolvedFilePath = \"\";\n\n const views = this.config.views;\n\n if (!views) {\n throw new EtaFileResolutionError(\"Views directory is not defined\");\n }\n\n const baseFilePath = options?.filepath;\n const defaultExtension =\n this.config.defaultExtension === undefined\n ? \".eta\"\n : this.config.defaultExtension;\n\n // how we index cached template paths\n const cacheIndex = JSON.stringify({\n filename: baseFilePath, // filename of the template which called includeFile()\n path: templatePath,\n views: this.config.views,\n });\n\n templatePath += path.extname(templatePath) ? \"\" : defaultExtension;\n\n // if the file was included from another template\n if (baseFilePath) {\n // check the cache\n\n if (this.config.cacheFilepaths && this.filepathCache[cacheIndex]) {\n return this.filepathCache[cacheIndex];\n }\n\n const absolutePathTest = absolutePathRegExp.exec(templatePath);\n\n if (absolutePathTest?.length) {\n const formattedPath = templatePath.replace(/^\\/*|^\\\\*/, \"\");\n resolvedFilePath = path.join(views, formattedPath);\n } else {\n resolvedFilePath = path.join(path.dirname(baseFilePath), templatePath);\n }\n } else {\n resolvedFilePath = path.join(views, templatePath);\n }\n\n if (dirIsChild(views, resolvedFilePath)) {\n // add resolved path to the cache\n if (baseFilePath && this.config.cacheFilepaths) {\n this.filepathCache[cacheIndex] = resolvedFilePath;\n }\n\n return resolvedFilePath;\n } else {\n throw new EtaFileResolutionError(\n `Template '${templatePath}' is not in the views directory`,\n );\n }\n}\n\nfunction dirIsChild(parent: string, dir: string) {\n const relative = path.relative(parent, dir);\n return relative && !relative.startsWith(\"..\") && !path.isAbsolute(relative);\n}\n\nconst absolutePathRegExp = /^\\\\|^\\//;\n","import type { EtaConfig, Options } from \"./config.ts\";\nimport { EtaParseError } from \"./err.ts\";\nimport type { Eta } from \"./internal.ts\";\n\nexport type TemplateFunction = (\n this: Eta,\n data?: object,\n options?: Partial<Options>,\n) => string;\n/* END TYPES */\n\n/* istanbul ignore next */\nconst AsyncFunction = (async () => {}).constructor;\n\n/**\n * Takes a template string and returns a template function that can be called with (data, config)\n *\n * @param str - The template string\n * @param config - A custom configuration object (optional)\n */\n\nexport function compile(\n this: Eta,\n str: string,\n options?: Partial<Options>,\n): TemplateFunction {\n const config: EtaConfig = this.config;\n\n /* ASYNC HANDLING */\n // code gratefully taken from https://github.com/mde/ejs and adapted\n const ctor = options?.async\n ? (AsyncFunction as FunctionConstructor)\n : Function;\n /* END ASYNC HANDLING */\n\n try {\n return new ctor(\n config.varName,\n \"options\",\n this.compileToString.call(this, str, options),\n ) as TemplateFunction; // eslint-disable-line no-new-func\n } catch (e) {\n if (e instanceof SyntaxError) {\n throw new EtaParseError(\n \"Bad template syntax\\n\\n\" +\n e.message +\n \"\\n\" +\n Array(e.message.length + 1).join(\"=\") +\n \"\\n\" +\n this.compileToString.call(this, str, options) +\n \"\\n\", // This will put an extra newline before the callstack for extra readability\n );\n } else {\n throw e;\n }\n }\n}\n","import type { Options } from \"./config.ts\";\nimport type { Eta } from \"./internal.ts\";\nimport type { AstObject } from \"./parse.ts\";\n\n/**\n * Compiles a template string to a function string. Most often users just use `compile()`, which calls `compileToString` and creates a new function using the result\n */\n\nexport function compileToString(\n this: Eta,\n str: string,\n options?: Partial<Options>,\n): string {\n const config = this.config;\n const isAsync = options?.async;\n\n const compileBody = this.compileBody;\n\n const buffer: Array<AstObject> = this.parse.call(this, str);\n\n // note: when the include function passes through options, the only parameter that matters is the filepath parameter\n let res = `${config.functionHeader}\nlet include = (__eta_t, __eta_d) => this.render(__eta_t, {...${config.varName}, ...(__eta_d ?? {})}, options);\nlet includeAsync = (__eta_t, __eta_d) => this.renderAsync(__eta_t, {...${config.varName}, ...(__eta_d ?? {})}, options);\n\nlet __eta = {res: \"\", e: this.config.escapeFunction, f: this.config.filterFunction, blocks: {}${\n config.debug\n ? ', line: 1, templateStr: \"' +\n str.replace(/\\\\|\"/g, \"\\\\$&\").replace(/\\r\\n|\\n|\\r/g, \"\\\\n\") +\n '\"'\n : \"\"\n }};\n\nfunction layout(path, data) {\n __eta.layout = path;\n __eta.layoutData = data;\n}${config.debug ? \"try {\" : \"\"}${\n config.useWith ? \"with(\" + config.varName + \"||{}){\" : \"\"\n }\n\nfunction ${config.outputFunctionName}(s){__eta.res+=s;}\nfunction capture(fn){const s=__eta.res;__eta.res='';try{fn();return __eta.res}finally{__eta.res=s;}}\nasync function captureAsync(fn){const s=__eta.res;__eta.res='';try{await fn();return __eta.res}finally{__eta.res=s;}}\nfunction block(name,fn){if(__eta.layout){if(fn){__eta.blocks[name]=capture(fn);}return '';}const b=${config.varName}.__blocks||{};if(name in b){return b[name];}return fn?capture(fn):'';}\nasync function blockAsync(name,fn){if(__eta.layout){if(fn){__eta.blocks[name]=await captureAsync(fn);}return '';}const b=${config.varName}.__blocks||{};if(name in b){return b[name];}return fn?await captureAsync(fn):'';}\n\n${compileBody.call(this, buffer)}\nif (__eta.layout) {\n __eta.res = ${\n isAsync ? \"await includeAsync\" : \"include\"\n } (__eta.layout, {...${\n config.varName\n }, body: __eta.res, ...__eta.layoutData, __blocks: __eta.blocks});\n}\n${config.useWith ? \"}\" : \"\"}${\n config.debug\n ? \"} catch (e) { this.RuntimeErr(e, __eta.templateStr, __eta.line, options.filepath) }\"\n : \"\"\n}\nreturn __eta.res;\n`;\n\n if (config.plugins) {\n for (let i = 0; i < config.plugins.length; i++) {\n const plugin = config.plugins[i];\n if (plugin.processFnString) {\n res = plugin.processFnString(res, config);\n }\n }\n }\n\n return res;\n}\n\n/**\n * Loops through the AST generated by `parse` and transform each item into JS calls\n *\n * **Example**\n *\n * ```js\n * let templateAST = ['Hi ', { val: 'it.name', t: 'i' }]\n * compileBody.call(Eta, templateAST)\n * // => \"__eta.res+='Hi '\\n__eta.res+=__eta.e(it.name)\\n\"\n * ```\n */\n\nexport function compileBody(this: Eta, buff: Array<AstObject>): string {\n const config = this.config;\n\n let i = 0;\n const buffLength = buff.length;\n let returnStr = \"\";\n\n for (i; i < buffLength; i++) {\n const currentBlock = buff[i];\n if (typeof currentBlock === \"string\") {\n const str = currentBlock;\n\n // we know string exists\n returnStr += \"__eta.res+='\" + str + \"';\\n\";\n } else {\n const type = currentBlock.t; // \"r\", \"e\", \"i\" or custom tag name\n let content = currentBlock.val || \"\";\n\n if (config.debug) returnStr += \"__eta.line=\" + currentBlock.lineNo + \"\\n\";\n\n if (type === \"r\") {\n // raw\n\n if (config.autoFilter) {\n content = \"__eta.f(\" + content + \")\";\n }\n\n returnStr += \"__eta.res+=\" + content + \";\\n\";\n } else if (type === \"i\") {\n // interpolate\n\n if (config.autoFilter) {\n content = \"__eta.f(\" + content + \")\";\n }\n\n if (config.autoEscape) {\n content = \"__eta.e(\" + content + \")\";\n }\n\n returnStr += \"__eta.res+=\" + content + \";\\n\";\n } else if (type === \"e\") {\n // execute\n returnStr += content + \"\\n\";\n } else if (Object.hasOwn(config.customTags, type)) {\n returnStr += `__eta.res+=this.config.customTags[${JSON.stringify(type)}](${JSON.stringify(content)},${config.varName});\\n`;\n }\n }\n }\n\n return returnStr;\n}\n","import type { EtaConfig } from \"./config.ts\";\n\n/**\n * Takes a string within a template and trims it, based on the preceding tag's whitespace control and `config.autoTrim`\n */\n\nexport function trimWS(\n str: string,\n config: EtaConfig,\n wsLeft: string | false,\n wsRight?: string | false,\n): string {\n let leftTrim: string | false;\n let rightTrim: string | false;\n\n if (Array.isArray(config.autoTrim)) {\n // Slightly confusing,\n // but _}} will trim the left side of the following string\n leftTrim = config.autoTrim[1];\n rightTrim = config.autoTrim[0];\n } else {\n leftTrim = rightTrim = config.autoTrim;\n }\n\n if (wsLeft || wsLeft === false) {\n leftTrim = wsLeft;\n }\n\n if (wsRight || wsRight === false) {\n rightTrim = wsRight;\n }\n\n if (!rightTrim && !leftTrim) {\n return str;\n }\n\n if (leftTrim === \"slurp\" && rightTrim === \"slurp\") {\n return str.trim();\n }\n\n if (leftTrim === \"_\" || leftTrim === \"slurp\") {\n // full slurp\n str = str.trimStart();\n } else if (leftTrim === \"-\" || leftTrim === \"nl\") {\n // nl trim\n str = str.replace(/^(?:\\r\\n|\\n|\\r)/, \"\");\n }\n\n if (rightTrim === \"_\" || rightTrim === \"slurp\") {\n // full slurp\n str = str.trimEnd();\n } else if (rightTrim === \"-\" || rightTrim === \"nl\") {\n // nl trim\n str = str.replace(/(?:\\r\\n|\\n|\\r)$/, \"\");\n }\n\n return str;\n}\n\n/**\n * A map of special HTML characters to their XML-escaped equivalents\n */\n\nconst escMap: { [key: string]: string } = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n};\n\nfunction replaceChar(s: string): string {\n return escMap[s];\n}\n\n/**\n * XML-escapes an input value after converting it to a string\n *\n * @param str - Input value (usually a string)\n * @returns XML-escaped string\n */\n\nexport function XMLEscape(str: unknown): string {\n // To deal with XSS. Based on Escape implementations of Mustache.JS and Marko, then customized.\n const newStr = String(str);\n if (/[&<>\"']/.test(newStr)) {\n return newStr.replace(/[&<>\"']/g, replaceChar);\n } else {\n return newStr;\n }\n}\n","import type { AstObject } from \"./parse.ts\";\nimport { XMLEscape } from \"./utils.ts\";\n\ntype trimConfig = \"nl\" | \"slurp\" | false;\n\nexport interface Options {\n /** Compile to async function */\n async?: boolean;\n\n /** Absolute path to template file */\n filepath?: string;\n}\n\nexport interface EtaConfig {\n /** Whether or not to automatically XML-escape interpolations. Default true */\n autoEscape: boolean;\n\n /** Apply a filter function defined on the class to every interpolation or raw interpolation */\n autoFilter: boolean;\n\n /** Configure automatic whitespace trimming. Default `[false, 'nl']` */\n autoTrim: trimConfig | [trimConfig, trimConfig];\n\n /** Whether or not to cache templates if `name` or `filename` is passed */\n cache: boolean;\n\n /** Holds cache of resolved filepaths. Set to `false` to disable. */\n cacheFilepaths: boolean;\n\n /** Object specifying custom tags. Keys are tag prefixes, values are functions which take tag content and return a string. */\n customTags: Record<string, (content: string, data: unknown) => string>;\n\n /** Whether to pretty-format error messages (introduces runtime penalties) */\n debug: boolean;\n\n /** Function to XML-sanitize interpolations */\n escapeFunction: (str: unknown) => string;\n\n /** Function applied to all interpolations when autoFilter is true */\n filterFunction: (val: unknown) => string;\n\n /** Name of the function that can be used in template code to output text to the result (like EJS's `outputFunctionName`). */\n outputFunctionName: string;\n\n /** Raw JS code inserted in the template function. Useful for declaring global variables for user templates */\n functionHeader: string;\n\n /** Parsing options */\n parse: {\n /** Which prefix to use for evaluation. Default `\"\"`, does not support `\"-\"` or `\"_\"` */\n exec: string;\n\n /** Which prefix to use for interpolation. Default `\"=\"`, does not support `\"-\"` or `\"_\"` */\n interpolate: string;\n\n /** Which prefix to use for raw interpolation. Default `\"~\"`, does not support `\"-\"` or `\"_\"` */\n raw: string;\n };\n\n /** Array of plugins */\n plugins: Array<{\n processFnString?: (fnString: string, env?: EtaConfig) => string;\n processAST?: (ast: AstObject[], env?: EtaConfig) => AstObject[];\n processTemplate?: (fnString: string, env?: EtaConfig) => string;\n }>;\n\n /** Remove empty lines and whitespace between lines */\n rmWhitespace: boolean;\n\n /** Delimiters: by default `['<%', '%>']` */\n tags: [string, string];\n\n /** Make data available on the global object instead of varName */\n useWith: boolean;\n\n /** Name of the data object. Default `it` */\n varName: string;\n\n /** Directory that contains templates */\n views?: string;\n\n /** Control template file extension defaults. Default `.eta` */\n defaultExtension?: string;\n}\n\n/* END TYPES */\n\n/** Eta's base (global) configuration */\nconst defaultConfig: EtaConfig = {\n autoEscape: true,\n autoFilter: false,\n autoTrim: [false, \"nl\"],\n cache: false,\n cacheFilepaths: true,\n customTags: {},\n debug: false,\n escapeFunction: XMLEscape,\n // default filter function (not used unless enables) just stringifies the input\n filterFunction: (val) => String(val),\n outputFunctionName: \"output\",\n functionHeader: \"\",\n parse: {\n exec: \"\",\n interpolate: \"=\",\n raw: \"~\",\n },\n plugins: [],\n rmWhitespace: false,\n tags: [\"<%\", \"%>\"],\n useWith: false,\n varName: \"it\",\n defaultExtension: \".eta\",\n};\n\nexport { defaultConfig };\n","import { ParseErr } from \"./err.ts\";\nimport type { Eta } from \"./internal.ts\";\nimport { trimWS } from \"./utils.ts\";\n\nexport interface TemplateObject {\n t: string;\n val: string;\n lineNo?: number;\n}\n\nexport type AstObject = string | TemplateObject;\n\n/* END TYPES */\n\nconst templateLitReg =\n /`(?:\\\\[\\s\\S]|\\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})*}|(?!\\${)[^\\\\`])*`/g;\n\nconst singleQuoteReg = /'(?:\\\\[\\s\\w\"'\\\\`]|[^\\n\\r'\\\\])*?'/g;\n\nconst doubleQuoteReg = /\"(?:\\\\[\\s\\w\"'\\\\`]|[^\\n\\r\"\\\\])*?\"/g;\n\n/** Escape special regular expression characters inside a string */\n\nfunction escapeRegExp(string: string) {\n // From MDN\n return string.replace(/[.*+\\-?^${}()|[\\]\\\\]/g, \"\\\\$&\"); // $& means the whole matched string\n}\n\nfunction getLineNo(str: string, index: number) {\n return str.slice(0, index).split(\"\\n\").length;\n}\n\nexport function parse(this: Eta, str: string): Array<AstObject> {\n const config = this.config;\n\n let buffer: Array<AstObject> = [];\n let trimLeftOfNextStr: string | false = false;\n let lastIndex = 0;\n const parseOptions = config.parse;\n\n const customTagPrefixes = Object.keys(config.customTags);\n\n if (config.plugins) {\n for (let i = 0; i < config.plugins.length; i++) {\n const plugin = config.plugins[i];\n if (plugin.processTemplate) {\n str = plugin.processTemplate(str, config);\n }\n }\n }\n\n /* Adding for EJS compatibility */\n if (config.rmWhitespace) {\n // Code taken directly from EJS\n // Have to use two separate replaces here as `^` and `$` operators don't\n // work well with `\\r` and empty lines don't work well with the `m` flag.\n // Essentially, this replaces the whitespace at the beginning and end of\n // each line and removes multiple newlines.\n str = str.replace(/[\\r\\n]+/g, \"\\n\").replace(/^\\s+|\\s+$/gm, \"\");\n }\n /* End rmWhitespace option */\n\n templateLitReg.lastIndex = 0;\n singleQuoteReg.lastIndex = 0;\n doubleQuoteReg.lastIndex = 0;\n\n function pushString(strng: string, shouldTrimRightOfString?: string | false) {\n if (strng) {\n // if string is truthy it must be of type 'string'\n\n strng = trimWS(\n strng,\n config,\n trimLeftOfNextStr, // this will only be false on the first str, the next ones will be null or undefined\n shouldTrimRightOfString,\n );\n\n if (strng) {\n // replace \\ with \\\\, ' with \\'\n // we're going to convert all CRLF to LF so it doesn't take more than one replace\n\n strng = strng.replace(/\\\\|'/g, \"\\\\$&\").replace(/\\r\\n|\\n|\\r/g, \"\\\\n\");\n\n buffer.push(strng);\n }\n }\n }\n\n const prefixes = [\n parseOptions.exec,\n parseOptions.interpolate,\n parseOptions.raw,\n ...customTagPrefixes,\n ].reduce((accumulator, prefix) => {\n if (accumulator && prefix) {\n return accumulator + \"|\" + escapeRegExp(prefix);\n } else if (prefix) {\n // accumulator is falsy\n return escapeRegExp(prefix);\n } else {\n // prefix and accumulator are both falsy\n return accumulator;\n }\n }, \"\");\n\n const parseOpenReg = new RegExp(\n escapeRegExp(config.tags[0]) + \"(-|_)?\\\\s*(\" + prefixes + \")?\\\\s*\",\n \"g\",\n );\n\n const parseCloseReg = new RegExp(\n \"'|\\\"|`|\\\\/\\\\*|(\\\\s*(-|_)?\" + escapeRegExp(config.tags[1]) + \")\",\n \"g\",\n );\n\n let m: RegExpExecArray | null;\n\n // biome-ignore lint/suspicious/noAssignInExpressions: this is performant\n while ((m = parseOpenReg.exec(str))) {\n const precedingString = str.slice(lastIndex, m.index);\n\n lastIndex = m[0].length + m.index;\n\n const wsLeft = m[1];\n const prefix = m[2] || \"\"; // by default either ~, =, or empty\n\n pushString(precedingString, wsLeft);\n\n parseCloseReg.lastIndex = lastIndex;\n let closeTag: RegExpExecArray | null;\n let currentObj: AstObject | false = false;\n\n // biome-ignore lint/suspicious/noAssignInExpressions: this is performant\n while ((closeTag = parseCloseReg.exec(str))) {\n if (closeTag[1]) {\n const content = str.slice(lastIndex, closeTag.index);\n\n parseOpenReg.lastIndex = lastIndex = parseCloseReg.lastIndex;\n\n trimLeftOfNextStr = closeTag[2];\n\n const currentType: string =\n prefix === parseOptions.exec\n ? \"e\"\n : prefix === parseOptions.raw\n ? \"r\"\n : prefix === parseOptions.interpolate\n ? \"i\"\n : customTagPrefixes.includes(prefix)\n ? prefix\n : \"\";\n\n currentObj = { t: currentType, val: content };\n break;\n } else {\n const char = closeTag[0];\n if (char === \"/*\") {\n const commentCloseInd = str.indexOf(\"*/\", parseCloseReg.lastIndex);\n\n if (commentCloseInd === -1) {\n ParseErr(\"unclosed comment\", str, closeTag.index);\n }\n parseCloseReg.lastIndex = commentCloseInd;\n } else if (char === \"'\") {\n singleQuoteReg.lastIndex = closeTag.index;\n\n const singleQuoteMatch = singleQuoteReg.exec(str);\n if (singleQuoteMatch) {\n parseCloseReg.lastIndex = singleQuoteReg.lastIndex;\n } else {\n ParseErr(\"unclosed string\", str, closeTag.index);\n }\n } else if (char === '\"') {\n doubleQuoteReg.lastIndex = closeTag.index;\n const doubleQuoteMatch = doubleQuoteReg.exec(str);\n\n if (doubleQuoteMatch) {\n parseCloseReg.lastIndex = doubleQuoteReg.lastIndex;\n } else {\n ParseErr(\"unclosed string\", str, closeTag.index);\n }\n } else if (char === \"`\") {\n templateLitReg.lastIndex = closeTag.index;\n const templateLitMatch = templateLitReg.exec(str);\n if (templateLitMatch) {\n parseCloseReg.lastIndex = templateLitReg.lastIndex;\n } else {\n ParseErr(\"unclosed string\", str, closeTag.index);\n }\n }\n }\n }\n if (currentObj) {\n if (config.debug) {\n currentObj.lineNo = getLineNo(str, m.index);\n }\n buffer.push(currentObj);\n } else {\n ParseErr(\"unclosed tag\", str, m.index);\n }\n }\n\n pushString(str.slice(lastIndex, str.length), false);\n\n if (config.plugins) {\n for (let i = 0; i < config.plugins.length; i++) {\n const plugin = config.plugins[i];\n if (plugin.processAST) {\n buffer = plugin.processAST(buffer, config);\n }\n }\n }\n\n return buffer;\n}\n","import type { TemplateFunction } from \"./compile.ts\";\n\n/* TYPES */\nimport type { Options } from \"./config.ts\";\nimport { EtaNameResolutionError } from \"./err.ts\";\nimport type { Eta } from \"./internal.ts\";\n\n/* END TYPES */\n\nfunction handleCache(\n this: Eta,\n template: string,\n options: Partial<Options>,\n): TemplateFunction {\n const templateStore = options?.async\n ? this.templatesAsync\n : this.templatesSync;\n\n if (this.resolvePath && this.readFile && !template.startsWith(\"@\")) {\n const templatePath = options.filepath as string;\n\n const cachedTemplate = templateStore.get(templatePath);\n\n if (this.config.cache && cachedTemplate) {\n return cachedTemplate;\n } else {\n const templateString = this.readFile(templatePath);\n\n const templateFn = this.compile(templateString, options);\n\n if (this.config.cache) templateStore.define(templatePath, templateFn);\n\n return templateFn;\n }\n } else {\n const cachedTemplate = templateStore.get(template);\n\n if (cachedTemplate) {\n return cachedTemplate;\n } else {\n throw new EtaNameResolutionError(`Failed to get template '${template}'`);\n }\n }\n}\n\nexport function render<T extends object>(\n this: Eta,\n template: string | TemplateFunction, // template name or template function\n data: T,\n meta?: { filepath: string },\n): string {\n let templateFn: TemplateFunction;\n const options = { ...meta, async: false };\n\n if (typeof template === \"string\") {\n if (this.resolvePath && this.readFile && !template.startsWith(\"@\")) {\n options.filepath = this.resolvePath(template, options);\n }\n\n templateFn = handleCache.call(this, template, options);\n } else {\n templateFn = template;\n }\n\n const res = templateFn.call(this, data, options);\n\n return res;\n}\n\nexport function renderAsync<T extends object>(\n this: Eta,\n template: string | TemplateFunction, // template name or template function\n data: T,\n meta?: { filepath: string },\n): Promise<string> {\n let templateFn: TemplateFunction;\n const options = { ...meta, async: true };\n\n if (typeof template === \"string\") {\n if (this.resolvePath && this.readFile && !template.startsWith(\"@\")) {\n options.filepath = this.resolvePath(template, options);\n }\n\n templateFn = handleCache.call(this, template, options);\n } else {\n templateFn = template;\n }\n\n const res = templateFn.call(this, data, options);\n\n // Return a promise\n return Promise.resolve(res);\n}\n\nexport function renderString<T extends object>(\n this: Eta,\n template: string,\n data: T,\n): string {\n const templateFn = this.compile(template, { async: false });\n\n return render.call(this, templateFn, data);\n}\n\nexport function renderStringAsync<T extends object>(\n this: Eta,\n template: string,\n data: T,\n): Promise<string> {\n const templateFn = this.compile(template, { async: true });\n\n return renderAsync.call(this, templateFn, data);\n}\n","/**\n * Handles storage and accessing of values\n *\n * In this case, we use it to store compiled template functions\n * Indexed by their `name` or `filename`\n */\n\nexport class Cacher<T> {\n constructor(private cache: Record<string, T>) {}\n define(key: string, val: T): void {\n this.cache[key] = val;\n }\n get(key: string): T {\n return this.cache[key];\n }\n remove(key: string): void {\n delete this.cache[key];\n }\n reset(): void {\n this.cache = {};\n }\n load(cacheObj: Record<string, T>): void {\n this.cache = { ...this.cache, ...cacheObj };\n }\n}\n","import type { TemplateFunction } from \"./compile.ts\";\nimport { compile } from \"./compile.ts\";\nimport { compileBody, compileToString } from \"./compile-string.ts\";\nimport type { EtaConfig, Options } from \"./config.ts\";\nimport { defaultConfig } from \"./config.ts\";\nimport { EtaError, RuntimeErr } from \"./err.ts\";\nimport { parse } from \"./parse.ts\";\nimport {\n render,\n renderAsync,\n renderString,\n renderStringAsync,\n} from \"./render.ts\";\nimport { Cacher } from \"./storage.ts\";\n\nexport class Eta {\n constructor(customConfig?: Partial<EtaConfig>) {\n if (customConfig) {\n this.config = { ...defaultConfig, ...customConfig };\n } else {\n this.config = { ...defaultConfig };\n }\n\n const reserved = [\n this.config.parse.exec,\n this.config.parse.interpolate,\n this.config.parse.raw,\n \"-\",\n \"_\",\n ];\n\n for (const prefix of Object.keys(this.config.customTags)) {\n if (reserved.includes(prefix)) {\n throw new EtaError(\n `Custom tag prefix \"${prefix}\" conflicts with a built-in prefix`,\n );\n }\n }\n }\n\n config: EtaConfig;\n\n RuntimeErr = RuntimeErr;\n\n compile = compile;\n compileToString = compileToString;\n compileBody = compileBody;\n parse = parse;\n render = render;\n renderAsync = renderAsync;\n renderString = renderString;\n renderStringAsync = renderStringAsync;\n\n filepathCache: Record<string, string> = {};\n templatesSync: Cacher<TemplateFunction> = new Cacher<TemplateFunction>({});\n templatesAsync: Cacher<TemplateFunction> = new Cacher<TemplateFunction>({});\n\n // resolvePath takes a relative path from the \"views\" directory\n resolvePath:\n | null\n | ((this: Eta, template: string, options?: Partial<Options>) => string) =\n null;\n readFile: null | ((this: Eta, path: string) => string) = null;\n\n // METHODS\n\n configure(customConfig: Partial<EtaConfig>) {\n this.config = { ...this.config, ...customConfig };\n }\n\n withConfig(customConfig: Partial<EtaConfig>): this & { config: EtaConfig } {\n return { ...this, config: { ...this.config, ...customConfig } };\n }\n\n loadTemplate(\n name: string,\n template: string | TemplateFunction, // template string or template function\n options?: { async: boolean },\n ): void {\n if (typeof template === \"string\") {\n const templates = options?.async\n ? this.templatesAsync\n : this.templatesSync;\n\n templates.define(name, this.compile(template, options));\n } else {\n let templates = this.templatesSync;\n\n if (template.constructor.name === \"AsyncFunction\" || options?.async) {\n templates = this.templatesAsync;\n }\n\n templates.define(name, template);\n }\n }\n}\n\n// for instance checking against thrown errors\nexport { EtaError };\n","import { readFile, resolvePath } from \"./file-handling.ts\";\nimport { Eta as EtaCore } from \"./internal.ts\";\n\nexport type { TemplateFunction } from \"./compile.ts\";\nexport type { EtaConfig, Options } from \"./config.ts\";\nexport {\n EtaError,\n EtaFileResolutionError,\n EtaNameResolutionError,\n EtaParseError,\n EtaRuntimeError,\n} from \"./err.ts\";\n\nexport class Eta extends EtaCore {\n readFile = readFile;\n\n resolvePath = resolvePath;\n}\n"],"mappings":"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"}