netherte
Version:
A template engine that supports custom filters and compiles templates into rendering functions.
8 lines (7 loc) • 2.37 kB
JavaScript
class H{static cache=new Map;filters={};registerFilter(j,z){this.filters[j]=z}compile(j){let z=H.cache.get(j);if(z)return z;let q=this.generateRenderCode(j),A=new Function("data","filters",q),B=(C)=>{try{return A.call(null,C,this.filters)}catch(D){throw new Error(`Render error: ${D.message}`)}};return H.cache.set(j,B),B}parseTemplate(j){let z=/{{(.*?)}}|{%(.*?)%}|([^{<]+)/g,q=[],A;while((A=z.exec(j))!==null)if(A[1])q.push({type:"directive",value:A[1].trim()});else if(A[2]);else if(A[3])q.push({type:"literal",value:A[3]});return q}generateRenderCode(j){let z=this.parseTemplate(j),q=`let __output = [];
`;return z.forEach((A)=>{if(A.type==="literal")q+=`__output.push(${JSON.stringify(A.value)});
`;else{let B=this.processDirective(A.value);if(B)q+=`${B}
`}}),q+=`return __output.join("").trim();
`,`with(data || {}) {
${q}
}`}processDirective(j){return`__output.push(${this.wrapExpression(j)});`}wrapExpression(j){let z=j.split(/\s*\|\s*/).map((A)=>A.trim()),q=this.parseExpression(z.shift());return z.reduce((A,B)=>{let[C,...D]=B.split(":").map((G)=>G.trim()),I=D.map((G)=>this.parseExpression(G)).join(", ");return`filters['${C}'](${A}${I.length?`, ${I}`:""})`},`(() => { try { return ${q} } catch { return '' } })()`)}parseExpression(j){return j.replace(/(\w+\.)+\w+/g,(z)=>z.split(".").reduce((q,A,B)=>B===0?A:`${q}?.${A}`,"")).replace(/(.*)\?(.*):(.*)/,(z,q,A,B)=>`((${q}) ? (${A}) : (${B}))`)}constructor(){this.registerDefaultFilters()}registerDefaultFilters(){this.registerFilter("uppercase",(j)=>typeof j==="string"?j.toUpperCase():j),this.registerFilter("lowercase",(j)=>typeof j==="string"?j.toLowerCase():j),this.registerFilter("capitalize",(j)=>typeof j==="string"?j.charAt(0).toUpperCase()+j.slice(1).toLowerCase():j),this.registerFilter("currency",(j,z="$")=>typeof j==="number"?`${z}${j.toFixed(2)}`:j),this.registerFilter("truncate",(j,z=100,q="...")=>{if(typeof j!=="string")return j;return j.length>z?j.substring(0,z)+q:j}),this.registerFilter("dateFormat",(j,z)=>{let q=new Date(j);return z.replace("YYYY",q.getFullYear().toString()).replace("MM",(q.getMonth()+1).toString().padStart(2,"0")).replace("DD",q.getDate().toString().padStart(2,"0"))}),this.registerFilter("escape",(j)=>typeof j==="string"?j.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"):j)}}export{H as NetherTE};