UNPKG

surfstar

Version:

A modern template engine for Bun, inspired by Handlebars and EJS, with a focus on simplicity and performance.

7 lines (6 loc) 7.97 kB
// @bun var g={eachStart:{begin:2,end:7,wordBegin:3},eachEnd:{end:7}};var b;((i)=>{i.LEXER_ERROR="LEXER_ERROR";i.PARSER_ERROR="PARSER_ERROR";i.RENDERER_ERROR="RENDERER_ERROR";i.FILE_ERROR="FILE_ERROR";i.COMPILATION_ERROR="COMPILATION_ERROR"})(b||={});class c extends Error{type;filePath;lineNumber;columnNumber;originalError;constructor(n){let{message:e,type:r,filePath:o,lineNumber:t,columnNumber:i,originalError:s}=n,u=e;if(o){if(u+=` in ${o}`,t!==void 0){if(u+=`:${t}`,i!==void 0)u+=`:${i}`}}super(u);if(this.name="SurfstarError",this.type=r,this.filePath=o,this.lineNumber=t,this.columnNumber=i,this.originalError=s,Error.captureStackTrace)Error.captureStackTrace(this,c)}static lexerError(n,e={}){return new c({message:n,type:"LEXER_ERROR",...e})}static parserError(n,e={}){return new c({message:n,type:"PARSER_ERROR",...e})}static rendererError(n,e={}){return new c({message:n,type:"RENDERER_ERROR",...e})}static fileError(n,e={}){return new c({message:n,type:"FILE_ERROR",...e})}static compilationError(n,e={}){return new c({message:n,type:"COMPILATION_ERROR",...e})}}function m(n,e){let{filePath:r,lineNumber:o,columnNumber:t,defaultMessage:i,errorFactory:s}=e;if(n instanceof c)throw n;throw s(n instanceof Error?n.message:i,{filePath:r,lineNumber:o,columnNumber:t,originalError:n instanceof Error?n:void 0})}function d(n,e){let r=[],o="",t=1,i=1;try{for(let s=0;s<n.length;s++){let u=n[s];if(u===` `)t++,i=1;else i++;if(w(u,n[s+1])){if(o=R(r,o,t,i-1),S(n,s)){try{let E=I(n,s);F(E,t,i,e),r.push(l("EACH_START",E,t,i));let p=N(n,s);a(p,t,i,e),s+=p}catch(E){m(E,{filePath:e,lineNumber:t,columnNumber:i,defaultMessage:"Invalid each block syntax",errorFactory:c.lexerError})}continue}if(L(n,s)){r.push(l("EACH_END","/each",t,i));let E=N(n,s);a(E,t,i,e),s+=E;continue}r.push(l("OPEN_BRACE","{{",t,i)),s=f(s);continue}if(B(u,n[s+1])){try{o=H(r,o,t,i-o.length),r.push(l("CLOSE_BRACE","}}",t,i)),s=f(s)}catch(E){m(E,{filePath:e,lineNumber:t,columnNumber:i,defaultMessage:"Invalid variable syntax",errorFactory:c.lexerError})}continue}o+=u}return R(r,o,t,i),O(r,e),r}catch(s){if(s instanceof c)throw s;throw c.lexerError("Error during tokenization",{filePath:e,originalError:s instanceof Error?s:void 0})}}function O(n,e){let r=0,o=0;for(let t of n){if(t.type==="OPEN_BRACE")r++;if(t.type==="CLOSE_BRACE"){if(r--,r<0)throw c.lexerError("Unmatched closing brace",{filePath:e,lineNumber:t.line,columnNumber:t.column})}if(t.type==="EACH_START")o++;if(t.type==="EACH_END"){if(o--,o<0)throw c.lexerError("Unmatched {{/each}} tag",{filePath:e,lineNumber:t.line,columnNumber:t.column})}}if(r>0){let t=n[n.length-1];throw c.lexerError("Unclosed braces in template",{filePath:e,lineNumber:t.line,columnNumber:t.column})}if(o>0){let t=n[n.length-1];throw c.lexerError("Unclosed {{#each}} block in template",{filePath:e,lineNumber:t.line,columnNumber:t.column})}}function w(n,e){return n==="{"&&e==="{"}function B(n,e){return n==="}"&&e==="}"}function S(n,e){return n[e+g.eachStart.begin]==="#"&&n.slice(e+g.eachStart.wordBegin,e+g.eachStart.end)==="each"}function L(n,e){return n[e+2]==="/"&&n.slice(e+3,e+7)==="each"}function I(n,e){return n.slice(e+g.eachEnd.end).split("}}")[0].trim()}function N(n,e){let r=n.slice(e).indexOf("}}");return r>=0?r+1:-1}function R(n,e,r,o){if(e)return n.push(l("TEXT",e,r,o-e.length+1)),"";return e}function H(n,e,r,o){if(e){let t=e.trim();if(!t)throw c.lexerError("Empty variable name",{lineNumber:r,columnNumber:o});return n.push(l("VARIABLE",t,r,o)),""}return e}function l(n,e,r,o){return{type:n,value:e,line:r,column:o}}function f(n){return n+1}function F(n,e,r,o){if(!n)throw c.lexerError("Each block is missing array name",{filePath:o,lineNumber:e,columnNumber:r})}function a(n,e,r,o){if(n<0)throw c.lexerError("Unclosed tag",{filePath:o,lineNumber:e,columnNumber:r})}function y(n,e){try{let r=M(),o="",t=0;while(t<n.length){let i=n[t];if(i.type==="TEXT")o+=i.value;else if(i.type==="VARIABLE")o=T(r,o),A(r,i.value);else if(i.type==="EACH_START"){o=T(r,o);try{let[s,u]=k(n,t,e);r.content.push(s),t=u}catch(s){m(s,{filePath:e,lineNumber:i.line,columnNumber:i.column,defaultMessage:"Error parsing each block",errorFactory:c.parserError})}}t++}return T(r,o),r}catch(r){m(r,{filePath:e,defaultMessage:"Error parsing template",errorFactory:c.parserError})}}function M(){return{type:"TEMPLATE",content:[]}}function T(n,e){if(!e)return e;let r=X(n,e);if(r){let o={type:"TEXT",content:r};n.content.push(o)}return""}function X(n,e){if(n.type==="EACH")return v(n,e);return e}function v(n,e){if(n.content.length===0)return D(e);return U(e)}function D(n){return n.replace(/^\s+/,"")}function U(n){return n.replace(/\n/g,` `)}function A(n,e){let r={type:"VARIABLE",name:e};n.content.push(r)}function k(n,e,r){let o=n[e],t=$(o.value),i="",s=e+1;while(s<n.length){let u=n[s];if(V(u))return z(t,i),[t,s];try{[i,s]=j(u,i,t,n,s,r)}catch(E){m(E,{filePath:r,lineNumber:u.line,columnNumber:u.column,defaultMessage:"Error processing token in each block",errorFactory:c.parserError})}s++}throw c.parserError("Unclosed each block",{filePath:r,lineNumber:o.line,columnNumber:o.column})}function $(n){return{type:"EACH",arrayName:n,content:[]}}function V(n){return n.type==="EACH_END"}function z(n,e){if(!e)return;let r=W(e);T(n,r)}function W(n){return n.includes(` `)?n:` ${n}`}function j(n,e,r,o,t,i){if(q(n))return G(e,n,t);return J(n,e,r,o,t,i)}function q(n){return n.type==="TEXT"}function G(n,e,r){return[n+e.value,r]}function J(n,e,r,o,t,i){return[K(n,e,r,o,t,i),Z(n)?t:t]}function K(n,e,r,o,t,i){if(e)T(r,e);if(Q(n))A(r,n.value);else if(Y(n))P(r,o,t,i);return""}function Q(n){return n.type==="VARIABLE"}function Y(n){return n.type==="EACH_START"}function Z(n){return n.type==="EACH_START"}function P(n,e,r,o){try{let[t,i]=k(e,r,o);n.content.push(t),r=i}catch(t){if(t instanceof c)throw t;let i=e[r];throw c.parserError("Error processing nested each block",{filePath:o,lineNumber:i.line,columnNumber:i.column,originalError:t instanceof Error?t:void 0})}}function h(n,e,r){try{return nn(n,r),n.content.map((o)=>_(o,e,r)).join("")}catch(o){return m(o,{filePath:r,defaultMessage:"Error rendering template",errorFactory:c.rendererError})}}function nn(n,e){if(n.type!=="TEMPLATE")throw c.rendererError(`Expected template node but got ${n.type}`,{filePath:e})}function _(n,e,r){try{switch(n.type){case"TEXT":return n.content;case"VARIABLE":return en(n,e,r);case"EACH":return rn(n,e,r);default:throw c.rendererError(`Unknown node type: ${n.type}`,{filePath:r})}}catch(o){return m(o,{filePath:r,defaultMessage:`Error rendering ${n.type} node`,errorFactory:c.rendererError})}}function en(n,e,r){if(n.type!=="VARIABLE")throw c.rendererError(`Expected variable node but got ${n.type}`,{filePath:r});try{let o=n.name.split("."),t=x(e,o);return t!==void 0&&t!==null?String(t):""}catch(o){return m(o,{filePath:r,defaultMessage:`Error accessing variable '${n.name}'`,errorFactory:c.rendererError})}}function x(n,e){try{return e.reduce((r,o)=>{if(r===void 0||r===null)return;return r[o]},n)}catch(r){return}}function rn(n,e,r){if(n.type!=="EACH")throw c.rendererError(`Expected each node but got ${n.type}`,{filePath:r});try{let o=x(e,n.arrayName.split("."));if(o===void 0||o===null||!Array.isArray(o))return"";return o.map((t,i)=>{let s={...e,this:t,"@index":i};return n.content.map((u)=>_(u,s,r)).join("")}).join("")}catch(o){return m(o,{filePath:r,defaultMessage:`Error rendering each block for '${n.arrayName}'`,errorFactory:c.rendererError})}}import{existsSync as on}from"fs";async function C(n){try{return tn(n),await Bun.file(n).text()}catch(e){m(e,{filePath:n,defaultMessage:`Error loading file: ${n}`,errorFactory:c.fileError})}}function tn(n){if(!on(n))throw c.fileError(`File not found: ${n}`,{filePath:n})}async function cn(n,e){try{let r=await C(n),o=d(r,n),t=y(o,n);return h(t,e,n)}catch(r){m(r,{filePath:n,defaultMessage:"Error compiling template",errorFactory:c.compilationError})}}export{cn as compileTemplate,b as SurfstarErrorType,c as SurfstarError};