Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

eta

Package Overview
Dependencies
Maintainers
1
Versions
52
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eta - npm Package Compare versions

Comparing version 1.2.2 to 1.3.0

dist/types/file-methods.d.ts

276

dist/browser/eta.dev.js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.Eta = {}));
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Eta = {}));
}(this, (function (exports) { 'use strict';
function setPrototypeOf(obj, proto) {
// eslint-disable-line @typescript-eslint/no-explicit-any
if (Object.setPrototypeOf) {

@@ -15,2 +16,15 @@ Object.setPrototypeOf(obj, proto);

}
// This is pretty much the only way to get nice, extended Errors
// without using ES6
/**
* This returns a new Error with a custom prototype. Note that it's _not_ a constructor
*
* @param message Error message
*
* **Example**
*
* ```js
* throw EtaErr("template not found")
* ```
*/
function EtaErr(message) {

@@ -24,3 +38,5 @@ var err = new Error(message);

});
// TODO: Class transpilation adds a lot to the bundle size
/**
* Throws an EtaErr with a nicely formatted error and message showing where in the template the error occurred.
*/
function ParseErr(message, str, indx) {

@@ -45,10 +61,62 @@ var whitespace = str.slice(0, indx).split(/\n/);

/**
* @returns The global Promise function
*/
var promiseImpl = new Function('return this')().Promise;
/**
* @returns A new AsyncFunction constuctor
*/
function getAsyncFunctionConstructor() {
try {
return new Function('return (async function(){}).constructor')();
}
catch (e) {
if (e instanceof SyntaxError) {
throw EtaErr("This environment doesn't support async/await");
}
else {
throw e;
}
}
}
/**
* str.trimLeft polyfill
*
* @param str - Input string
* @returns The string with left whitespace removed
*
*/
function trimLeft(str) {
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimLeft) {
return str.trimLeft();
}
else {
return str.replace(/^\s+/, '');
}
}
/**
* str.trimRight polyfill
*
* @param str - Input string
* @returns The string with right whitespace removed
*
*/
function trimRight(str) {
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimRight) {
return str.trimRight();
}
else {
return str.replace(/\s+$/, ''); // TODO: do we really need to replace BOM's?
}
}
// TODO: allow '-' to trim up until newline. Use [^\S\n\r] instead of \s
// TODO: only include trimLeft polyfill if not in ES6
/* END TYPES */
var promiseImpl = new Function('return this;')().Promise;
function hasOwnProp(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function copyProps(toObj, fromObj, notConfig) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function copyProps(toObj, fromObj) {
for (var key in fromObj) {

@@ -61,13 +129,16 @@ if (hasOwnProp(fromObj, key)) {

}
function trimWS(str, env, wsLeft, wsRight) {
/**
* Takes a string within a template and trims it, based on the preceding tag's whitespace control and `config.autoTrim`
*/
function trimWS(str, config, wsLeft, wsRight) {
var leftTrim;
var rightTrim;
if (Array.isArray(env.autoTrim)) {
if (Array.isArray(config.autoTrim)) {
// kinda confusing
// but _}} will trim the left side of the following string
leftTrim = env.autoTrim[1];
rightTrim = env.autoTrim[0];
leftTrim = config.autoTrim[1];
rightTrim = config.autoTrim[0];
}
else {
leftTrim = rightTrim = env.autoTrim;
leftTrim = rightTrim = config.autoTrim;
}

@@ -89,12 +160,5 @@ if (wsLeft || wsLeft === false) {

// full slurp
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimLeft) {
str = str.trimLeft();
}
else {
str = str.replace(/^[\s\uFEFF\xA0]+/, '');
}
str = trimLeft(str);
}
else if (leftTrim === '-' || leftTrim === 'nl') {
// console.log('trimming left nl' + leftTrim)
// nl trim

@@ -104,14 +168,6 @@ str = str.replace(/^(?:\r\n|\n|\r)/, '');

if (rightTrim === '_' || rightTrim === 'slurp') {
// console.log('trimming right' + rightTrim)
// full slurp
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimRight) {
str = str.trimRight();
}
else {
str = str.replace(/[\s\uFEFF\xA0]+$/, '');
}
str = trimRight(str);
}
else if (rightTrim === '-' || rightTrim === 'nl') {
// console.log('trimming right nl' + rightTrim)
// nl trim

@@ -122,2 +178,5 @@ str = str.replace(/(?:\r\n|\n|\r)$/, ''); // TODO: make sure this gets \r\n

}
/**
* A map of special HTML characters to their XML-escaped equivalents
*/
var escMap = {

@@ -128,3 +187,3 @@ '&': '&',

'"': '"',
"'": '''
"'": ''',
};

@@ -134,3 +193,10 @@ function replaceChar(s) {

}
/**
* XML-escapes an input value after converting it to a string
*
* @param str - Input value (usually a string)
* @returns XML-escaped string
*/
function XMLEscape(str) {
// eslint-disable-line @typescript-eslint/no-explicit-any
// To deal with XSS. Based on Escape implementations of Mustache.JS and Marko, then customized.

@@ -150,2 +216,3 @@ var newStr = String(str);

var doubleQuoteReg = /"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g;
/** Escape special regular expression characters inside a string */
function escapeRegExp(string) {

@@ -176,3 +243,2 @@ // From MDN

// if string is truthy it must be of type 'string'
// TODO: benchmark replace( /(\\|')/g, '\\$1')
strng = trimWS(strng, env, trimLeftOfNextStr, // this will only be false on the first str, the next ones will be null or undefined

@@ -182,4 +248,4 @@ shouldTrimRightOfString);

// replace \ with \\, ' with \'
// we're going to convert all CRLF to LF so it doesn't take more than one replace
strng = strng.replace(/\\|'/g, '\\$&').replace(/\r\n|\n|\r/g, '\\n');
// we're going to convert all CRLF to LF so it doesn't take more than one replace
buffer.push(strng);

@@ -207,3 +273,2 @@ }

while ((m = parseOpenReg.exec(str))) {
// TODO: check if above needs exec(str) !== null. Don't think it's possible to have 0-width matches but...
lastIndex = m[0].length + m.index;

@@ -296,5 +361,15 @@ var precedingString = m[1];

/* END TYPES */
/**
* 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
*
* **Example**
*
* ```js
* compileToString("Hi <%= it.user %>", eta.defaultConfig)
* // "var tR='';tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR"
* ```
*/
function compileToString(str, env) {
var buffer = parse(str, env);
var res = "var tR='';" +
var res = "var tR=''\n" +
(env.useWith ? 'with(' + env.varName + '||{}){' : '') +

@@ -313,4 +388,15 @@ compileScope(buffer, env) +

return res;
// TODO: is `return cb()` necessary, or could we just do `cb()`
}
/**
* Loops through the AST generated by `parse` and transform each item into JS calls
*
* **Example**
*
* ```js
* // AST version of 'Hi <%= it.user %>'
* let templateAST = ['Hi ', { val: 'it.user', t: 'i' }]
* compileScope(templateAST, eta.defaultConfig)
* // "tR+='Hi ';tR+=E.e(it.user);"
* ```
*/
function compileScope(buff, env) {

@@ -325,3 +411,3 @@ var i = 0;

// we know string exists
returnStr += "tR+='" + str + "';";
returnStr += "tR+='" + str + "'\n";
}

@@ -333,3 +419,3 @@ else {

// raw
returnStr += 'tR+=' + content + ';';
returnStr += 'tR+=' + content + '\n';
}

@@ -341,3 +427,3 @@ else if (type === 'i') {

}
returnStr += 'tR+=' + content + ';';
returnStr += 'tR+=' + content + '\n';
// reference

@@ -354,3 +440,8 @@ }

/* END TYPES */
/**
* Handles storage and accessing of values
*
* In this case, we use it to store compiled template functions
* Indexed by their `name` or `filename`
*/
var Cacher = /** @class */ (function () {

@@ -376,4 +467,2 @@ function Cacher(cache) {

Cacher.prototype.load = function (cacheObj) {
// TODO: this will err with deep objects and `storage` or `plugins` keys.
// Update Feb 26: EDITED so it shouldn't err
copyProps(this.cache, cacheObj);

@@ -385,5 +474,15 @@ };

/* END TYPES */
/**
* Eta's template storage
*
* Stores partials and cached templates
*/
var templates = new Cacher({});
/* END TYPES */
/**
* Include a template based on its name (or filepath, if it's already been cached).
*
* Called like `E.include(templateNameOrPath, data)`
*/
function includeHelper(templateNameOrPath, data) {

@@ -405,3 +504,3 @@ var template = this.templates.get(templateNameOrPath);

raw: '~',
exec: ''
exec: '',
},

@@ -414,5 +513,17 @@ async: false,

e: XMLEscape,
include: includeHelper
include: includeHelper,
};
includeHelper.bind(defaultConfig);
/**
* Takes one or two partial (not necessarily complete) configuration objects, merges them 1 layer deep into defaultConfig, and returns the result
*
* @param override Partial configuration object
* @param baseConfig Partial configuration object to merge before `override`
*
* **Example**
*
* ```js
* let customConfig = getConfig({tags: ['!#', '#!']})
* ```
*/
function getConfig(override, baseConfig) {

@@ -432,4 +543,19 @@ // TODO: run more tests on this

/* END TYPES */
function compile(str, env) {
var options = getConfig(env || {});
/**
* Takes a template string and returns a template function that can be called with (data, config, [cb])
*
* @param str - The template string
* @param config - A custom configuration object (optional)
*
* **Example**
*
* ```js
* let compiledFn = eta.compile("Hi <%= it.user %>")
* // function anonymous()
* let compiledFnStr = compiledFn.toString()
* // "function anonymous(it,E,cb\n) {\nvar tR='';tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR\n}"
* ```
*/
function compile(str, config) {
var options = getConfig(config || {});
var ctor; // constructor

@@ -439,15 +565,3 @@ /* ASYNC HANDLING */

if (options.async) {
// Have to use generated function for this, since in envs without support,
// it breaks in parsing
try {
ctor = new Function('return (async function(){}).constructor;')();
}
catch (e) {
if (e instanceof SyntaxError) {
throw EtaErr("This environment doesn't support async/await");
}
else {
throw e;
}
}
ctor = getAsyncFunctionConstructor();
}

@@ -470,3 +584,5 @@ else {

'\n' +
compileToString(str, options));
compileToString(str, options) +
'\n' // This will put an extra newline before the callstack for extra readability
);
}

@@ -491,2 +607,4 @@ else {

}
// Note that we don't have to check if it already exists in the cache;
// it would have returned earlier if it had
if (options.cache && options.name) {

@@ -497,7 +615,37 @@ options.templates.define(options.name, templateFunc);

}
function render(template, data, env, cb) {
var options = getConfig(env || {});
/**
* Render a template
*
* If `template` is a string, Eta will compile it to a function and then call it with the provided data.
* If `template` is a template function, Eta will call it with the provided data.
*
* If `config.async` is `false`, Eta will return the rendered template.
*
* If `config.async` is `true` and there's a callback function, Eta will call the callback with `(err, renderedTemplate)`.
* If `config.async` is `true` and there's not a callback function, Eta will return a Promise that resolves to the rendered template
*
* If `config.cache` is `true` and `config` has a `name` or `filename` property, Eta will cache the template on the first render and use the cached template for all subsequent renders.
*
* @param template Template string or template function
* @param data Data to render the template with
* @param config Optional config options
* @param cb Callback function
*/
function render(template, data, config, cb) {
var options = getConfig(config || {});
if (options.async) {
var result;
if (!cb) {
if (cb) {
// If user passes callback
try {
// Note: if there is an error while rendering the template,
// It will bubble up and be caught here
var templateFn = handleCache(template, options);
templateFn(data, options, cb);
}
catch (err) {
return cb(err);
}
}
else {
// No callback, try returning a promise

@@ -519,10 +667,2 @@ if (typeof promiseImpl === 'function') {

}
else {
try {
handleCache(template, options)(data, options, cb);
}
catch (err) {
return cb(err);
}
}
}

@@ -529,0 +669,0 @@ else {

@@ -1,2 +0,2 @@

!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).Eta={})}(this,(function(e){"use strict";function t(e){var n,r,a=new Error(e);return n=a,r=t.prototype,Object.setPrototypeOf?Object.setPrototypeOf(n,r):n.__proto__=r,a}function n(e,n,r){var a=n.slice(0,r).split(/\n/),i=a.length,o=a[i-1].length+1;throw t(e+=" at line "+i+" col "+o+":\n\n "+n.split(/\n/)[i-1]+"\n "+Array(o).join(" ")+"^")}t.prototype=Object.create(Error.prototype,{name:{value:"Eta Error",enumerable:!1}});var r=new Function("return this;")().Promise;function a(e,t,n){for(var r in t)a=t,i=r,Object.prototype.hasOwnProperty.call(a,i)&&(e[r]=t[r]);var a,i;return e}var i={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};function o(e){return i[e]}var s=/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})*}|(?!\${)[^\\`])*`/g,c=/'(?:\\[\s\w"'\\`]|[^\n\r'\\])*?'/g,u=/"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g;function l(e){return e.replace(/[.*+\-?^${}()|[\]\\]/g,"\\$&")}function p(e,t){var r=[],a=!1,i=0,o=t.parse;function p(e,n){e&&(e=function(e,t,n,r){var a,i;return Array.isArray(t.autoTrim)?(a=t.autoTrim[1],i=t.autoTrim[0]):a=i=t.autoTrim,(n||!1===n)&&(a=n),(r||!1===r)&&(i=r),i||a?"slurp"===a&&"slurp"===i?e.trim():("_"===a||"slurp"===a?e=String.prototype.trimLeft?e.trimLeft():e.replace(/^[\s\uFEFF\xA0]+/,""):"-"!==a&&"nl"!==a||(e=e.replace(/^(?:\r\n|\n|\r)/,"")),"_"===i||"slurp"===i?e=String.prototype.trimRight?e.trimRight():e.replace(/[\s\uFEFF\xA0]+$/,""):"-"!==i&&"nl"!==i||(e=e.replace(/(?:\r\n|\n|\r)$/,"")),e):e}(e,t,a,n))&&(e=e.replace(/\\|'/g,"\\$&").replace(/\r\n|\n|\r/g,"\\n"),r.push(e))}t.rmWhitespace&&(e=e.replace(/[\r\n]+/g,"\n").replace(/^\s+|\s+$/gm,"")),s.lastIndex=0,c.lastIndex=0,u.lastIndex=0;for(var f,d=[o.exec,o.interpolate,o.raw].reduce((function(e,t){return e&&t?e+"|"+l(t):t?l(t):e}),""),g=new RegExp("([^]*?)"+l(t.tags[0])+"(-|_)?\\s*("+d+")?\\s*","g"),h=new RegExp("'|\"|`|\\/\\*|(\\s*(-|_)?"+l(t.tags[1])+")","g");f=g.exec(e);){i=f[0].length+f.index;var x,m=f[1],v=f[2],y=f[3]||"";p(m,v),h.lastIndex=i;for(var w=!1;x=h.exec(e);){if(x[1]){var I=e.slice(i,x.index);g.lastIndex=i=h.lastIndex,a=x[2];var E="";y===o.exec?E="e":y===o.raw?E="r":y===o.interpolate&&(E="i"),w={t:E,val:I};break}var b=x[0];if("/*"===b){var F=e.indexOf("*/",h.lastIndex);-1===F&&n("unclosed comment",e,x.index),h.lastIndex=F}else if("'"===b){c.lastIndex=x.index,c.exec(e)?h.lastIndex=c.lastIndex:n("unclosed string",e,x.index)}else if('"'===b){u.lastIndex=x.index,u.exec(e)?h.lastIndex=u.lastIndex:n("unclosed string",e,x.index)}else if("`"===b){s.lastIndex=x.index,s.exec(e)?h.lastIndex=s.lastIndex:n("unclosed string",e,x.index)}}w?r.push(w):n("unclosed tag",e,f.index+m.length)}if(p(e.slice(i,e.length),!1),t.plugins)for(var S=0;S<t.plugins.length;S++){var R=t.plugins[S];R.processAST&&(r=R.processAST(r,t))}return r}function f(e,t){var n=p(e,t),r="var tR='';"+(t.useWith?"with("+t.varName+"||{}){":"")+function(e,t){var n=0,r=e.length,a="";for(;n<r;n++){var i=e[n];if("string"==typeof i){a+="tR+='"+i+"';"}else{var o=i.t,s=i.val||"";"r"===o?a+="tR+="+s+";":"i"===o?(t.autoEscape&&(s="E.e("+s+")"),a+="tR+="+s+";"):"e"===o&&(a+=s+"\n")}}return a}(n,t)+"if(cb){cb(null,tR)} return tR"+(t.useWith?"}":"");if(t.plugins)for(var a=0;a<t.plugins.length;a++){var i=t.plugins[a];i.processFnString&&(r=i.processFnString(r,t))}return r}var d=new(function(){function e(e){this.cache=e}return e.prototype.define=function(e,t){this.cache[e]=t},e.prototype.get=function(e){return this.cache[e]},e.prototype.remove=function(e){delete this.cache[e]},e.prototype.reset=function(){this.cache={}},e.prototype.load=function(e){a(this.cache,e)},e}())({});function g(e,n){var r=this.templates.get(e);if(!r)throw t('Could not fetch template "'+e+'"');return r(n,this)}var h={varName:"it",autoTrim:[!1,"nl"],rmWhitespace:!1,autoEscape:!0,tags:["<%","%>"],parse:{interpolate:"=",raw:"~",exec:""},async:!1,templates:d,cache:!1,plugins:[],useWith:!1,e:function(e){var t=String(e);return/[&<>"']/.test(t)?t.replace(/[&<>"']/g,o):t},include:g};function x(e,t){var n={};return a(n,h),t&&a(n,t),e&&a(n,e),n}function m(e,n){var r,a=x(n||{});if(a.async)try{r=new Function("return (async function(){}).constructor;")()}catch(e){throw e instanceof SyntaxError?t("This environment doesn't support async/await"):e}else r=Function;try{return new r(a.varName,"E","cb",f(e,a))}catch(n){throw n instanceof SyntaxError?t("Bad template syntax\n\n"+n.message+"\n"+Array(n.message.length+1).join("=")+"\n"+f(e,a)):n}}function v(e,t){var n;return t.cache&&t.name&&t.templates.get(t.name)?t.templates.get(t.name):(n="function"==typeof e?e:m(e,t),t.cache&&t.name&&t.templates.define(t.name,n),n)}g.bind(h),e.compile=m,e.compileToString=f,e.defaultConfig=h,e.getConfig=x,e.parse=p,e.render=function(e,n,a,i){var o=x(a||{});if(!o.async)return v(e,o)(n,o);if(!i){if("function"==typeof r)return new r((function(t,r){try{t(v(e,o)(n,o))}catch(e){r(e)}}));throw t("Please provide a callback function, this env doesn't support Promises")}try{v(e,o)(n,o,i)}catch(e){return i(e)}},e.templates=d,Object.defineProperty(e,"__esModule",{value:!0})}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Eta={})}(this,(function(e){"use strict";function t(e){var n,r,a=new Error(e);return n=a,r=t.prototype,Object.setPrototypeOf?Object.setPrototypeOf(n,r):n.__proto__=r,a}function n(e,n,r){var a=n.slice(0,r).split(/\n/),i=a.length,o=a[i-1].length+1;throw t(e+=" at line "+i+" col "+o+":\n\n "+n.split(/\n/)[i-1]+"\n "+Array(o).join(" ")+"^")}t.prototype=Object.create(Error.prototype,{name:{value:"Eta Error",enumerable:!1}});var r=new Function("return this")().Promise;function a(e,t){for(var n in t)r=t,a=n,Object.prototype.hasOwnProperty.call(r,a)&&(e[n]=t[n]);var r,a;return e}function i(e,t,n,r){var a,i;return Array.isArray(t.autoTrim)?(a=t.autoTrim[1],i=t.autoTrim[0]):a=i=t.autoTrim,(n||!1===n)&&(a=n),(r||!1===r)&&(i=r),i||a?"slurp"===a&&"slurp"===i?e.trim():("_"===a||"slurp"===a?e=function(e){return String.prototype.trimLeft?e.trimLeft():e.replace(/^\s+/,"")}(e):"-"!==a&&"nl"!==a||(e=e.replace(/^(?:\r\n|\n|\r)/,"")),"_"===i||"slurp"===i?e=function(e){return String.prototype.trimRight?e.trimRight():e.replace(/\s+$/,"")}(e):"-"!==i&&"nl"!==i||(e=e.replace(/(?:\r\n|\n|\r)$/,"")),e):e}var o={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};function s(e){return o[e]}var c=/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})*}|(?!\${)[^\\`])*`/g,u=/'(?:\\[\s\w"'\\`]|[^\n\r'\\])*?'/g,l=/"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g;function p(e){return e.replace(/[.*+\-?^${}()|[\]\\]/g,"\\$&")}function f(e,t){var r=[],a=!1,o=0,s=t.parse;function f(e,n){e&&(e=i(e,t,a,n))&&(e=e.replace(/\\|'/g,"\\$&").replace(/\r\n|\n|\r/g,"\\n"),r.push(e))}t.rmWhitespace&&(e=e.replace(/[\r\n]+/g,"\n").replace(/^\s+|\s+$/gm,"")),c.lastIndex=0,u.lastIndex=0,l.lastIndex=0;for(var d,g=[s.exec,s.interpolate,s.raw].reduce((function(e,t){return e&&t?e+"|"+p(t):t?p(t):e}),""),h=new RegExp("([^]*?)"+p(t.tags[0])+"(-|_)?\\s*("+g+")?\\s*","g"),m=new RegExp("'|\"|`|\\/\\*|(\\s*(-|_)?"+p(t.tags[1])+")","g");d=h.exec(e);){o=d[0].length+d.index;var v,x=d[1],y=d[2],w=d[3]||"";f(x,y),m.lastIndex=o;for(var I=!1;v=m.exec(e);){if(v[1]){var b=e.slice(o,v.index);h.lastIndex=o=m.lastIndex,a=v[2];var E="";w===s.exec?E="e":w===s.raw?E="r":w===s.interpolate&&(E="i"),I={t:E,val:b};break}var S=v[0];if("/*"===S){var T=e.indexOf("*/",m.lastIndex);-1===T&&n("unclosed comment",e,v.index),m.lastIndex=T}else if("'"===S){u.lastIndex=v.index,u.exec(e)?m.lastIndex=u.lastIndex:n("unclosed string",e,v.index)}else if('"'===S){l.lastIndex=v.index,l.exec(e)?m.lastIndex=l.lastIndex:n("unclosed string",e,v.index)}else if("`"===S){c.lastIndex=v.index,c.exec(e)?m.lastIndex=c.lastIndex:n("unclosed string",e,v.index)}}I?r.push(I):n("unclosed tag",e,d.index+x.length)}if(f(e.slice(o,e.length),!1),t.plugins)for(var R=0;R<t.plugins.length;R++){var _=t.plugins[R];_.processAST&&(r=_.processAST(r,t))}return r}function d(e,t){var n=f(e,t),r="var tR=''\n"+(t.useWith?"with("+t.varName+"||{}){":"")+function(e,t){var n=0,r=e.length,a="";for(;n<r;n++){var i=e[n];if("string"==typeof i){a+="tR+='"+i+"'\n"}else{var o=i.t,s=i.val||"";"r"===o?a+="tR+="+s+"\n":"i"===o?(t.autoEscape&&(s="E.e("+s+")"),a+="tR+="+s+"\n"):"e"===o&&(a+=s+"\n")}}return a}(n,t)+"if(cb){cb(null,tR)} return tR"+(t.useWith?"}":"");if(t.plugins)for(var a=0;a<t.plugins.length;a++){var i=t.plugins[a];i.processFnString&&(r=i.processFnString(r,t))}return r}var g=new(function(){function e(e){this.cache=e}return e.prototype.define=function(e,t){this.cache[e]=t},e.prototype.get=function(e){return this.cache[e]},e.prototype.remove=function(e){delete this.cache[e]},e.prototype.reset=function(){this.cache={}},e.prototype.load=function(e){a(this.cache,e)},e}())({});function h(e,n){var r=this.templates.get(e);if(!r)throw t('Could not fetch template "'+e+'"');return r(n,this)}var m={varName:"it",autoTrim:[!1,"nl"],rmWhitespace:!1,autoEscape:!0,tags:["<%","%>"],parse:{interpolate:"=",raw:"~",exec:""},async:!1,templates:g,cache:!1,plugins:[],useWith:!1,e:function(e){var t=String(e);return/[&<>"']/.test(t)?t.replace(/[&<>"']/g,s):t},include:h};function v(e,t){var n={};return a(n,m),t&&a(n,t),e&&a(n,e),n}function x(e,n){var r,a=v(n||{});r=a.async?function(){try{return new Function("return (async function(){}).constructor")()}catch(e){throw e instanceof SyntaxError?t("This environment doesn't support async/await"):e}}():Function;try{return new r(a.varName,"E","cb",d(e,a))}catch(n){throw n instanceof SyntaxError?t("Bad template syntax\n\n"+n.message+"\n"+Array(n.message.length+1).join("=")+"\n"+d(e,a)+"\n"):n}}function y(e,t){var n;return t.cache&&t.name&&t.templates.get(t.name)?t.templates.get(t.name):(n="function"==typeof e?e:x(e,t),t.cache&&t.name&&t.templates.define(t.name,n),n)}h.bind(m),e.compile=x,e.compileToString=d,e.defaultConfig=m,e.getConfig=v,e.parse=f,e.render=function(e,n,a,i){var o=v(a||{});if(!o.async)return y(e,o)(n,o);if(!i){if("function"==typeof r)return new r((function(t,r){try{t(y(e,o)(n,o))}catch(e){r(e)}}));throw t("Please provide a callback function, this env doesn't support Promises")}try{y(e,o)(n,o,i)}catch(e){return i(e)}},e.templates=g,Object.defineProperty(e,"__esModule",{value:!0})}));
//# sourceMappingURL=eta.min.js.map

@@ -6,2 +6,3 @@ 'use strict';

function setPrototypeOf(obj, proto) {
// eslint-disable-line @typescript-eslint/no-explicit-any
if (Object.setPrototypeOf) {

@@ -14,2 +15,15 @@ Object.setPrototypeOf(obj, proto);

}
// This is pretty much the only way to get nice, extended Errors
// without using ES6
/**
* This returns a new Error with a custom prototype. Note that it's _not_ a constructor
*
* @param message Error message
*
* **Example**
*
* ```js
* throw EtaErr("template not found")
* ```
*/
function EtaErr(message) {

@@ -23,3 +37,5 @@ var err = new Error(message);

});
// TODO: Class transpilation adds a lot to the bundle size
/**
* Throws an EtaErr with a nicely formatted error and message showing where in the template the error occurred.
*/
function ParseErr(message, str, indx) {

@@ -44,10 +60,62 @@ var whitespace = str.slice(0, indx).split(/\n/);

/**
* @returns The global Promise function
*/
var promiseImpl = new Function('return this')().Promise;
/**
* @returns A new AsyncFunction constuctor
*/
function getAsyncFunctionConstructor() {
try {
return new Function('return (async function(){}).constructor')();
}
catch (e) {
if (e instanceof SyntaxError) {
throw EtaErr("This environment doesn't support async/await");
}
else {
throw e;
}
}
}
/**
* str.trimLeft polyfill
*
* @param str - Input string
* @returns The string with left whitespace removed
*
*/
function trimLeft(str) {
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimLeft) {
return str.trimLeft();
}
else {
return str.replace(/^\s+/, '');
}
}
/**
* str.trimRight polyfill
*
* @param str - Input string
* @returns The string with right whitespace removed
*
*/
function trimRight(str) {
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimRight) {
return str.trimRight();
}
else {
return str.replace(/\s+$/, ''); // TODO: do we really need to replace BOM's?
}
}
// TODO: allow '-' to trim up until newline. Use [^\S\n\r] instead of \s
// TODO: only include trimLeft polyfill if not in ES6
/* END TYPES */
var promiseImpl = new Function('return this;')().Promise;
function hasOwnProp(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function copyProps(toObj, fromObj, notConfig) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function copyProps(toObj, fromObj) {
for (var key in fromObj) {

@@ -60,13 +128,16 @@ if (hasOwnProp(fromObj, key)) {

}
function trimWS(str, env, wsLeft, wsRight) {
/**
* Takes a string within a template and trims it, based on the preceding tag's whitespace control and `config.autoTrim`
*/
function trimWS(str, config, wsLeft, wsRight) {
var leftTrim;
var rightTrim;
if (Array.isArray(env.autoTrim)) {
if (Array.isArray(config.autoTrim)) {
// kinda confusing
// but _}} will trim the left side of the following string
leftTrim = env.autoTrim[1];
rightTrim = env.autoTrim[0];
leftTrim = config.autoTrim[1];
rightTrim = config.autoTrim[0];
}
else {
leftTrim = rightTrim = env.autoTrim;
leftTrim = rightTrim = config.autoTrim;
}

@@ -88,12 +159,5 @@ if (wsLeft || wsLeft === false) {

// full slurp
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimLeft) {
str = str.trimLeft();
}
else {
str = str.replace(/^[\s\uFEFF\xA0]+/, '');
}
str = trimLeft(str);
}
else if (leftTrim === '-' || leftTrim === 'nl') {
// console.log('trimming left nl' + leftTrim)
// nl trim

@@ -103,14 +167,6 @@ str = str.replace(/^(?:\r\n|\n|\r)/, '');

if (rightTrim === '_' || rightTrim === 'slurp') {
// console.log('trimming right' + rightTrim)
// full slurp
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimRight) {
str = str.trimRight();
}
else {
str = str.replace(/[\s\uFEFF\xA0]+$/, '');
}
str = trimRight(str);
}
else if (rightTrim === '-' || rightTrim === 'nl') {
// console.log('trimming right nl' + rightTrim)
// nl trim

@@ -121,2 +177,5 @@ str = str.replace(/(?:\r\n|\n|\r)$/, ''); // TODO: make sure this gets \r\n

}
/**
* A map of special HTML characters to their XML-escaped equivalents
*/
var escMap = {

@@ -127,3 +186,3 @@ '&': '&amp;',

'"': '&quot;',
"'": '&#39;'
"'": '&#39;',
};

@@ -133,3 +192,10 @@ function replaceChar(s) {

}
/**
* XML-escapes an input value after converting it to a string
*
* @param str - Input value (usually a string)
* @returns XML-escaped string
*/
function XMLEscape(str) {
// eslint-disable-line @typescript-eslint/no-explicit-any
// To deal with XSS. Based on Escape implementations of Mustache.JS and Marko, then customized.

@@ -149,2 +215,3 @@ var newStr = String(str);

var doubleQuoteReg = /"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g;
/** Escape special regular expression characters inside a string */
function escapeRegExp(string) {

@@ -175,3 +242,2 @@ // From MDN

// if string is truthy it must be of type 'string'
// TODO: benchmark replace( /(\\|')/g, '\\$1')
strng = trimWS(strng, env, trimLeftOfNextStr, // this will only be false on the first str, the next ones will be null or undefined

@@ -181,4 +247,4 @@ shouldTrimRightOfString);

// replace \ with \\, ' with \'
// we're going to convert all CRLF to LF so it doesn't take more than one replace
strng = strng.replace(/\\|'/g, '\\$&').replace(/\r\n|\n|\r/g, '\\n');
// we're going to convert all CRLF to LF so it doesn't take more than one replace
buffer.push(strng);

@@ -206,3 +272,2 @@ }

while ((m = parseOpenReg.exec(str))) {
// TODO: check if above needs exec(str) !== null. Don't think it's possible to have 0-width matches but...
lastIndex = m[0].length + m.index;

@@ -295,5 +360,15 @@ var precedingString = m[1];

/* END TYPES */
/**
* 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
*
* **Example**
*
* ```js
* compileToString("Hi <%= it.user %>", eta.defaultConfig)
* // "var tR='';tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR"
* ```
*/
function compileToString(str, env) {
var buffer = parse(str, env);
var res = "var tR='';" +
var res = "var tR=''\n" +
(env.useWith ? 'with(' + env.varName + '||{}){' : '') +

@@ -312,4 +387,15 @@ compileScope(buffer, env) +

return res;
// TODO: is `return cb()` necessary, or could we just do `cb()`
}
/**
* Loops through the AST generated by `parse` and transform each item into JS calls
*
* **Example**
*
* ```js
* // AST version of 'Hi <%= it.user %>'
* let templateAST = ['Hi ', { val: 'it.user', t: 'i' }]
* compileScope(templateAST, eta.defaultConfig)
* // "tR+='Hi ';tR+=E.e(it.user);"
* ```
*/
function compileScope(buff, env) {

@@ -324,3 +410,3 @@ var i = 0;

// we know string exists
returnStr += "tR+='" + str + "';";
returnStr += "tR+='" + str + "'\n";
}

@@ -332,3 +418,3 @@ else {

// raw
returnStr += 'tR+=' + content + ';';
returnStr += 'tR+=' + content + '\n';
}

@@ -340,3 +426,3 @@ else if (type === 'i') {

}
returnStr += 'tR+=' + content + ';';
returnStr += 'tR+=' + content + '\n';
// reference

@@ -353,3 +439,8 @@ }

/* END TYPES */
/**
* Handles storage and accessing of values
*
* In this case, we use it to store compiled template functions
* Indexed by their `name` or `filename`
*/
var Cacher = /** @class */ (function () {

@@ -375,4 +466,2 @@ function Cacher(cache) {

Cacher.prototype.load = function (cacheObj) {
// TODO: this will err with deep objects and `storage` or `plugins` keys.
// Update Feb 26: EDITED so it shouldn't err
copyProps(this.cache, cacheObj);

@@ -384,5 +473,15 @@ };

/* END TYPES */
/**
* Eta's template storage
*
* Stores partials and cached templates
*/
var templates = new Cacher({});
/* END TYPES */
/**
* Include a template based on its name (or filepath, if it's already been cached).
*
* Called like `E.include(templateNameOrPath, data)`
*/
function includeHelper(templateNameOrPath, data) {

@@ -404,3 +503,3 @@ var template = this.templates.get(templateNameOrPath);

raw: '~',
exec: ''
exec: '',
},

@@ -413,5 +512,17 @@ async: false,

e: XMLEscape,
include: includeHelper
include: includeHelper,
};
includeHelper.bind(defaultConfig);
/**
* Takes one or two partial (not necessarily complete) configuration objects, merges them 1 layer deep into defaultConfig, and returns the result
*
* @param override Partial configuration object
* @param baseConfig Partial configuration object to merge before `override`
*
* **Example**
*
* ```js
* let customConfig = getConfig({tags: ['!#', '#!']})
* ```
*/
function getConfig(override, baseConfig) {

@@ -431,4 +542,19 @@ // TODO: run more tests on this

/* END TYPES */
function compile(str, env) {
var options = getConfig(env || {});
/**
* Takes a template string and returns a template function that can be called with (data, config, [cb])
*
* @param str - The template string
* @param config - A custom configuration object (optional)
*
* **Example**
*
* ```js
* let compiledFn = eta.compile("Hi <%= it.user %>")
* // function anonymous()
* let compiledFnStr = compiledFn.toString()
* // "function anonymous(it,E,cb\n) {\nvar tR='';tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR\n}"
* ```
*/
function compile(str, config) {
var options = getConfig(config || {});
var ctor; // constructor

@@ -438,15 +564,3 @@ /* ASYNC HANDLING */

if (options.async) {
// Have to use generated function for this, since in envs without support,
// it breaks in parsing
try {
ctor = new Function('return (async function(){}).constructor;')();
}
catch (e) {
if (e instanceof SyntaxError) {
throw EtaErr("This environment doesn't support async/await");
}
else {
throw e;
}
}
ctor = getAsyncFunctionConstructor();
}

@@ -469,3 +583,5 @@ else {

'\n' +
compileToString(str, options));
compileToString(str, options) +
'\n' // This will put an extra newline before the callstack for extra readability
);
}

@@ -480,2 +596,4 @@ else {

var path = require('path');
var readFileSync = fs.readFileSync;
var _BOM = /^\uFEFF/;

@@ -487,6 +605,8 @@ /* END TYPES */

*
* @param {String} name specified path
* @param {String} parentfile parent file path
* @param {Boolean} [isDir=false] whether parent file path is a directory
* @return {String}
* If `name` does not have an extension, it will default to `.eta`
*
* @param name specified path
* @param parentfile parent file path
* @param isDirectory whether parentfile is a directory
* @return absolute path to template
*/

@@ -504,7 +624,13 @@ function getWholeFilePath(name, parentfile, isDirectory) {

/**
* Get the path to the included file by Options
* Get the absolute path to an included template
*
* @param {String} path specified path
* @param {Options} options compilation options
* @return {String}
* If this is called with an absolute path (for example, starting with '/' or 'C:\') then Eta will return the filepath.
*
* If this is called with a relative path, Eta will:
* - Look relative to the current template (if the current template has the `filename` property)
* - Look inside each directory in options.views
*
* @param path specified path
* @param options compilation options
* @return absolute path to template
*/

@@ -530,3 +656,5 @@ function getPath(path, options) {

// Then look in any views directories
// TODO: write tests for if views is a string
if (!includePath) {
// Loop through each views directory and search for the file.
if (Array.isArray(views) &&

@@ -539,2 +667,9 @@ views.some(function (v) {

}
else if (typeof views === 'string') {
// Search for the file if views is a single directory
filePath = getWholeFilePath(path, views, true);
if (fs.existsSync(filePath)) {
includePath = filePath;
}
}
}

@@ -547,9 +682,19 @@ if (!includePath) {

}
/**
* Reads a file synchronously
*/
function readFile(filePath) {
return fs
.readFileSync(filePath)
.toString()
.replace(_BOM, ''); // TODO: is replacing BOM's necessary?
return readFileSync(filePath).toString().replace(_BOM, ''); // TODO: is replacing BOM's necessary?
}
function loadFile(filePath, options) {
// express is set like: app.engine('html', require('eta').renderFile)
/* END TYPES */
/**
* Reads a template, compiles it into a function, caches it if caching isn't disabled, returns the function
*
* @param filePath Absolute path to template file
* @param options Eta configuration overrides
* @param noCache Optionally, make Eta not cache the template
*/
function loadFile(filePath, options, noCache) {
var config = getConfig(options);

@@ -559,12 +704,11 @@ var template = readFile(filePath);

var compiledTemplate = compile(template, config);
config.templates.define(config.filename, compiledTemplate);
if (!noCache) {
config.templates.define(config.filename, compiledTemplate);
}
return compiledTemplate;
}
catch (e) {
throw EtaErr('Loading file: ' + filePath + ' failed');
throw EtaErr('Loading file: ' + filePath + ' failed:\n\n' + e.message);
}
}
// express is set like: app.engine('html', require('eta').renderFile)
/* END TYPES */
/**

@@ -577,7 +721,4 @@ * Get the template from a string or a file, either compiled on-the-fly or

*
* @param {Options} options compilation options
* @param {String} [template] template source
* @return {(TemplateFunction|ClientFunction)}
* Depending on the value of `options.client`, either type might be returned.
* @static
* @param options compilation options
* @return Eta template function
*/

@@ -591,7 +732,6 @@ function handleCache(options) {

}
else {
return loadFile(filename, options);
}
return loadFile(filename, options);
}
return compile(readFile(filename), options);
// Caching is disabled, so pass noCache = true
return loadFile(filename, options, true);
}

@@ -603,10 +743,19 @@ /**

*
* @param {Options} options compilation options
* @param {Object} data template data
* @param {RenderFileCallback} cb callback
* @static
* @param data template data
* @param options compilation options
* @param cb callback
*/
function tryHandleCache(options, data, cb) {
var result;
if (!cb) {
function tryHandleCache(data, options, cb) {
if (cb) {
try {
// Note: if there is an error while rendering the template,
// It will bubble up and be caught here
var templateFn = handleCache(options);
templateFn(data, options, cb);
}
catch (err) {
return cb(err);
}
}
else {
// No callback, try returning a promise

@@ -616,3 +765,4 @@ if (typeof promiseImpl === 'function') {

try {
result = handleCache(options)(data, options);
var templateFn = handleCache(options);
var result = templateFn(data, options);
resolve(result);

@@ -629,10 +779,2 @@ }

}
else {
try {
handleCache(options)(data, options, cb);
}
catch (err) {
return cb(err);
}
}
}

@@ -644,7 +786,12 @@ /**

*
* @param {String} path path for the specified file
* @param {Options} options compilation options
* @return {(TemplateFunction|ClientFunction)}
* Depending on the value of `options.client`, either type might be returned
* @static
* This returns a template function and the config object with which that template function should be called.
*
* @remarks
*
* It's important that this returns a config object with `filename` set.
* Otherwise, the included file would not be able to use relative paths
*
* @param path path for the specified file (if relative, specify `views` on `options`)
* @param options compilation options
* @return [Eta template function, new config object]
*/

@@ -655,39 +802,64 @@ function includeFile(path, options) {

// TODO: make sure properties are currectly copied over
return handleCache(newFileOptions);
return [handleCache(newFileOptions), newFileOptions];
}
function renderFile(filename, data, cb) {
var Config = getConfig(data || {});
// TODO: make sure above doesn't error. We do set filename down below
if (data.settings) {
// Pull a few things from known locations
if (data.settings.views) {
Config.views = data.settings.views;
function renderFile(filename, data, config, cb) {
/*
Here we have some function overloading.
Essentially, the first 2 arguments to renderFile should always be the filename and data
However, with Express, configuration options will be passed along with the data.
Thus, Express will call renderFile with (filename, dataAndOptions, cb)
And we want to also make (filename, data, options, cb) available
*/
var renderConfig;
var callback;
// First, assign our callback function to `callback`
// We can leave it undefined if neither parameter is a function;
// Callbacks are optional
if (typeof cb === 'function') {
// The 4th argument is the callback
callback = cb;
}
else if (typeof config === 'function') {
// The 3rd arg is the callback
callback = config;
}
// If there is a config object passed in explicitly, use it
if (typeof config === 'object') {
renderConfig = getConfig(config || {});
}
else {
// Otherwise, get the config from the data object
// And then grab some config options from data.settings
// Which is where Express sometimes stores them
renderConfig = getConfig(data || {});
if (data.settings) {
// Pull a few things from known locations
if (data.settings.views) {
renderConfig.views = data.settings.views;
}
if (data.settings['view cache']) {
renderConfig.cache = true;
}
// Undocumented after Express 2, but still usable, esp. for
// items that are unsafe to be passed along with data, like `root`
var viewOpts = data.settings['view options'];
if (viewOpts) {
copyProps(renderConfig, viewOpts);
}
}
if (data.settings['view cache']) {
Config.cache = true;
}
// Undocumented after Express 2, but still usable, esp. for
// items that are unsafe to be passed along with data, like `root`
var viewOpts = data.settings['view options'];
if (viewOpts) {
copyProps(Config, viewOpts);
}
}
Config.filename = filename; // Make sure filename is right
return tryHandleCache(Config, data, cb);
// Set the filename option on the template
// This will first try to resolve the file path (see getPath for details)
renderConfig.filename = getPath(filename, renderConfig);
return tryHandleCache(data, renderConfig, callback);
}
/* END TYPES */
/**
* Called with `E.includeFile(path, data)`
*/
function includeFileHelper(path, data) {
return includeFile(path, this)(data, this);
var templateAndConfig = includeFile(path, this);
return templateAndConfig[0](data, templateAndConfig[1]);
}
// export function extendsFileHelper(path: string, data: GenericData, config: EtaConfig): string {
// var data: GenericData = content.params[1] || {}
// data.content = content.exec()
// for (var i = 0; i < blocks.length; i++) {
// var currentBlock = blocks[i]
// data[currentBlock.name] = currentBlock.exec()
// }
// return includeFile(content.params[0], config)(data, config)
// }

@@ -706,2 +878,4 @@ /* END TYPES */

}
// Note that we don't have to check if it already exists in the cache;
// it would have returned earlier if it had
if (options.cache && options.name) {

@@ -712,7 +886,37 @@ options.templates.define(options.name, templateFunc);

}
function render(template, data, env, cb) {
var options = getConfig(env || {});
/**
* Render a template
*
* If `template` is a string, Eta will compile it to a function and then call it with the provided data.
* If `template` is a template function, Eta will call it with the provided data.
*
* If `config.async` is `false`, Eta will return the rendered template.
*
* If `config.async` is `true` and there's a callback function, Eta will call the callback with `(err, renderedTemplate)`.
* If `config.async` is `true` and there's not a callback function, Eta will return a Promise that resolves to the rendered template
*
* If `config.cache` is `true` and `config` has a `name` or `filename` property, Eta will cache the template on the first render and use the cached template for all subsequent renders.
*
* @param template Template string or template function
* @param data Data to render the template with
* @param config Optional config options
* @param cb Callback function
*/
function render(template, data, config, cb) {
var options = getConfig(config || {});
if (options.async) {
var result;
if (!cb) {
if (cb) {
// If user passes callback
try {
// Note: if there is an error while rendering the template,
// It will bubble up and be caught here
var templateFn = handleCache$1(template, options);
templateFn(data, options, cb);
}
catch (err) {
return cb(err);
}
}
else {
// No callback, try returning a promise

@@ -734,10 +938,2 @@ if (typeof promiseImpl === 'function') {

}
else {
try {
handleCache$1(template, options)(data, options, cb);
}
catch (err) {
return cb(err);
}
}
}

@@ -749,5 +945,3 @@ else {

/* Export file stuff */
/* TYPES */
/* END TYPES */
// @denoify-ignore
defaultConfig.includeFile = includeFileHelper;

@@ -754,0 +948,0 @@ includeFileHelper.bind(defaultConfig);

function setPrototypeOf(obj, proto) {
// eslint-disable-line @typescript-eslint/no-explicit-any
if (Object.setPrototypeOf) {

@@ -9,2 +10,15 @@ Object.setPrototypeOf(obj, proto);

}
// This is pretty much the only way to get nice, extended Errors
// without using ES6
/**
* This returns a new Error with a custom prototype. Note that it's _not_ a constructor
*
* @param message Error message
*
* **Example**
*
* ```js
* throw EtaErr("template not found")
* ```
*/
function EtaErr(message) {

@@ -18,3 +32,5 @@ var err = new Error(message);

});
// TODO: Class transpilation adds a lot to the bundle size
/**
* Throws an EtaErr with a nicely formatted error and message showing where in the template the error occurred.
*/
function ParseErr(message, str, indx) {

@@ -39,10 +55,62 @@ var whitespace = str.slice(0, indx).split(/\n/);

/**
* @returns The global Promise function
*/
var promiseImpl = new Function('return this')().Promise;
/**
* @returns A new AsyncFunction constuctor
*/
function getAsyncFunctionConstructor() {
try {
return new Function('return (async function(){}).constructor')();
}
catch (e) {
if (e instanceof SyntaxError) {
throw EtaErr("This environment doesn't support async/await");
}
else {
throw e;
}
}
}
/**
* str.trimLeft polyfill
*
* @param str - Input string
* @returns The string with left whitespace removed
*
*/
function trimLeft(str) {
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimLeft) {
return str.trimLeft();
}
else {
return str.replace(/^\s+/, '');
}
}
/**
* str.trimRight polyfill
*
* @param str - Input string
* @returns The string with right whitespace removed
*
*/
function trimRight(str) {
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimRight) {
return str.trimRight();
}
else {
return str.replace(/\s+$/, ''); // TODO: do we really need to replace BOM's?
}
}
// TODO: allow '-' to trim up until newline. Use [^\S\n\r] instead of \s
// TODO: only include trimLeft polyfill if not in ES6
/* END TYPES */
var promiseImpl = new Function('return this;')().Promise;
function hasOwnProp(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function copyProps(toObj, fromObj, notConfig) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function copyProps(toObj, fromObj) {
for (var key in fromObj) {

@@ -55,13 +123,16 @@ if (hasOwnProp(fromObj, key)) {

}
function trimWS(str, env, wsLeft, wsRight) {
/**
* Takes a string within a template and trims it, based on the preceding tag's whitespace control and `config.autoTrim`
*/
function trimWS(str, config, wsLeft, wsRight) {
var leftTrim;
var rightTrim;
if (Array.isArray(env.autoTrim)) {
if (Array.isArray(config.autoTrim)) {
// kinda confusing
// but _}} will trim the left side of the following string
leftTrim = env.autoTrim[1];
rightTrim = env.autoTrim[0];
leftTrim = config.autoTrim[1];
rightTrim = config.autoTrim[0];
}
else {
leftTrim = rightTrim = env.autoTrim;
leftTrim = rightTrim = config.autoTrim;
}

@@ -83,12 +154,5 @@ if (wsLeft || wsLeft === false) {

// full slurp
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimLeft) {
str = str.trimLeft();
}
else {
str = str.replace(/^[\s\uFEFF\xA0]+/, '');
}
str = trimLeft(str);
}
else if (leftTrim === '-' || leftTrim === 'nl') {
// console.log('trimming left nl' + leftTrim)
// nl trim

@@ -98,14 +162,6 @@ str = str.replace(/^(?:\r\n|\n|\r)/, '');

if (rightTrim === '_' || rightTrim === 'slurp') {
// console.log('trimming right' + rightTrim)
// full slurp
// eslint-disable-next-line no-extra-boolean-cast
if (!!String.prototype.trimRight) {
str = str.trimRight();
}
else {
str = str.replace(/[\s\uFEFF\xA0]+$/, '');
}
str = trimRight(str);
}
else if (rightTrim === '-' || rightTrim === 'nl') {
// console.log('trimming right nl' + rightTrim)
// nl trim

@@ -116,2 +172,5 @@ str = str.replace(/(?:\r\n|\n|\r)$/, ''); // TODO: make sure this gets \r\n

}
/**
* A map of special HTML characters to their XML-escaped equivalents
*/
var escMap = {

@@ -122,3 +181,3 @@ '&': '&amp;',

'"': '&quot;',
"'": '&#39;'
"'": '&#39;',
};

@@ -128,3 +187,10 @@ function replaceChar(s) {

}
/**
* XML-escapes an input value after converting it to a string
*
* @param str - Input value (usually a string)
* @returns XML-escaped string
*/
function XMLEscape(str) {
// eslint-disable-line @typescript-eslint/no-explicit-any
// To deal with XSS. Based on Escape implementations of Mustache.JS and Marko, then customized.

@@ -144,2 +210,3 @@ var newStr = String(str);

var doubleQuoteReg = /"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g;
/** Escape special regular expression characters inside a string */
function escapeRegExp(string) {

@@ -170,3 +237,2 @@ // From MDN

// if string is truthy it must be of type 'string'
// TODO: benchmark replace( /(\\|')/g, '\\$1')
strng = trimWS(strng, env, trimLeftOfNextStr, // this will only be false on the first str, the next ones will be null or undefined

@@ -176,4 +242,4 @@ shouldTrimRightOfString);

// replace \ with \\, ' with \'
// we're going to convert all CRLF to LF so it doesn't take more than one replace
strng = strng.replace(/\\|'/g, '\\$&').replace(/\r\n|\n|\r/g, '\\n');
// we're going to convert all CRLF to LF so it doesn't take more than one replace
buffer.push(strng);

@@ -201,3 +267,2 @@ }

while ((m = parseOpenReg.exec(str))) {
// TODO: check if above needs exec(str) !== null. Don't think it's possible to have 0-width matches but...
lastIndex = m[0].length + m.index;

@@ -290,5 +355,15 @@ var precedingString = m[1];

/* END TYPES */
/**
* 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
*
* **Example**
*
* ```js
* compileToString("Hi <%= it.user %>", eta.defaultConfig)
* // "var tR='';tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR"
* ```
*/
function compileToString(str, env) {
var buffer = parse(str, env);
var res = "var tR='';" +
var res = "var tR=''\n" +
(env.useWith ? 'with(' + env.varName + '||{}){' : '') +

@@ -307,4 +382,15 @@ compileScope(buffer, env) +

return res;
// TODO: is `return cb()` necessary, or could we just do `cb()`
}
/**
* Loops through the AST generated by `parse` and transform each item into JS calls
*
* **Example**
*
* ```js
* // AST version of 'Hi <%= it.user %>'
* let templateAST = ['Hi ', { val: 'it.user', t: 'i' }]
* compileScope(templateAST, eta.defaultConfig)
* // "tR+='Hi ';tR+=E.e(it.user);"
* ```
*/
function compileScope(buff, env) {

@@ -319,3 +405,3 @@ var i = 0;

// we know string exists
returnStr += "tR+='" + str + "';";
returnStr += "tR+='" + str + "'\n";
}

@@ -327,3 +413,3 @@ else {

// raw
returnStr += 'tR+=' + content + ';';
returnStr += 'tR+=' + content + '\n';
}

@@ -335,3 +421,3 @@ else if (type === 'i') {

}
returnStr += 'tR+=' + content + ';';
returnStr += 'tR+=' + content + '\n';
// reference

@@ -348,3 +434,8 @@ }

/* END TYPES */
/**
* Handles storage and accessing of values
*
* In this case, we use it to store compiled template functions
* Indexed by their `name` or `filename`
*/
var Cacher = /** @class */ (function () {

@@ -370,4 +461,2 @@ function Cacher(cache) {

Cacher.prototype.load = function (cacheObj) {
// TODO: this will err with deep objects and `storage` or `plugins` keys.
// Update Feb 26: EDITED so it shouldn't err
copyProps(this.cache, cacheObj);

@@ -379,5 +468,15 @@ };

/* END TYPES */
/**
* Eta's template storage
*
* Stores partials and cached templates
*/
var templates = new Cacher({});
/* END TYPES */
/**
* Include a template based on its name (or filepath, if it's already been cached).
*
* Called like `E.include(templateNameOrPath, data)`
*/
function includeHelper(templateNameOrPath, data) {

@@ -399,3 +498,3 @@ var template = this.templates.get(templateNameOrPath);

raw: '~',
exec: ''
exec: '',
},

@@ -408,5 +507,17 @@ async: false,

e: XMLEscape,
include: includeHelper
include: includeHelper,
};
includeHelper.bind(defaultConfig);
/**
* Takes one or two partial (not necessarily complete) configuration objects, merges them 1 layer deep into defaultConfig, and returns the result
*
* @param override Partial configuration object
* @param baseConfig Partial configuration object to merge before `override`
*
* **Example**
*
* ```js
* let customConfig = getConfig({tags: ['!#', '#!']})
* ```
*/
function getConfig(override, baseConfig) {

@@ -426,4 +537,19 @@ // TODO: run more tests on this

/* END TYPES */
function compile(str, env) {
var options = getConfig(env || {});
/**
* Takes a template string and returns a template function that can be called with (data, config, [cb])
*
* @param str - The template string
* @param config - A custom configuration object (optional)
*
* **Example**
*
* ```js
* let compiledFn = eta.compile("Hi <%= it.user %>")
* // function anonymous()
* let compiledFnStr = compiledFn.toString()
* // "function anonymous(it,E,cb\n) {\nvar tR='';tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR\n}"
* ```
*/
function compile(str, config) {
var options = getConfig(config || {});
var ctor; // constructor

@@ -433,15 +559,3 @@ /* ASYNC HANDLING */

if (options.async) {
// Have to use generated function for this, since in envs without support,
// it breaks in parsing
try {
ctor = new Function('return (async function(){}).constructor;')();
}
catch (e) {
if (e instanceof SyntaxError) {
throw EtaErr("This environment doesn't support async/await");
}
else {
throw e;
}
}
ctor = getAsyncFunctionConstructor();
}

@@ -464,3 +578,5 @@ else {

'\n' +
compileToString(str, options));
compileToString(str, options) +
'\n' // This will put an extra newline before the callstack for extra readability
);
}

@@ -475,2 +591,4 @@ else {

var path = require('path');
var readFileSync = fs.readFileSync;
var _BOM = /^\uFEFF/;

@@ -482,6 +600,8 @@ /* END TYPES */

*
* @param {String} name specified path
* @param {String} parentfile parent file path
* @param {Boolean} [isDir=false] whether parent file path is a directory
* @return {String}
* If `name` does not have an extension, it will default to `.eta`
*
* @param name specified path
* @param parentfile parent file path
* @param isDirectory whether parentfile is a directory
* @return absolute path to template
*/

@@ -499,7 +619,13 @@ function getWholeFilePath(name, parentfile, isDirectory) {

/**
* Get the path to the included file by Options
* Get the absolute path to an included template
*
* @param {String} path specified path
* @param {Options} options compilation options
* @return {String}
* If this is called with an absolute path (for example, starting with '/' or 'C:\') then Eta will return the filepath.
*
* If this is called with a relative path, Eta will:
* - Look relative to the current template (if the current template has the `filename` property)
* - Look inside each directory in options.views
*
* @param path specified path
* @param options compilation options
* @return absolute path to template
*/

@@ -525,3 +651,5 @@ function getPath(path, options) {

// Then look in any views directories
// TODO: write tests for if views is a string
if (!includePath) {
// Loop through each views directory and search for the file.
if (Array.isArray(views) &&

@@ -534,2 +662,9 @@ views.some(function (v) {

}
else if (typeof views === 'string') {
// Search for the file if views is a single directory
filePath = getWholeFilePath(path, views, true);
if (fs.existsSync(filePath)) {
includePath = filePath;
}
}
}

@@ -542,9 +677,19 @@ if (!includePath) {

}
/**
* Reads a file synchronously
*/
function readFile(filePath) {
return fs
.readFileSync(filePath)
.toString()
.replace(_BOM, ''); // TODO: is replacing BOM's necessary?
return readFileSync(filePath).toString().replace(_BOM, ''); // TODO: is replacing BOM's necessary?
}
function loadFile(filePath, options) {
// express is set like: app.engine('html', require('eta').renderFile)
/* END TYPES */
/**
* Reads a template, compiles it into a function, caches it if caching isn't disabled, returns the function
*
* @param filePath Absolute path to template file
* @param options Eta configuration overrides
* @param noCache Optionally, make Eta not cache the template
*/
function loadFile(filePath, options, noCache) {
var config = getConfig(options);

@@ -554,12 +699,11 @@ var template = readFile(filePath);

var compiledTemplate = compile(template, config);
config.templates.define(config.filename, compiledTemplate);
if (!noCache) {
config.templates.define(config.filename, compiledTemplate);
}
return compiledTemplate;
}
catch (e) {
throw EtaErr('Loading file: ' + filePath + ' failed');
throw EtaErr('Loading file: ' + filePath + ' failed:\n\n' + e.message);
}
}
// express is set like: app.engine('html', require('eta').renderFile)
/* END TYPES */
/**

@@ -572,7 +716,4 @@ * Get the template from a string or a file, either compiled on-the-fly or

*
* @param {Options} options compilation options
* @param {String} [template] template source
* @return {(TemplateFunction|ClientFunction)}
* Depending on the value of `options.client`, either type might be returned.
* @static
* @param options compilation options
* @return Eta template function
*/

@@ -586,7 +727,6 @@ function handleCache(options) {

}
else {
return loadFile(filename, options);
}
return loadFile(filename, options);
}
return compile(readFile(filename), options);
// Caching is disabled, so pass noCache = true
return loadFile(filename, options, true);
}

@@ -598,10 +738,19 @@ /**

*
* @param {Options} options compilation options
* @param {Object} data template data
* @param {RenderFileCallback} cb callback
* @static
* @param data template data
* @param options compilation options
* @param cb callback
*/
function tryHandleCache(options, data, cb) {
var result;
if (!cb) {
function tryHandleCache(data, options, cb) {
if (cb) {
try {
// Note: if there is an error while rendering the template,
// It will bubble up and be caught here
var templateFn = handleCache(options);
templateFn(data, options, cb);
}
catch (err) {
return cb(err);
}
}
else {
// No callback, try returning a promise

@@ -611,3 +760,4 @@ if (typeof promiseImpl === 'function') {

try {
result = handleCache(options)(data, options);
var templateFn = handleCache(options);
var result = templateFn(data, options);
resolve(result);

@@ -624,10 +774,2 @@ }

}
else {
try {
handleCache(options)(data, options, cb);
}
catch (err) {
return cb(err);
}
}
}

@@ -639,7 +781,12 @@ /**

*
* @param {String} path path for the specified file
* @param {Options} options compilation options
* @return {(TemplateFunction|ClientFunction)}
* Depending on the value of `options.client`, either type might be returned
* @static
* This returns a template function and the config object with which that template function should be called.
*
* @remarks
*
* It's important that this returns a config object with `filename` set.
* Otherwise, the included file would not be able to use relative paths
*
* @param path path for the specified file (if relative, specify `views` on `options`)
* @param options compilation options
* @return [Eta template function, new config object]
*/

@@ -650,39 +797,64 @@ function includeFile(path, options) {

// TODO: make sure properties are currectly copied over
return handleCache(newFileOptions);
return [handleCache(newFileOptions), newFileOptions];
}
function renderFile(filename, data, cb) {
var Config = getConfig(data || {});
// TODO: make sure above doesn't error. We do set filename down below
if (data.settings) {
// Pull a few things from known locations
if (data.settings.views) {
Config.views = data.settings.views;
function renderFile(filename, data, config, cb) {
/*
Here we have some function overloading.
Essentially, the first 2 arguments to renderFile should always be the filename and data
However, with Express, configuration options will be passed along with the data.
Thus, Express will call renderFile with (filename, dataAndOptions, cb)
And we want to also make (filename, data, options, cb) available
*/
var renderConfig;
var callback;
// First, assign our callback function to `callback`
// We can leave it undefined if neither parameter is a function;
// Callbacks are optional
if (typeof cb === 'function') {
// The 4th argument is the callback
callback = cb;
}
else if (typeof config === 'function') {
// The 3rd arg is the callback
callback = config;
}
// If there is a config object passed in explicitly, use it
if (typeof config === 'object') {
renderConfig = getConfig(config || {});
}
else {
// Otherwise, get the config from the data object
// And then grab some config options from data.settings
// Which is where Express sometimes stores them
renderConfig = getConfig(data || {});
if (data.settings) {
// Pull a few things from known locations
if (data.settings.views) {
renderConfig.views = data.settings.views;
}
if (data.settings['view cache']) {
renderConfig.cache = true;
}
// Undocumented after Express 2, but still usable, esp. for
// items that are unsafe to be passed along with data, like `root`
var viewOpts = data.settings['view options'];
if (viewOpts) {
copyProps(renderConfig, viewOpts);
}
}
if (data.settings['view cache']) {
Config.cache = true;
}
// Undocumented after Express 2, but still usable, esp. for
// items that are unsafe to be passed along with data, like `root`
var viewOpts = data.settings['view options'];
if (viewOpts) {
copyProps(Config, viewOpts);
}
}
Config.filename = filename; // Make sure filename is right
return tryHandleCache(Config, data, cb);
// Set the filename option on the template
// This will first try to resolve the file path (see getPath for details)
renderConfig.filename = getPath(filename, renderConfig);
return tryHandleCache(data, renderConfig, callback);
}
/* END TYPES */
/**
* Called with `E.includeFile(path, data)`
*/
function includeFileHelper(path, data) {
return includeFile(path, this)(data, this);
var templateAndConfig = includeFile(path, this);
return templateAndConfig[0](data, templateAndConfig[1]);
}
// export function extendsFileHelper(path: string, data: GenericData, config: EtaConfig): string {
// var data: GenericData = content.params[1] || {}
// data.content = content.exec()
// for (var i = 0; i < blocks.length; i++) {
// var currentBlock = blocks[i]
// data[currentBlock.name] = currentBlock.exec()
// }
// return includeFile(content.params[0], config)(data, config)
// }

@@ -701,2 +873,4 @@ /* END TYPES */

}
// Note that we don't have to check if it already exists in the cache;
// it would have returned earlier if it had
if (options.cache && options.name) {

@@ -707,7 +881,37 @@ options.templates.define(options.name, templateFunc);

}
function render(template, data, env, cb) {
var options = getConfig(env || {});
/**
* Render a template
*
* If `template` is a string, Eta will compile it to a function and then call it with the provided data.
* If `template` is a template function, Eta will call it with the provided data.
*
* If `config.async` is `false`, Eta will return the rendered template.
*
* If `config.async` is `true` and there's a callback function, Eta will call the callback with `(err, renderedTemplate)`.
* If `config.async` is `true` and there's not a callback function, Eta will return a Promise that resolves to the rendered template
*
* If `config.cache` is `true` and `config` has a `name` or `filename` property, Eta will cache the template on the first render and use the cached template for all subsequent renders.
*
* @param template Template string or template function
* @param data Data to render the template with
* @param config Optional config options
* @param cb Callback function
*/
function render(template, data, config, cb) {
var options = getConfig(config || {});
if (options.async) {
var result;
if (!cb) {
if (cb) {
// If user passes callback
try {
// Note: if there is an error while rendering the template,
// It will bubble up and be caught here
var templateFn = handleCache$1(template, options);
templateFn(data, options, cb);
}
catch (err) {
return cb(err);
}
}
else {
// No callback, try returning a promise

@@ -729,10 +933,2 @@ if (typeof promiseImpl === 'function') {

}
else {
try {
handleCache$1(template, options)(data, options, cb);
}
catch (err) {
return cb(err);
}
}
}

@@ -744,5 +940,3 @@ else {

/* Export file stuff */
/* TYPES */
/* END TYPES */
// @denoify-ignore
defaultConfig.includeFile = includeFileHelper;

@@ -749,0 +943,0 @@ includeFileHelper.bind(defaultConfig);

import { EtaConfig } from './config';
/**
* 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
*
* **Example**
*
* ```js
* compileToString("Hi <%= it.user %>", eta.defaultConfig)
* // "var tR='';tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR"
* ```
*/
export default function compileToString(str: string, env: EtaConfig): string;
import { EtaConfig, PartialConfig } from './config';
import { CallbackFn } from './file-handlers';
export declare type TemplateFunction = (data: object, config: EtaConfig, cb?: CallbackFn) => string;
export default function compile(str: string, env?: PartialConfig): TemplateFunction;
/**
* Takes a template string and returns a template function that can be called with (data, config, [cb])
*
* @param str - The template string
* @param config - A custom configuration object (optional)
*
* **Example**
*
* ```js
* let compiledFn = eta.compile("Hi <%= it.user %>")
* // function anonymous()
* let compiledFnStr = compiledFn.toString()
* // "function anonymous(it,E,cb\n) {\nvar tR='';tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR\n}"
* ```
*/
export default function compile(str: string, config?: PartialConfig): TemplateFunction;

@@ -5,12 +5,22 @@ import { TemplateFunction } from './compile';

export interface EtaConfig {
/** Name of the data object. Default `it` */
varName: string;
/** Configure automatic whitespace trimming. Default `[false, 'nl']` */
autoTrim: trimConfig | [trimConfig, trimConfig];
/** Remove all safe-to-remove whitespace */
rmWhitespace: boolean;
/** Whether or not to automatically XML-escape interpolations. Default true */
autoEscape: boolean;
/** Delimiters: by default `['<%', '%>']` */
tags: [string, string];
/** Parsing options */
parse: {
/** Which prefix to use for interpolation. Default `"="` */
interpolate: string;
/** Which prefix to use for raw interpolation. Default `"~"` */
raw: string;
/** Which prefix to use for evaluation. Default `""` */
exec: string;
};
/** XML-escaping function */
e: (str: string) => string;

@@ -21,18 +31,40 @@ plugins: Array<{

}>;
/** Compile to async function */
async: boolean;
/** Holds template cache */
templates: Cacher<TemplateFunction>;
/** Whether or not to cache templates if `name` or `filename` is passed */
cache: boolean;
/** Directories that contain templates */
views?: string | Array<string>;
/** Where should absolute paths begin? Default '/' */
root?: string;
/** Absolute path to template file */
filename?: string;
/** Name of template file */
name?: string;
/** Whether or not to cache templates if `name` or `filename` is passed */
'view cache'?: boolean;
/** Make data available on the global object instead of varName */
useWith?: boolean;
[index: string]: any;
}
export declare type PartialConfig = {
[P in keyof EtaConfig]?: EtaConfig[P];
};
export interface EtaConfigWithFilename extends EtaConfig {
filename: string;
}
export declare type PartialConfig = Partial<EtaConfig>;
declare var defaultConfig: EtaConfig;
/**
* Takes one or two partial (not necessarily complete) configuration objects, merges them 1 layer deep into defaultConfig, and returns the result
*
* @param override Partial configuration object
* @param baseConfig Partial configuration object to merge before `override`
*
* **Example**
*
* ```js
* let customConfig = getConfig({tags: ['!#', '#!']})
* ```
*/
declare function getConfig(override: PartialConfig, baseConfig?: EtaConfig): EtaConfig;
export { defaultConfig, getConfig };
import { Cacher } from './storage';
import { TemplateFunction } from './compile';
/**
* Eta's template storage
*
* Stores partials and cached templates
*/
declare var templates: Cacher<TemplateFunction>;
export { templates };

@@ -0,1 +1,12 @@

/**
* This returns a new Error with a custom prototype. Note that it's _not_ a constructor
*
* @param message Error message
*
* **Example**
*
* ```js
* throw EtaErr("template not found")
* ```
*/
declare function EtaErr(message: string): Error;

@@ -6,2 +17,5 @@ declare namespace EtaErr {

export default EtaErr;
/**
* Throws an EtaErr with a nicely formatted error and message showing where in the template the error occurred.
*/
export declare function ParseErr(message: string, str: string, indx: number): void;

@@ -1,5 +0,6 @@

import { EtaConfig } from './config';
import { EtaConfig, PartialConfig } from './config';
import { TemplateFunction } from './compile';
export declare type CallbackFn = (err: Error | null, str?: string) => void;
interface DataObj {
/** Express.js settings may be stored here */
settings?: {

@@ -10,3 +11,14 @@ [key: string]: any;

}
interface PartialConfigWithFilename extends Partial<EtaConfig> {
filename: string;
}
/**
* Reads a template, compiles it into a function, caches it if caching isn't disabled, returns the function
*
* @param filePath Absolute path to template file
* @param options Eta configuration overrides
* @param noCache Optionally, make Eta not cache the template
*/
export declare function loadFile(filePath: string, options: PartialConfigWithFilename, noCache?: boolean): TemplateFunction;
/**
* Get the template function.

@@ -16,10 +28,42 @@ *

*
* @param {String} path path for the specified file
* @param {Options} options compilation options
* @return {(TemplateFunction|ClientFunction)}
* Depending on the value of `options.client`, either type might be returned
* @static
* This returns a template function and the config object with which that template function should be called.
*
* @remarks
*
* It's important that this returns a config object with `filename` set.
* Otherwise, the included file would not be able to use relative paths
*
* @param path path for the specified file (if relative, specify `views` on `options`)
* @param options compilation options
* @return [Eta template function, new config object]
*/
declare function includeFile(path: string, options: EtaConfig): TemplateFunction;
declare function includeFile(path: string, options: EtaConfig): [TemplateFunction, EtaConfig];
/**
* Render a template from a filepath.
*
* @param filepath Path to template file. If relative, specify `views` on the config object
*
* This can take two different function signatures:
*
* - `renderFile(filename, dataAndConfig, [cb])`
* - Eta will merge `dataAndConfig` into `eta.defaultConfig`
* - `renderFile(filename, data, [config], [cb])`
*
* Note that renderFile does not immediately return the rendered result. If you pass in a callback function, it will be called with `(err, res)`. Otherwise, `renderFile` will return a `Promise` that resolves to the render result.
*
* **Examples**
*
* ```js
* eta.renderFile("./template.eta", data, {cache: true}, function (err, rendered) {
* if (err) console.log(err)
* console.log(rendered)
* })
*
* let rendered = await eta.renderFile("./template.eta", data, {cache: true})
*
* let rendered = await eta.renderFile("./template", {...data, cache: true})
* ```
*/
declare function renderFile(filename: string, data: DataObj, config?: PartialConfig, cb?: CallbackFn): any;
declare function renderFile(filename: string, data: DataObj, cb?: CallbackFn): any;
export { includeFile, renderFile };

@@ -5,3 +5,6 @@ import { EtaConfig } from './config';

}
/**
* Called with `E.includeFile(path, data)`
*/
export declare function includeFileHelper(this: EtaConfig, path: string, data: GenericData): string;
export {};

@@ -1,16 +0,20 @@

import { EtaConfig, PartialConfig } from './config';
import { TemplateFunction } from './compile';
interface PartialFileConfig extends PartialConfig {
filename: string;
}
import { EtaConfig } from './config';
/**
* Get the path to the included file by Options
* Get the absolute path to an included template
*
* @param {String} path specified path
* @param {Options} options compilation options
* @return {String}
* If this is called with an absolute path (for example, starting with '/' or 'C:\') then Eta will return the filepath.
*
* If this is called with a relative path, Eta will:
* - Look relative to the current template (if the current template has the `filename` property)
* - Look inside each directory in options.views
*
* @param path specified path
* @param options compilation options
* @return absolute path to template
*/
declare function getPath(path: string, options: EtaConfig): any;
declare function getPath(path: string, options: EtaConfig): string;
/**
* Reads a file synchronously
*/
declare function readFile(filePath: string): any;
declare function loadFile(filePath: string, options: PartialFileConfig): TemplateFunction;
export { getPath, readFile, loadFile };
export { getPath, readFile };

@@ -1,3 +0,2 @@

export { renderFile, renderFile as __express } from './file-handlers';
export { loadFile } from './file-utils';
export { loadFile, renderFile, renderFile as __express } from './file-handlers';
export { default as compileToString } from './compile-string';

@@ -4,0 +3,0 @@ export { default as compile } from './compile';

import { PartialConfig } from './config';
import { TemplateFunction } from './compile';
import { CallbackFn } from './file-handlers';
export default function render(template: string | TemplateFunction, data: object, env?: PartialConfig, cb?: CallbackFn): any;
/**
* Render a template
*
* If `template` is a string, Eta will compile it to a function and then call it with the provided data.
* If `template` is a template function, Eta will call it with the provided data.
*
* If `config.async` is `false`, Eta will return the rendered template.
*
* If `config.async` is `true` and there's a callback function, Eta will call the callback with `(err, renderedTemplate)`.
* If `config.async` is `true` and there's not a callback function, Eta will return a Promise that resolves to the rendered template
*
* If `config.cache` is `true` and `config` has a `name` or `filename` property, Eta will cache the template on the first render and use the cached template for all subsequent renders.
*
* @param template Template string or template function
* @param data Data to render the template with
* @param config Optional config options
* @param cb Callback function
*/
export default function render(template: string | TemplateFunction, data: object, config?: PartialConfig, cb?: CallbackFn): string | Promise<string> | void;

@@ -1,7 +0,10 @@

interface Dict<T> {
[key: string]: T;
}
/**
* Handles storage and accessing of values
*
* In this case, we use it to store compiled template functions
* Indexed by their `name` or `filename`
*/
declare class Cacher<T> {
private cache;
constructor(cache: Dict<T>);
constructor(cache: Record<string, T>);
define(key: string, val: T): void;

@@ -11,4 +14,4 @@ get(key: string): T;

reset(): void;
load(cacheObj: Dict<T>): void;
load(cacheObj: Record<string, T>): void;
}
export { Cacher };
import { EtaConfig } from './config';
export declare var promiseImpl: any;
export declare function hasOwnProp(obj: object, prop: string): boolean;
export declare function copyProps<T>(toObj: T, fromObj: T, notConfig?: boolean): T;
declare function trimWS(str: string, env: EtaConfig, wsLeft: string | false, wsRight?: string | false): string;
export declare function copyProps<T>(toObj: T, fromObj: T): T;
/**
* Takes a string within a template and trims it, based on the preceding tag's whitespace control and `config.autoTrim`
*/
declare function trimWS(str: string, config: EtaConfig, wsLeft: string | false, wsRight?: string | false): string;
/**
* XML-escapes an input value after converting it to a string
*
* @param str - Input value (usually a string)
* @returns XML-escaped string
*/
declare function XMLEscape(str: any): string;
export { trimWS, XMLEscape };
{
"name": "eta",
"version": "1.2.2",
"version": "1.3.0",
"description": "Lightweight, fast, and powerful embedded JS template engine",

@@ -38,7 +38,8 @@ "keywords": [

"lint": "eslint src/*.ts test/*.spec.ts examples/* --ext .js,.ts",
"prebuild": "rimraf dist",
"build": "tsc --module es6 && rollup -c rollup.config.ts && typedoc --out docs --target es6 --mode modules src",
"quickbuild": "tsc --module es6 && rollup -c rollup.config.ts",
"prebuild": "rimraf dist && rimraf deno_dist",
"build": "denoify && rollup -c rollup.config.ts && typedoc --tsconfig tsconfig.json --target es6 src && deno fmt deno_dist/*.ts",
"start": "rollup -c rollup.config.ts -w",
"test": "jest --coverage",
"size": "size-limit",
"test": "jest --coverage && npm run test:deno && npm run size",
"test:deno": "deno test test/deno/*.spec.ts --unstable --allow-read",
"test:watch": "jest --coverage --watch",

@@ -50,7 +51,14 @@ "test:prod": "npm run lint && npm run test -- --no-cache",

"travis-deploy-once": "travis-deploy-once --pro",
"format": "prettier-standard --format '{src,test}/**/*.ts'"
"format": "prettier --write '{src,test}/**/!(*.deno).ts' && deno fmt deno_dist/*.ts",
"release": "np"
},
"size-limit": [
{
"path": "dist/browser/eta.min.js",
"limit": "3 KB"
}
],
"lint-staged": {
"{src,test}/**/*.ts": [
"prettier-standard --lint"
"{src,test}/**/!(*.deno).ts": [
"eslint"
]

@@ -68,3 +76,3 @@ },

"testEnvironment": "node",
"testRegex": "(/test/.*|\\.(test|spec))\\.(ts|js)$",
"testRegex": "\\/test\\/(?!deno\\/).*(\\.spec\\.ts)$",
"moduleFileExtensions": [

@@ -76,7 +84,12 @@ "ts",

"/node_modules/",
"/test/"
"/test/",
"/test/deno/",
"/src/*.deno.ts"
],
"testPathIgnorePatterns": [
"/src/*.deno.ts"
],
"coverageThreshold": {
"global": {
"branches": 84,
"branches": 85,
"functions": 95,

@@ -88,3 +101,3 @@ "lines": 95,

"collectCoverageFrom": [
"src/{!(browser),}.ts"
"src/!(*.deno|browser).ts"
]

@@ -94,3 +107,5 @@ },

"ignore": [
"dist"
"dist",
"deno_dist",
"src/*.deno.ts"
]

@@ -108,15 +123,17 @@ },

"devDependencies": {
"@commitlint/cli": "^8.2.0",
"@commitlint/config-conventional": "^8.2.0",
"@types/jest": "^25.1.0",
"@types/node": "^13.9.3",
"@typescript-eslint/eslint-plugin": "2",
"@typescript-eslint/parser": "^2.20.0",
"commitizen": "^4.0.3",
"coveralls": "^3.0.2",
"cross-env": "^7.0.0",
"cz-conventional-changelog": "^3.0.2",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.0",
"eslint-config-standard-with-typescript": "^14.0.0",
"@commitlint/cli": "^9.1.2",
"@commitlint/config-conventional": "^9.1.2",
"@size-limit/preset-small-lib": "^4.5.7",
"@types/jest": "^26.0.12",
"@types/node": "^14.6.2",
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"commitizen": "^4.2.1",
"coveralls": "^3.1.0",
"cross-env": "^7.0.2",
"cz-conventional-changelog": "^3.3.0",
"denoify": "^0.5.2",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-config-standard-with-typescript": "^18.0.2",
"eslint-plugin-import": "2",

@@ -126,22 +143,23 @@ "eslint-plugin-node": "11",

"eslint-plugin-standard": "4",
"husky": "^4.2.1",
"jest": "^25.2.0",
"jest-config": "^25.2.0",
"lint-staged": "^10.0.3",
"prettier-standard": "^16.1.0",
"husky": "^4.2.5",
"jest": "^26.4.2",
"jest-config": "^26.4.2",
"lint-staged": "^10.2.13",
"prettier": "2.1.1",
"prompt": "^1.0.0",
"replace-in-file": "^5.0.2",
"rimraf": "^3.0.0",
"rollup": "^1.27.13",
"replace-in-file": "^6.1.0",
"rimraf": "^3.0.2",
"rollup": "^2.26.8",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-prettier": "^0.6.0",
"rollup-plugin-terser": "^5.1.3",
"rollup-plugin-typescript2": "^0.26.0",
"shelljs": "^0.8.3",
"travis-deploy-once": "^5.0.9",
"ts-jest": "^25.1.0",
"ts-node": "^8.5.4",
"typedoc": "^0.17.3",
"typescript": "^3.0.3"
"rollup-plugin-prettier": "^2.1.0",
"rollup-plugin-terser": "^7.0.1",
"rollup-plugin-typescript2": "^0.27.2",
"shelljs": "^0.8.4",
"size-limit": "^4.5.7",
"travis-deploy-once": "^5.0.11",
"ts-jest": "^26.3.0",
"ts-node": "^9.0.0",
"typedoc": "^0.19.0",
"typescript": "^4.0.2"
},

@@ -148,0 +166,0 @@ "husky": {

@@ -1,3 +0,10 @@

# eta (η)
<h1 align="center">eta (η)</h1>
<p align="center">
<a href="https://eta.js.org">Documentation</a> -
<a href="https://gitter.im/eta-dev/community">Chat</a> -
<a href="https://runkit.com/nebrelbug/eta-simple-demo">RunKit Demo</a> -
<a href="https://eta.js.org/playground">Playground</a>
</p>
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

@@ -9,18 +16,40 @@

![GitHub package.json version (master)](https://img.shields.io/github/package-json/v/eta-dev/eta/master?label=current%20version)
<div align="center">
[![GitHub package.json version (master)](https://img.shields.io/github/package-json/v/eta-dev/eta/master?label=current%20version)](https://www.npmjs.com/package/eta)
[![deno module](https://img.shields.io/badge/deno-module-informational?logo=deno)](https://deno.land/x/eta)
[![deno doc](https://img.shields.io/badge/deno-doc-informational?logo=deno)](https://doc.deno.land/https/deno.land/x/eta/mod.ts)
[![Travis](https://img.shields.io/travis/com/eta-dev/eta/master.svg)](https://travis-ci.com/eta-dev/eta)
[![All Contributors][logo]](#contributors-)
[![Coveralls](https://img.shields.io/coveralls/eta-dev/eta.svg)](https://coveralls.io/github/eta-dev/eta)
[![documentation website](https://img.shields.io/badge/read_the_docs-website-brightgreen.svg)](https://eta.js.org)
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![Join the chat at https://gitter.im/eta-dev/community](https://img.shields.io/gitter/room/eta-dev/eta?color=%2346BC99)](https://gitter.im/eta-dev/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://paypal.me/bengubler)
</div>
**Summary**
Eta is a lightweight and blazing fast embedded JS templating engine. The team who created [Squirrelly](https://squirrelly.js.org) created it to serve as a better alternative to EJS.
Eta is a lightweight and blazing fast embedded JS templating engine that works inside Node, Deno, and the browser. Created by the developers of [Squirrelly](https://squirrelly.js.org), it's written in TypeScript and emphasizes phenomenal performance, configurability, and low bundle size.
## Eta vs Other Template Engines
### 🌟 Features
- 📦 0 dependencies
- 💡 2.3KB minzipped; size restricted to <3KB forever with [size-limit](https://github.com/ai/size-limit)
- ⚡️ Written in TypeScript
- ✨ Deno support (+ Node and browser)
- 🚀 Super Fast
- Check out [these benchmarks](https://ghcdn.rawgit.org/eta-dev/eta/master/browser-tests/benchmark.html)
- 🔧 Configurable
- Plugins, custom delimiters, caching
- 🔨 Powerful
- Precompilation, partials, async
- ExpressJS support out-of-the-box
- 🔥 Reliable
- Better quotes/comments support
- _ex._ `<%= someval + "string %>" %>` compiles correctly, while it fails with doT or EJS
- Great error reporting
- ⚡️ Exports ES Modules as well as UMD
- 📝 Easy template syntax
## Eta vs other template engines
<details open>

@@ -107,23 +136,2 @@ <summary>

### 🌟 Features
- 🔧 Great error reporting
- 📦 0 dependencies
- 🔧 Better quotes/comments support
- _ex. `<%= someval + "string %>" %>`_ compiles correctly, while it fails with doT or EJS
- ⚡️ Exports ES Modules as well as UMD
- 🔧 ExpressJS support out-of-the-box
- 🔨 Loops
- 🔧 Custom delimeters
- 📝 Easy template syntax
- 🔧 Precompilation
- ⚡️ 0-dependency
- 🔨 Partials
- 🔨 Comments
- 🔨 Server and browser support
- 🔧 Caching
- 🚀 Super Fast
- Check out [these benchmarks](https://ghcdn.rawgit.org/eta-dev/eta/master/browser-tests/benchmark.html)
- ⚡️ Async support
## 📜 Docs

@@ -203,3 +211,3 @@

<td align="center"><a href="http://www.bengubler.com"><img src="https://avatars3.githubusercontent.com/u/25597854?v=4" width="100px;" alt=""/><br /><sub><b>Ben Gubler</b></sub></a><br /><a href="https://github.com/eta-dev/eta/commits?author=nebrelbug" title="Code">💻</a> <a href="#question-nebrelbug" title="Answering Questions">💬</a> <a href="https://github.com/eta-dev/eta/commits?author=nebrelbug" title="Documentation">📖</a> <a href="https://github.com/eta-dev/eta/commits?author=nebrelbug" title="Tests">⚠️</a></td>
<td align="center"><a href="http://ducnhatphung@gmail.com"><img src="https://avatars1.githubusercontent.com/u/16368559?v=4" width="100px;" alt=""/><br /><sub><b>Clite Tailor</b></sub></a><br /><a href="#ideas-clitetailor" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/eta-dev/eta/commits?author=clitetailor" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/clitetailor"><img src="https://avatars1.githubusercontent.com/u/16368559?v=4" width="100px;" alt=""/><br /><sub><b>Clite Tailor</b></sub></a><br /><a href="#ideas-clitetailor" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/eta-dev/eta/commits?author=clitetailor" title="Code">💻</a></td>
<td align="center"><a href="https://twitter.com/ioan_chiriac"><img src="https://avatars2.githubusercontent.com/u/173203?v=4" width="100px;" alt=""/><br /><sub><b>Ioan CHIRIAC</b></sub></a><br /><a href="https://github.com/eta-dev/eta/commits?author=ichiriac" title="Code">💻</a> <a href="#ideas-ichiriac" title="Ideas, Planning, & Feedback">🤔</a></td>

@@ -218,3 +226,3 @@ </tr>

- Async support and file handling were added based on code from [EJS](https://github.com/mde/ejs), which is licensed under the Apache-2.0 license. Code was modified to throw an Eta Error.
- Async support and file handling were added based on code from [EJS](https://github.com/mde/ejs), which is licensed under the Apache-2.0 license. Code was modified and refactored to some extent.
- Syntax and some parts of compilation are heavily based off EJS, Nunjucks, and doT.

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc