binja 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +14 -14
- package/dist/engines/index.d.ts +2 -1
- package/dist/engines/twig/index.d.ts +35 -0
- package/dist/engines/twig/lexer.d.ts +16 -0
- package/dist/engines/twig/parser.d.ts +20 -0
- package/dist/index.js +61 -61
- package/dist/lexer/tokens.d.ts +3 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
import*as X from"fs";import*as Y from"path";var P={and:"AND",or:"OR",not:"NOT",true:"NAME",false:"NAME",True:"NAME",False:"NAME",None:"NAME",none:"NAME",is:"NAME",in:"NAME"};var x={red:"\x1B[31m",yellow:"\x1B[33m",cyan:"\x1B[36m",gray:"\x1B[90m",bold:"\x1B[1m",dim:"\x1B[2m",reset:"\x1B[0m"},p=process.stdout?.isTTY!==!1;function q(A,B){return p?`${x[A]}${B}${x.reset}`:B}class
|
|
4
|
-
`)}function d(A,B,R){let
|
|
5
|
-
`),
|
|
6
|
-
`)}class D{state;variableStart;variableEnd;blockStart;blockEnd;commentStart;commentEnd;constructor(A,B={}){this.state={source:A,pos:0,line:1,column:1,tokens:[]},this.variableStart=B.variableStart??"{{",this.variableEnd=B.variableEnd??"}}",this.blockStart=B.blockStart??"{%",this.blockEnd=B.blockEnd??"%}",this.commentStart=B.commentStart??"{#",this.commentEnd=B.commentEnd??"#}"}tokenize(){while(!this.isAtEnd())this.scanToken();return this.addToken("EOF",""),this.state.tokens}scanToken(){if(this.match(this.variableStart)){this.addToken("VARIABLE_START",this.variableStart),this.scanExpression(this.variableEnd,"VARIABLE_END");return}if(this.match(this.blockStart)){let A=this.peek()==="-";if(A)this.advance();let B=this.state.pos;if(this.skipWhitespace(),this.checkWord("raw")||this.checkWord("verbatim")){let R=this.checkWord("raw")?"raw":"verbatim";this.scanRawBlock(R,A);return}this.state.pos=B,this.addToken("BLOCK_START",this.blockStart+(A?"-":"")),this.scanExpression(this.blockEnd,"BLOCK_END");return}if(this.match(this.commentStart)){this.scanComment();return}this.scanText()}checkWord(A){let B=this.state.pos;for(let
|
|
7
|
-
`)this.state.line++,this.state.column=0;this.advance()}throw new
|
|
8
|
-
`)this.state.line++,this.state.column=0;this.advance()}if(this.state.pos>A){let
|
|
9
|
-
`)this.state.line++,this.state.column=0;this.advance()}if(this.isAtEnd())throw new
|
|
10
|
-
`)this.state.line++,this.state.column=0;this.advance()}if(!this.isAtEnd())this.match(this.commentEnd)}isAtEnd(){return this.state.pos>=this.state.source.length}peek(){if(this.isAtEnd())return"\x00";return this.state.source[this.state.pos]}peekNext(){if(this.state.pos+1>=this.state.source.length)return"\x00";return this.state.source[this.state.pos+1]}advance(){let A=this.state.source[this.state.pos];return this.state.pos++,this.state.column++,A}match(A,B=0){let R=this.state.source,
|
|
3
|
+
import*as X from"fs";import*as Y from"path";var P={and:"AND",or:"OR",not:"NOT",true:"NAME",false:"NAME",True:"NAME",False:"NAME",None:"NAME",none:"NAME",is:"NAME",in:"NAME"};var x={red:"\x1B[31m",yellow:"\x1B[33m",cyan:"\x1B[36m",gray:"\x1B[90m",bold:"\x1B[1m",dim:"\x1B[2m",reset:"\x1B[0m"},p=process.stdout?.isTTY!==!1;function q(A,B){return p?`${x[A]}${B}${x.reset}`:B}class I extends Error{line;column;source;templateName;suggestion;constructor(A,B){let R=v("TemplateSyntaxError",A,B);super(R);this.name="TemplateSyntaxError",this.line=B.line,this.column=B.column,this.source=B.source,this.templateName=B.templateName,this.suggestion=B.suggestion}}function v(A,B,R){let O=[],K=R.templateName?`${R.templateName}:${R.line}:${R.column}`:`line ${R.line}, column ${R.column}`;if(O.push(`${q("red",q("bold",A))}: ${B} at ${q("cyan",K)}`),R.source)O.push(""),O.push(d(R.source,R.line,R.column));if(R.suggestion)O.push(""),O.push(`${q("yellow","Did you mean")}: ${q("cyan",R.suggestion)}?`);if(R.availableOptions&&R.availableOptions.length>0){O.push("");let Q=R.availableOptions.slice(0,8),M=R.availableOptions.length>8?` ${q("gray",`... and ${R.availableOptions.length-8} more`)}`:"";O.push(`${q("gray","Available")}: ${Q.join(", ")}${M}`)}return O.join(`
|
|
4
|
+
`)}function d(A,B,R){let O=A.split(`
|
|
5
|
+
`),K=[],Q=Math.max(1,B-2),M=Math.min(O.length,B+1),$=String(M).length;for(let Z=Q;Z<=M;Z++){let U=O[Z-1]||"",G=String(Z).padStart($," ");if(Z===B){K.push(`${q("red"," \u2192")} ${q("gray",G)} ${q("dim","\u2502")} ${U}`);let H=" ".repeat($+4+Math.max(0,R-1)),z=q("red","^");K.push(`${H}${z}`)}else K.push(` ${q("gray",G)} ${q("dim","\u2502")} ${q("gray",U)}`)}return K.join(`
|
|
6
|
+
`)}class D{state;variableStart;variableEnd;blockStart;blockEnd;commentStart;commentEnd;constructor(A,B={}){this.state={source:A,pos:0,line:1,column:1,tokens:[]},this.variableStart=B.variableStart??"{{",this.variableEnd=B.variableEnd??"}}",this.blockStart=B.blockStart??"{%",this.blockEnd=B.blockEnd??"%}",this.commentStart=B.commentStart??"{#",this.commentEnd=B.commentEnd??"#}"}tokenize(){while(!this.isAtEnd())this.scanToken();return this.addToken("EOF",""),this.state.tokens}scanToken(){if(this.match(this.variableStart)){this.addToken("VARIABLE_START",this.variableStart),this.scanExpression(this.variableEnd,"VARIABLE_END");return}if(this.match(this.blockStart)){let A=this.peek()==="-";if(A)this.advance();let B=this.state.pos;if(this.skipWhitespace(),this.checkWord("raw")||this.checkWord("verbatim")){let R=this.checkWord("raw")?"raw":"verbatim";this.scanRawBlock(R,A);return}this.state.pos=B,this.addToken("BLOCK_START",this.blockStart+(A?"-":"")),this.scanExpression(this.blockEnd,"BLOCK_END");return}if(this.match(this.commentStart)){this.scanComment();return}this.scanText()}checkWord(A){let B=this.state.pos;for(let O=0;O<A.length;O++)if(this.state.source[B+O]?.toLowerCase()!==A[O])return!1;let R=this.state.source[B+A.length];return!R||!this.isAlphaNumeric(R)}scanRawBlock(A,B){let R=this.state.line,O=this.state.column;for(let M=0;M<A.length;M++)this.advance();if(this.skipWhitespace(),this.peek()==="-")this.advance();if(!this.match(this.blockEnd))throw new I(`Expected '${this.blockEnd}' after '${A}'`,{line:this.state.line,column:this.state.column,source:this.state.source});let K=`end${A}`,Q=this.state.pos;while(!this.isAtEnd()){if(this.check(this.blockStart)){let M=this.state.pos,$=this.state.line,Z=this.state.column;if(this.match(this.blockStart),this.peek()==="-")this.advance();if(this.skipWhitespace(),this.checkWord(K)){let U=this.state.source.slice(Q,M);if(U.length>0)this.state.tokens.push({type:"TEXT",value:U,line:R,column:O});for(let G=0;G<K.length;G++)this.advance();if(this.skipWhitespace(),this.peek()==="-")this.advance();if(!this.match(this.blockEnd))throw new I(`Expected '${this.blockEnd}' after '${K}'`,{line:this.state.line,column:this.state.column,source:this.state.source});return}this.state.pos=M,this.state.line=$,this.state.column=Z}if(this.peek()===`
|
|
7
|
+
`)this.state.line++,this.state.column=0;this.advance()}throw new I(`Unclosed '${A}' block`,{line:R,column:O,source:this.state.source,suggestion:`Add {% end${A} %} to close the block`})}scanText(){let A=this.state.pos,B=this.state.line,R=this.state.column;while(!this.isAtEnd()){if(this.check(this.variableStart)||this.check(this.blockStart)||this.check(this.commentStart))break;if(this.peek()===`
|
|
8
|
+
`)this.state.line++,this.state.column=0;this.advance()}if(this.state.pos>A){let O=this.state.source.slice(A,this.state.pos);this.state.tokens.push({type:"TEXT",value:O,line:B,column:R})}}scanExpression(A,B){this.skipWhitespace();while(!this.isAtEnd()){if(this.skipWhitespace(),this.peek()==="-"&&this.check(A,1))this.advance();if(this.match(A)){this.addToken(B,A);return}this.scanExpressionToken()}throw new I("Unclosed template tag",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing delimiter '${A}'`})}scanExpressionToken(){if(this.skipWhitespace(),this.isAtEnd())return;let A=this.peek();if(A==='"'||A==="'"){this.scanString(A);return}if(this.isDigit(A)){this.scanNumber();return}if(this.isAlpha(A)||A==="_"){this.scanIdentifier();return}this.scanOperator()}scanString(A){this.advance();let B=this.state.pos;while(!this.isAtEnd()&&this.peek()!==A){if(this.peek()==="\\"&&this.peekNext()===A)this.advance();if(this.peek()===`
|
|
9
|
+
`)this.state.line++,this.state.column=0;this.advance()}if(this.isAtEnd())throw new I("Unterminated string literal",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing quote '${A}'`});let R=this.state.source.slice(B,this.state.pos);this.advance(),this.addToken("STRING",R)}scanNumber(){let A=this.state.pos;while(this.isDigit(this.peek()))this.advance();if(this.peek()==="."&&this.isDigit(this.peekNext())){this.advance();while(this.isDigit(this.peek()))this.advance()}let B=this.state.source.slice(A,this.state.pos);this.addToken("NUMBER",B)}scanIdentifier(){let A=this.state.pos;while(this.isAlphaNumeric(this.peek())||this.peek()==="_")this.advance();let B=this.state.source.slice(A,this.state.pos),R=P[B]??"NAME";this.addToken(R,B)}scanOperator(){let A=this.advance();switch(A){case".":this.addToken("DOT",A);break;case",":this.addToken("COMMA",A);break;case":":this.addToken("COLON",A);break;case"|":this.addToken("PIPE",A);break;case"(":this.addToken("LPAREN",A);break;case")":this.addToken("RPAREN",A);break;case"[":this.addToken("LBRACKET",A);break;case"]":this.addToken("RBRACKET",A);break;case"{":this.addToken("LBRACE",A);break;case"}":this.addToken("RBRACE",A);break;case"+":this.addToken("ADD",A);break;case"-":this.addToken("SUB",A);break;case"*":this.addToken("MUL",A);break;case"/":this.addToken("DIV",A);break;case"%":this.addToken("MOD",A);break;case"~":this.addToken("TILDE",A);break;case"=":if(this.match("="))this.addToken("EQ","==");else this.addToken("ASSIGN","=");break;case"!":if(this.match("="))this.addToken("NE","!=");else throw new I("Unexpected character '!'",{line:this.state.line,column:this.state.column-1,source:this.state.source,suggestion:"Use '!=' for not-equal comparison or 'not' for negation"});break;case"<":if(this.match("="))this.addToken("LE","<=");else this.addToken("LT","<");break;case">":if(this.match("="))this.addToken("GE",">=");else this.addToken("GT",">");break;case"?":if(this.match("?"))this.addToken("NULLCOALESCE","??");else this.addToken("QUESTION","?");break;default:if(!this.isWhitespace(A))throw new I(`Unexpected character '${A}'`,{line:this.state.line,column:this.state.column-1,source:this.state.source})}}scanComment(){while(!this.isAtEnd()&&!this.check(this.commentEnd)){if(this.peek()===`
|
|
10
|
+
`)this.state.line++,this.state.column=0;this.advance()}if(!this.isAtEnd())this.match(this.commentEnd)}isAtEnd(){return this.state.pos>=this.state.source.length}peek(){if(this.isAtEnd())return"\x00";return this.state.source[this.state.pos]}peekNext(){if(this.state.pos+1>=this.state.source.length)return"\x00";return this.state.source[this.state.pos+1]}advance(){let A=this.state.source[this.state.pos];return this.state.pos++,this.state.column++,A}match(A,B=0){let R=this.state.source,O=this.state.pos+B,K=A.length;if(O+K>R.length)return!1;for(let Q=0;Q<K;Q++)if(R[O+Q]!==A[Q])return!1;if(B===0)this.state.pos+=K,this.state.column+=K;return!0}check(A,B=0){let R=this.state.source,O=this.state.pos+B,K=A.length;if(O+K>R.length)return!1;for(let Q=0;Q<K;Q++)if(R[O+Q]!==A[Q])return!1;return!0}skipWhitespace(){while(!this.isAtEnd()&&this.isWhitespace(this.peek())){if(this.peek()===`
|
|
11
11
|
`)this.state.line++,this.state.column=0;this.advance()}}isWhitespace(A){return A===" "||A==="\t"||A===`
|
|
12
|
-
`||A==="\r"}isDigit(A){let B=A.charCodeAt(0);return B>=48&&B<=57}isAlpha(A){let B=A.charCodeAt(0);return B>=97&&B<=122||B>=65&&B<=90}isAlphaNumeric(A){let B=A.charCodeAt(0);return B>=48&&B<=57||B>=97&&B<=122||B>=65&&B<=90}addToken(A,B){this.state.tokens.push({type:A,value:B,line:this.state.line,column:this.state.column-B.length})}}class S{tokens;current=0;source;constructor(A,B){this.tokens=A,this.source=B}parse(){let A=[];while(!this.isAtEnd()){let B=this.parseStatement();if(B)A.push(B)}return{type:"Template",body:A,line:1,column:1}}parseStatement(){switch(this.peek().type){case"TEXT":return this.parseText();case"VARIABLE_START":return this.parseOutput();case"BLOCK_START":return this.parseBlock();case"EOF":return null;default:return this.advance(),null}}parseText(){let A=this.advance();return{type:"Text",value:A.value,line:A.line,column:A.column}}parseOutput(){let A=this.advance(),B=this.parseExpression();return this.expect("VARIABLE_END"),{type:"Output",expression:B,line:A.line,column:A.column}}parseBlock(){let A=this.advance(),B=this.expect("NAME");switch(B.value){case"if":return this.parseIf(A);case"for":return this.parseFor(A);case"block":return this.parseBlockTag(A);case"extends":return this.parseExtends(A);case"include":return this.parseInclude(A);case"set":return this.parseSet(A);case"with":return this.parseWith(A);case"load":return this.parseLoad(A);case"url":return this.parseUrl(A);case"static":return this.parseStatic(A);case"now":return this.parseNow(A);case"comment":return this.parseComment(A);case"spaceless":case"autoescape":case"verbatim":return this.parseSimpleBlock(A,B.value);case"cycle":return this.parseCycle(A);case"firstof":return this.parseFirstof(A);case"ifchanged":return this.parseIfchanged(A);case"regroup":return this.parseRegroup(A);case"widthratio":return this.parseWidthratio(A);case"lorem":return this.parseLorem(A);case"csrf_token":return this.parseCsrfToken(A);case"debug":return this.parseDebug(A);case"templatetag":return this.parseTemplatetag(A);case"ifequal":return this.parseIfequal(A,!1);case"ifnotequal":return this.parseIfequal(A,!0);default:return this.skipToBlockEnd(),null}}parseIf(A){let B=this.parseExpression();this.expect("BLOCK_END");let R=[],K=[],O=[];while(!this.isAtEnd()){if(this.checkBlockTag("elif")||this.checkBlockTag("else")||this.checkBlockTag("endif"))break;let Q=this.parseStatement();if(Q)R.push(Q)}while(this.checkBlockTag("elif")){this.advance(),this.advance();let Q=this.parseExpression();this.expect("BLOCK_END");let U=[];while(!this.isAtEnd()){if(this.checkBlockTag("elif")||this.checkBlockTag("else")||this.checkBlockTag("endif"))break;let M=this.parseStatement();if(M)U.push(M)}K.push({test:Q,body:U})}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endif"))break;let Q=this.parseStatement();if(Q)O.push(Q)}}return this.expectBlockTag("endif"),{type:"If",test:B,body:R,elifs:K,else_:O,line:A.line,column:A.column}}parseFor(A){let B,R=this.expect("NAME").value;if(this.check("COMMA")){let Z=[R];while(this.match("COMMA"))Z.push(this.expect("NAME").value);B=Z}else B=R;let K=this.expect("NAME");if(K.value!=="in")throw this.error(`Expected 'in' in for loop, got '${K.value}'`);let O=this.parseExpression(),Q=this.check("NAME")&&this.peek().value==="recursive";if(Q)this.advance();this.expect("BLOCK_END");let U=[],M=[];while(!this.isAtEnd()){if(this.checkBlockTag("empty")||this.checkBlockTag("else")||this.checkBlockTag("endfor"))break;let Z=this.parseStatement();if(Z)U.push(Z)}if(this.checkBlockTag("empty")||this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endfor"))break;let Z=this.parseStatement();if(Z)M.push(Z)}}return this.expectBlockTag("endfor"),{type:"For",target:B,iter:O,body:U,else_:M,recursive:Q,line:A.line,column:A.column}}parseBlockTag(A){let B=this.expect("NAME").value,R=this.check("NAME")&&this.peek().value==="scoped";if(R)this.advance();this.expect("BLOCK_END");let K=[];while(!this.isAtEnd()){if(this.checkBlockTag("endblock"))break;let O=this.parseStatement();if(O)K.push(O)}if(this.advance(),this.advance(),this.check("NAME"))this.advance();return this.expect("BLOCK_END"),{type:"Block",name:B,body:K,scoped:R,line:A.line,column:A.column}}parseExtends(A){let B=this.parseExpression();return this.expect("BLOCK_END"),{type:"Extends",template:B,line:A.line,column:A.column}}parseInclude(A){let B=this.parseExpression(),R=null,K=!1,O=!1;while(this.check("NAME")){let Q=this.peek().value;if(Q==="ignore"&&this.peekNext()?.value==="missing")this.advance(),this.advance(),O=!0;else if(Q==="with")this.advance(),R=this.parseKeywordArgs();else if(Q==="only")this.advance(),K=!0;else if(Q==="without"){if(this.advance(),this.check("NAME")&&this.peek().value==="context")this.advance(),K=!0}else break}return this.expect("BLOCK_END"),{type:"Include",template:B,context:R,only:K,ignoreMissing:O,line:A.line,column:A.column}}parseSet(A){let B=this.expect("NAME").value;this.expect("ASSIGN");let R=this.parseExpression();return this.expect("BLOCK_END"),{type:"Set",target:B,value:R,line:A.line,column:A.column}}parseWith(A){let B=[];do{let K=this.expect("NAME").value;this.expect("ASSIGN");let O=this.parseExpression();B.push({target:K,value:O})}while(this.match("COMMA")||this.check("NAME")&&this.peekNext()?.type==="ASSIGN");this.expect("BLOCK_END");let R=[];while(!this.isAtEnd()){if(this.checkBlockTag("endwith"))break;let K=this.parseStatement();if(K)R.push(K)}return this.expectBlockTag("endwith"),{type:"With",assignments:B,body:R,line:A.line,column:A.column}}parseLoad(A){let B=[];while(this.check("NAME"))B.push(this.advance().value);return this.expect("BLOCK_END"),{type:"Load",names:B,line:A.line,column:A.column}}parseUrl(A){let B=this.parseExpression(),R=[],K={},O=null;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){this.advance(),O=this.expect("NAME").value;break}if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let Q=this.advance().value;this.advance(),K[Q]=this.parseExpression()}else R.push(this.parseExpression())}return this.expect("BLOCK_END"),{type:"Url",name:B,args:R,kwargs:K,asVar:O,line:A.line,column:A.column}}parseStatic(A){let B=this.parseExpression(),R=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),R=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Static",path:B,asVar:R,line:A.line,column:A.column}}parseNow(A){let B=this.parseExpression(),R=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),R=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Now",format:B,asVar:R,line:A.line,column:A.column}}parseComment(A){this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endcomment"))break;this.advance()}return this.expectBlockTag("endcomment"),null}parseSimpleBlock(A,B){this.skipToBlockEnd();let R=`end${B}`;while(!this.isAtEnd()){if(this.checkBlockTag(R))break;this.advance()}if(this.checkBlockTag(R))this.advance(),this.advance(),this.expect("BLOCK_END");return null}parseCycle(A){let B=[],R=null,K=!1;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){if(this.advance(),R=this.expect("NAME").value,this.check("NAME")&&this.peek().value==="silent")this.advance(),K=!0;break}B.push(this.parseExpression())}return this.expect("BLOCK_END"),{type:"Cycle",values:B,asVar:R,silent:K,line:A.line,column:A.column}}parseFirstof(A){let B=[],R=null,K=null;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){this.advance(),K=this.expect("NAME").value;break}B.push(this.parseExpression())}if(B.length>0){let O=B[B.length-1];if(O.type==="Literal"&&typeof O.value==="string")R=B.pop()}return this.expect("BLOCK_END"),{type:"Firstof",values:B,fallback:R,asVar:K,line:A.line,column:A.column}}parseIfchanged(A){let B=[];while(!this.check("BLOCK_END"))B.push(this.parseExpression());this.expect("BLOCK_END");let R=[],K=[];while(!this.isAtEnd()){if(this.checkBlockTag("else")||this.checkBlockTag("endifchanged"))break;let O=this.parseStatement();if(O)R.push(O)}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endifchanged"))break;let O=this.parseStatement();if(O)K.push(O)}}return this.expectBlockTag("endifchanged"),{type:"Ifchanged",values:B,body:R,else_:K,line:A.line,column:A.column}}parseRegroup(A){let B=this.parseExpression();this.expectName("by");let R=this.expect("NAME").value;this.expectName("as");let K=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Regroup",target:B,key:R,asVar:K,line:A.line,column:A.column}}parseWidthratio(A){let B=this.parseExpression(),R=this.parseExpression(),K=this.parseExpression(),O=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),O=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Widthratio",value:B,maxValue:R,maxWidth:K,asVar:O,line:A.line,column:A.column}}parseLorem(A){let B=null,R="p",K=!1;if(this.check("NUMBER"))B={type:"Literal",value:parseInt(this.advance().value,10),line:A.line,column:A.column};if(this.check("NAME")){let O=this.peek().value.toLowerCase();if(O==="w"||O==="p"||O==="b")R=O,this.advance()}if(this.check("NAME")&&this.peek().value==="random")K=!0,this.advance();return this.expect("BLOCK_END"),{type:"Lorem",count:B,method:R,random:K,line:A.line,column:A.column}}parseCsrfToken(A){return this.expect("BLOCK_END"),{type:"CsrfToken",line:A.line,column:A.column}}parseDebug(A){return this.expect("BLOCK_END"),{type:"Debug",line:A.line,column:A.column}}parseTemplatetag(A){let B=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Templatetag",tagType:B,line:A.line,column:A.column}}parseIfequal(A,B){let R=this.parseExpression(),K=this.parseExpression();this.expect("BLOCK_END");let O={type:"Compare",left:R,ops:[{operator:B?"!=":"==",right:K}],line:A.line,column:A.column},Q=[],U=[],M=B?"endifnotequal":"endifequal";while(!this.isAtEnd()){if(this.checkBlockTag("else")||this.checkBlockTag(M))break;let Z=this.parseStatement();if(Z)Q.push(Z)}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag(M))break;let Z=this.parseStatement();if(Z)U.push(Z)}}return this.expectBlockTag(M),{type:"If",test:O,body:Q,elifs:[],else_:U,line:A.line,column:A.column}}parseExpression(){return this.parseConditional()}parseConditional(){let A=this.parseOr();if(this.check("NAME")&&this.peek().value==="if"){this.advance();let B=this.parseOr();this.expectName("else");let R=this.parseConditional();A={type:"Conditional",test:B,trueExpr:A,falseExpr:R,line:A.line,column:A.column}}return A}parseOr(){let A=this.parseAnd();while(this.check("OR")||this.check("NAME")&&this.peek().value==="or"){this.advance();let B=this.parseAnd();A={type:"BinaryOp",operator:"or",left:A,right:B,line:A.line,column:A.column}}return A}parseAnd(){let A=this.parseNot();while(this.check("AND")||this.check("NAME")&&this.peek().value==="and"){this.advance();let B=this.parseNot();A={type:"BinaryOp",operator:"and",left:A,right:B,line:A.line,column:A.column}}return A}parseNot(){if(this.check("NOT")||this.check("NAME")&&this.peek().value==="not"){let A=this.advance();return{type:"UnaryOp",operator:"not",operand:this.parseNot(),line:A.line,column:A.column}}return this.parseCompare()}parseCompare(){let A=this.parseAddSub(),B=[];while(!0){let R=null;if(this.match("EQ"))R="==";else if(this.match("NE"))R="!=";else if(this.match("LT"))R="<";else if(this.match("GT"))R=">";else if(this.match("LE"))R="<=";else if(this.match("GE"))R=">=";else if(this.check("NAME")){let O=this.peek().value;if(O==="in")this.advance(),R="in";else if(O==="not"&&this.peekNext()?.value==="in")this.advance(),this.advance(),R="not in";else if(O==="is"){this.advance();let Q=this.check("NOT");if(Q)this.advance();let M=this.expect("NAME").value,Z=[];if(this.match("LPAREN")){while(!this.check("RPAREN"))if(Z.push(this.parseExpression()),!this.check("RPAREN"))this.expect("COMMA");this.expect("RPAREN")}A={type:"TestExpr",node:A,test:M,args:Z,negated:Q,line:A.line,column:A.column};continue}}if(!R)break;let K=this.parseAddSub();B.push({operator:R,right:K})}if(B.length===0)return A;return{type:"Compare",left:A,ops:B,line:A.line,column:A.column}}parseAddSub(){let A=this.parseMulDiv();while(this.check("ADD")||this.check("SUB")||this.check("TILDE")){let B=this.advance(),R=this.parseMulDiv();A={type:"BinaryOp",operator:B.value,left:A,right:R,line:A.line,column:A.column}}return A}parseMulDiv(){let A=this.parseUnary();while(this.check("MUL")||this.check("DIV")||this.check("MOD")){let B=this.advance(),R=this.parseUnary();A={type:"BinaryOp",operator:B.value,left:A,right:R,line:A.line,column:A.column}}return A}parseUnary(){if(this.check("SUB")||this.check("ADD")){let A=this.advance(),B=this.parseUnary();return{type:"UnaryOp",operator:A.value,operand:B,line:A.line,column:A.column}}return this.parseFilter()}parseFilter(){let A=this.parsePostfix();while(this.match("PIPE")){let B=this.expect("NAME").value,R=[],K={};if(this.match("COLON"))if(this.check("SUB")||this.check("ADD")){let O=this.advance(),Q=this.parsePostfix();R.push({type:"UnaryOp",operator:O.value,operand:Q,line:O.line,column:O.column})}else R.push(this.parsePostfix());else if(this.match("LPAREN")){while(!this.check("RPAREN")){if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let O=this.advance().value;this.advance(),K[O]=this.parseExpression()}else R.push(this.parseExpression());if(!this.check("RPAREN"))this.expect("COMMA")}this.expect("RPAREN")}A={type:"FilterExpr",node:A,filter:B,args:R,kwargs:K,line:A.line,column:A.column}}return A}parsePostfix(){let A=this.parsePrimary();while(!0)if(this.match("DOT")){let B;if(this.check("NUMBER"))B=this.advance().value;else B=this.expect("NAME").value;A={type:"GetAttr",object:A,attribute:B,line:A.line,column:A.column}}else if(this.match("LBRACKET")){let B=this.parseExpression();this.expect("RBRACKET"),A={type:"GetItem",object:A,index:B,line:A.line,column:A.column}}else if(this.match("LPAREN")){let B=[],R={};while(!this.check("RPAREN")){if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let K=this.advance().value;this.advance(),R[K]=this.parseExpression()}else B.push(this.parseExpression());if(!this.check("RPAREN"))this.expect("COMMA")}this.expect("RPAREN"),A={type:"FunctionCall",callee:A,args:B,kwargs:R,line:A.line,column:A.column}}else break;return A}parsePrimary(){let A=this.peek();if(this.match("STRING"))return{type:"Literal",value:A.value,line:A.line,column:A.column};if(this.match("NUMBER"))return{type:"Literal",value:A.value.includes(".")?parseFloat(A.value):parseInt(A.value,10),line:A.line,column:A.column};if(this.check("NAME")){let B=this.advance().value;if(B==="true"||B==="True")return{type:"Literal",value:!0,line:A.line,column:A.column};if(B==="false"||B==="False")return{type:"Literal",value:!1,line:A.line,column:A.column};if(B==="none"||B==="None"||B==="null")return{type:"Literal",value:null,line:A.line,column:A.column};return{type:"Name",name:B,line:A.line,column:A.column}}if(this.match("LPAREN")){let B=this.parseExpression();return this.expect("RPAREN"),B}if(this.match("LBRACKET")){let B=[];while(!this.check("RBRACKET"))if(B.push(this.parseExpression()),!this.check("RBRACKET"))this.expect("COMMA");return this.expect("RBRACKET"),{type:"Array",elements:B,line:A.line,column:A.column}}if(this.match("LBRACE")){let B=[];while(!this.check("RBRACE")){let R=this.parseExpression();this.expect("COLON");let K=this.parseExpression();if(B.push({key:R,value:K}),!this.check("RBRACE"))this.expect("COMMA")}return this.expect("RBRACE"),{type:"Object",pairs:B,line:A.line,column:A.column}}throw this.error(`Unexpected token: ${A.type} (${A.value})`)}parseKeywordArgs(){let A={};while(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let B=this.advance().value;this.advance(),A[B]=this.parseExpression()}return A}checkBlockTag(A){if(this.peek().type!=="BLOCK_START")return!1;let B=this.current+1;if(B>=this.tokens.length)return!1;let R=this.tokens[B];return R.type==="NAME"&&R.value===A}expectBlockTag(A){this.advance();let B=this.expect("NAME");if(B.value!==A)throw this.error(`Expected '${A}', got '${B.value}'`);this.expect("BLOCK_END")}expectName(A){let B=this.expect("NAME");if(B.value!==A)throw this.error(`Expected '${A}', got '${B.value}'`)}skipToBlockEnd(){while(!this.isAtEnd()&&!this.check("BLOCK_END"))this.advance();if(this.check("BLOCK_END"))this.advance()}isAtEnd(){return this.peek().type==="EOF"}peek(){return this.tokens[this.current]}peekNext(){if(this.current+1>=this.tokens.length)return null;return this.tokens[this.current+1]}advance(){if(!this.isAtEnd())this.current++;return this.tokens[this.current-1]}check(A){if(this.isAtEnd())return!1;return this.peek().type===A}match(A){if(this.check(A))return this.advance(),!0;return!1}expect(A){if(this.check(A))return this.advance();let B=this.peek();throw this.error(`Expected ${A}, got ${B.type} (${B.value})`)}error(A){let B=this.peek();return new H(A,{line:B.line,column:B.column,source:this.source})}}function f(A,B={}){return new y(B).compile(A)}class y{options;indent=0;varCounter=0;loopStack=[];localVars=[];constructor(A={}){this.options={functionName:A.functionName??"render",inlineHelpers:A.inlineHelpers??!0,minify:A.minify??!1,autoescape:A.autoescape??!0}}pushScope(){this.localVars.push(new Set)}popScope(){this.localVars.pop()}addLocalVar(A){if(this.localVars.length>0)this.localVars[this.localVars.length-1].add(A)}isLocalVar(A){for(let B=this.localVars.length-1;B>=0;B--)if(this.localVars[B].has(A))return!0;return!1}compile(A){let B=this.compileNodes(A.body),R=this.options.minify?"":`
|
|
13
|
-
`;return`function ${this.options.functionName}(__ctx) {${R} let __out = '';${R}`+B+` return __out;${R}}`}compileNodes(A){return A.map((B)=>this.compileNode(B)).join("")}compileNode(A){switch(A.type){case"Text":return this.compileText(A);case"Output":return this.compileOutput(A);case"If":return this.compileIf(A);case"For":return this.compileFor(A);case"Set":return this.compileSet(A);case"With":return this.compileWith(A);case"Comment":return"";case"Extends":case"Block":case"Include":throw Error(`AOT compilation does not support '${A.type}' - use Environment.render() for templates with inheritance`);case"Url":case"Static":throw Error(`AOT compilation does not support '${A.type}' tag - use Environment.render() with urlResolver/staticResolver`);default:throw Error(`Unknown node type in AOT compiler: ${A.type}`)}}compileText(A){return` __out += ${JSON.stringify(A.value)};${this.nl()}`}compileOutput(A){let B=this.compileExpr(A.expression);if(this.options.autoescape&&!this.isMarkedSafe(A.expression))return` __out += escape(${B});${this.nl()}`;return` __out += (${B}) ?? '';${this.nl()}`}compileIf(A){let B="",R=this.compileExpr(A.test);B+=` if (isTruthy(${R})) {${this.nl()}`,B+=this.compileNodes(A.body),B+=" }";for(let
|
|
14
|
-
`}}function k(A,B){return new g(B).flatten(A)}function N(A){return new m().check(A)}class g{loader;maxDepth;blocks=new Map;depth=0;constructor(A){this.loader=A.loader,this.maxDepth=A.maxDepth??10}flatten(A){return this.blocks.clear(),this.depth=0,this.processTemplate(A)}processTemplate(A,B=!0){if(this.depth>this.maxDepth)throw Error(`Maximum template inheritance depth (${this.maxDepth}) exceeded`);this.collectBlocks(A.body,B);let R=this.findExtends(A.body);if(R){let
|
|
12
|
+
`||A==="\r"}isDigit(A){let B=A.charCodeAt(0);return B>=48&&B<=57}isAlpha(A){let B=A.charCodeAt(0);return B>=97&&B<=122||B>=65&&B<=90}isAlphaNumeric(A){let B=A.charCodeAt(0);return B>=48&&B<=57||B>=97&&B<=122||B>=65&&B<=90}addToken(A,B){this.state.tokens.push({type:A,value:B,line:this.state.line,column:this.state.column-B.length})}}class S{tokens;current=0;source;constructor(A,B){this.tokens=A,this.source=B}parse(){let A=[];while(!this.isAtEnd()){let B=this.parseStatement();if(B)A.push(B)}return{type:"Template",body:A,line:1,column:1}}parseStatement(){switch(this.peek().type){case"TEXT":return this.parseText();case"VARIABLE_START":return this.parseOutput();case"BLOCK_START":return this.parseBlock();case"EOF":return null;default:return this.advance(),null}}parseText(){let A=this.advance();return{type:"Text",value:A.value,line:A.line,column:A.column}}parseOutput(){let A=this.advance(),B=this.parseExpression();return this.expect("VARIABLE_END"),{type:"Output",expression:B,line:A.line,column:A.column}}parseBlock(){let A=this.advance(),B=this.expect("NAME");switch(B.value){case"if":return this.parseIf(A);case"for":return this.parseFor(A);case"block":return this.parseBlockTag(A);case"extends":return this.parseExtends(A);case"include":return this.parseInclude(A);case"set":return this.parseSet(A);case"with":return this.parseWith(A);case"load":return this.parseLoad(A);case"url":return this.parseUrl(A);case"static":return this.parseStatic(A);case"now":return this.parseNow(A);case"comment":return this.parseComment(A);case"spaceless":case"autoescape":case"verbatim":return this.parseSimpleBlock(A,B.value);case"cycle":return this.parseCycle(A);case"firstof":return this.parseFirstof(A);case"ifchanged":return this.parseIfchanged(A);case"regroup":return this.parseRegroup(A);case"widthratio":return this.parseWidthratio(A);case"lorem":return this.parseLorem(A);case"csrf_token":return this.parseCsrfToken(A);case"debug":return this.parseDebug(A);case"templatetag":return this.parseTemplatetag(A);case"ifequal":return this.parseIfequal(A,!1);case"ifnotequal":return this.parseIfequal(A,!0);default:return this.skipToBlockEnd(),null}}parseIf(A){let B=this.parseExpression();this.expect("BLOCK_END");let R=[],O=[],K=[];while(!this.isAtEnd()){if(this.checkBlockTag("elif")||this.checkBlockTag("else")||this.checkBlockTag("endif"))break;let Q=this.parseStatement();if(Q)R.push(Q)}while(this.checkBlockTag("elif")){this.advance(),this.advance();let Q=this.parseExpression();this.expect("BLOCK_END");let M=[];while(!this.isAtEnd()){if(this.checkBlockTag("elif")||this.checkBlockTag("else")||this.checkBlockTag("endif"))break;let $=this.parseStatement();if($)M.push($)}O.push({test:Q,body:M})}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endif"))break;let Q=this.parseStatement();if(Q)K.push(Q)}}return this.expectBlockTag("endif"),{type:"If",test:B,body:R,elifs:O,else_:K,line:A.line,column:A.column}}parseFor(A){let B,R=this.expect("NAME").value;if(this.check("COMMA")){let Z=[R];while(this.match("COMMA"))Z.push(this.expect("NAME").value);B=Z}else B=R;let O=this.expect("NAME");if(O.value!=="in")throw this.error(`Expected 'in' in for loop, got '${O.value}'`);let K=this.parseExpression(),Q=this.check("NAME")&&this.peek().value==="recursive";if(Q)this.advance();this.expect("BLOCK_END");let M=[],$=[];while(!this.isAtEnd()){if(this.checkBlockTag("empty")||this.checkBlockTag("else")||this.checkBlockTag("endfor"))break;let Z=this.parseStatement();if(Z)M.push(Z)}if(this.checkBlockTag("empty")||this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endfor"))break;let Z=this.parseStatement();if(Z)$.push(Z)}}return this.expectBlockTag("endfor"),{type:"For",target:B,iter:K,body:M,else_:$,recursive:Q,line:A.line,column:A.column}}parseBlockTag(A){let B=this.expect("NAME").value,R=this.check("NAME")&&this.peek().value==="scoped";if(R)this.advance();this.expect("BLOCK_END");let O=[];while(!this.isAtEnd()){if(this.checkBlockTag("endblock"))break;let K=this.parseStatement();if(K)O.push(K)}if(this.advance(),this.advance(),this.check("NAME"))this.advance();return this.expect("BLOCK_END"),{type:"Block",name:B,body:O,scoped:R,line:A.line,column:A.column}}parseExtends(A){let B=this.parseExpression();return this.expect("BLOCK_END"),{type:"Extends",template:B,line:A.line,column:A.column}}parseInclude(A){let B=this.parseExpression(),R=null,O=!1,K=!1;while(this.check("NAME")){let Q=this.peek().value;if(Q==="ignore"&&this.peekNext()?.value==="missing")this.advance(),this.advance(),K=!0;else if(Q==="with")this.advance(),R=this.parseKeywordArgs();else if(Q==="only")this.advance(),O=!0;else if(Q==="without"){if(this.advance(),this.check("NAME")&&this.peek().value==="context")this.advance(),O=!0}else break}return this.expect("BLOCK_END"),{type:"Include",template:B,context:R,only:O,ignoreMissing:K,line:A.line,column:A.column}}parseSet(A){let B=this.expect("NAME").value;this.expect("ASSIGN");let R=this.parseExpression();return this.expect("BLOCK_END"),{type:"Set",target:B,value:R,line:A.line,column:A.column}}parseWith(A){let B=[];do{let O=this.expect("NAME").value;this.expect("ASSIGN");let K=this.parseExpression();B.push({target:O,value:K})}while(this.match("COMMA")||this.check("NAME")&&this.peekNext()?.type==="ASSIGN");this.expect("BLOCK_END");let R=[];while(!this.isAtEnd()){if(this.checkBlockTag("endwith"))break;let O=this.parseStatement();if(O)R.push(O)}return this.expectBlockTag("endwith"),{type:"With",assignments:B,body:R,line:A.line,column:A.column}}parseLoad(A){let B=[];while(this.check("NAME"))B.push(this.advance().value);return this.expect("BLOCK_END"),{type:"Load",names:B,line:A.line,column:A.column}}parseUrl(A){let B=this.parseExpression(),R=[],O={},K=null;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){this.advance(),K=this.expect("NAME").value;break}if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let Q=this.advance().value;this.advance(),O[Q]=this.parseExpression()}else R.push(this.parseExpression())}return this.expect("BLOCK_END"),{type:"Url",name:B,args:R,kwargs:O,asVar:K,line:A.line,column:A.column}}parseStatic(A){let B=this.parseExpression(),R=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),R=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Static",path:B,asVar:R,line:A.line,column:A.column}}parseNow(A){let B=this.parseExpression(),R=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),R=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Now",format:B,asVar:R,line:A.line,column:A.column}}parseComment(A){this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endcomment"))break;this.advance()}return this.expectBlockTag("endcomment"),null}parseSimpleBlock(A,B){this.skipToBlockEnd();let R=`end${B}`;while(!this.isAtEnd()){if(this.checkBlockTag(R))break;this.advance()}if(this.checkBlockTag(R))this.advance(),this.advance(),this.expect("BLOCK_END");return null}parseCycle(A){let B=[],R=null,O=!1;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){if(this.advance(),R=this.expect("NAME").value,this.check("NAME")&&this.peek().value==="silent")this.advance(),O=!0;break}B.push(this.parseExpression())}return this.expect("BLOCK_END"),{type:"Cycle",values:B,asVar:R,silent:O,line:A.line,column:A.column}}parseFirstof(A){let B=[],R=null,O=null;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){this.advance(),O=this.expect("NAME").value;break}B.push(this.parseExpression())}if(B.length>0){let K=B[B.length-1];if(K.type==="Literal"&&typeof K.value==="string")R=B.pop()}return this.expect("BLOCK_END"),{type:"Firstof",values:B,fallback:R,asVar:O,line:A.line,column:A.column}}parseIfchanged(A){let B=[];while(!this.check("BLOCK_END"))B.push(this.parseExpression());this.expect("BLOCK_END");let R=[],O=[];while(!this.isAtEnd()){if(this.checkBlockTag("else")||this.checkBlockTag("endifchanged"))break;let K=this.parseStatement();if(K)R.push(K)}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endifchanged"))break;let K=this.parseStatement();if(K)O.push(K)}}return this.expectBlockTag("endifchanged"),{type:"Ifchanged",values:B,body:R,else_:O,line:A.line,column:A.column}}parseRegroup(A){let B=this.parseExpression();this.expectName("by");let R=this.expect("NAME").value;this.expectName("as");let O=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Regroup",target:B,key:R,asVar:O,line:A.line,column:A.column}}parseWidthratio(A){let B=this.parseExpression(),R=this.parseExpression(),O=this.parseExpression(),K=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),K=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Widthratio",value:B,maxValue:R,maxWidth:O,asVar:K,line:A.line,column:A.column}}parseLorem(A){let B=null,R="p",O=!1;if(this.check("NUMBER"))B={type:"Literal",value:parseInt(this.advance().value,10),line:A.line,column:A.column};if(this.check("NAME")){let K=this.peek().value.toLowerCase();if(K==="w"||K==="p"||K==="b")R=K,this.advance()}if(this.check("NAME")&&this.peek().value==="random")O=!0,this.advance();return this.expect("BLOCK_END"),{type:"Lorem",count:B,method:R,random:O,line:A.line,column:A.column}}parseCsrfToken(A){return this.expect("BLOCK_END"),{type:"CsrfToken",line:A.line,column:A.column}}parseDebug(A){return this.expect("BLOCK_END"),{type:"Debug",line:A.line,column:A.column}}parseTemplatetag(A){let B=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Templatetag",tagType:B,line:A.line,column:A.column}}parseIfequal(A,B){let R=this.parseExpression(),O=this.parseExpression();this.expect("BLOCK_END");let K={type:"Compare",left:R,ops:[{operator:B?"!=":"==",right:O}],line:A.line,column:A.column},Q=[],M=[],$=B?"endifnotequal":"endifequal";while(!this.isAtEnd()){if(this.checkBlockTag("else")||this.checkBlockTag($))break;let Z=this.parseStatement();if(Z)Q.push(Z)}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag($))break;let Z=this.parseStatement();if(Z)M.push(Z)}}return this.expectBlockTag($),{type:"If",test:K,body:Q,elifs:[],else_:M,line:A.line,column:A.column}}parseExpression(){return this.parseConditional()}parseConditional(){let A=this.parseOr();if(this.check("NAME")&&this.peek().value==="if"){this.advance();let B=this.parseOr();this.expectName("else");let R=this.parseConditional();A={type:"Conditional",test:B,trueExpr:A,falseExpr:R,line:A.line,column:A.column}}return A}parseOr(){let A=this.parseAnd();while(this.check("OR")||this.check("NAME")&&this.peek().value==="or"){this.advance();let B=this.parseAnd();A={type:"BinaryOp",operator:"or",left:A,right:B,line:A.line,column:A.column}}return A}parseAnd(){let A=this.parseNot();while(this.check("AND")||this.check("NAME")&&this.peek().value==="and"){this.advance();let B=this.parseNot();A={type:"BinaryOp",operator:"and",left:A,right:B,line:A.line,column:A.column}}return A}parseNot(){if(this.check("NOT")||this.check("NAME")&&this.peek().value==="not"){let A=this.advance();return{type:"UnaryOp",operator:"not",operand:this.parseNot(),line:A.line,column:A.column}}return this.parseCompare()}parseCompare(){let A=this.parseAddSub(),B=[];while(!0){let R=null;if(this.match("EQ"))R="==";else if(this.match("NE"))R="!=";else if(this.match("LT"))R="<";else if(this.match("GT"))R=">";else if(this.match("LE"))R="<=";else if(this.match("GE"))R=">=";else if(this.check("NAME")){let K=this.peek().value;if(K==="in")this.advance(),R="in";else if(K==="not"&&this.peekNext()?.value==="in")this.advance(),this.advance(),R="not in";else if(K==="is"){this.advance();let Q=this.check("NOT");if(Q)this.advance();let $=this.expect("NAME").value,Z=[];if(this.match("LPAREN")){while(!this.check("RPAREN"))if(Z.push(this.parseExpression()),!this.check("RPAREN"))this.expect("COMMA");this.expect("RPAREN")}A={type:"TestExpr",node:A,test:$,args:Z,negated:Q,line:A.line,column:A.column};continue}}if(!R)break;let O=this.parseAddSub();B.push({operator:R,right:O})}if(B.length===0)return A;return{type:"Compare",left:A,ops:B,line:A.line,column:A.column}}parseAddSub(){let A=this.parseMulDiv();while(this.check("ADD")||this.check("SUB")||this.check("TILDE")){let B=this.advance(),R=this.parseMulDiv();A={type:"BinaryOp",operator:B.value,left:A,right:R,line:A.line,column:A.column}}return A}parseMulDiv(){let A=this.parseUnary();while(this.check("MUL")||this.check("DIV")||this.check("MOD")){let B=this.advance(),R=this.parseUnary();A={type:"BinaryOp",operator:B.value,left:A,right:R,line:A.line,column:A.column}}return A}parseUnary(){if(this.check("SUB")||this.check("ADD")){let A=this.advance(),B=this.parseUnary();return{type:"UnaryOp",operator:A.value,operand:B,line:A.line,column:A.column}}return this.parseFilter()}parseFilter(){let A=this.parsePostfix();while(this.match("PIPE")){let B=this.expect("NAME").value,R=[],O={};if(this.match("COLON"))if(this.check("SUB")||this.check("ADD")){let K=this.advance(),Q=this.parsePostfix();R.push({type:"UnaryOp",operator:K.value,operand:Q,line:K.line,column:K.column})}else R.push(this.parsePostfix());else if(this.match("LPAREN")){while(!this.check("RPAREN")){if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let K=this.advance().value;this.advance(),O[K]=this.parseExpression()}else R.push(this.parseExpression());if(!this.check("RPAREN"))this.expect("COMMA")}this.expect("RPAREN")}A={type:"FilterExpr",node:A,filter:B,args:R,kwargs:O,line:A.line,column:A.column}}return A}parsePostfix(){let A=this.parsePrimary();while(!0)if(this.match("DOT")){let B;if(this.check("NUMBER"))B=this.advance().value;else B=this.expect("NAME").value;A={type:"GetAttr",object:A,attribute:B,line:A.line,column:A.column}}else if(this.match("LBRACKET")){let B=this.parseExpression();this.expect("RBRACKET"),A={type:"GetItem",object:A,index:B,line:A.line,column:A.column}}else if(this.match("LPAREN")){let B=[],R={};while(!this.check("RPAREN")){if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let O=this.advance().value;this.advance(),R[O]=this.parseExpression()}else B.push(this.parseExpression());if(!this.check("RPAREN"))this.expect("COMMA")}this.expect("RPAREN"),A={type:"FunctionCall",callee:A,args:B,kwargs:R,line:A.line,column:A.column}}else break;return A}parsePrimary(){let A=this.peek();if(this.match("STRING"))return{type:"Literal",value:A.value,line:A.line,column:A.column};if(this.match("NUMBER"))return{type:"Literal",value:A.value.includes(".")?parseFloat(A.value):parseInt(A.value,10),line:A.line,column:A.column};if(this.check("NAME")){let B=this.advance().value;if(B==="true"||B==="True")return{type:"Literal",value:!0,line:A.line,column:A.column};if(B==="false"||B==="False")return{type:"Literal",value:!1,line:A.line,column:A.column};if(B==="none"||B==="None"||B==="null")return{type:"Literal",value:null,line:A.line,column:A.column};return{type:"Name",name:B,line:A.line,column:A.column}}if(this.match("LPAREN")){let B=this.parseExpression();return this.expect("RPAREN"),B}if(this.match("LBRACKET")){let B=[];while(!this.check("RBRACKET"))if(B.push(this.parseExpression()),!this.check("RBRACKET"))this.expect("COMMA");return this.expect("RBRACKET"),{type:"Array",elements:B,line:A.line,column:A.column}}if(this.match("LBRACE")){let B=[];while(!this.check("RBRACE")){let R=this.parseExpression();this.expect("COLON");let O=this.parseExpression();if(B.push({key:R,value:O}),!this.check("RBRACE"))this.expect("COMMA")}return this.expect("RBRACE"),{type:"Object",pairs:B,line:A.line,column:A.column}}throw this.error(`Unexpected token: ${A.type} (${A.value})`)}parseKeywordArgs(){let A={};while(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let B=this.advance().value;this.advance(),A[B]=this.parseExpression()}return A}checkBlockTag(A){if(this.peek().type!=="BLOCK_START")return!1;let B=this.current+1;if(B>=this.tokens.length)return!1;let R=this.tokens[B];return R.type==="NAME"&&R.value===A}expectBlockTag(A){this.advance();let B=this.expect("NAME");if(B.value!==A)throw this.error(`Expected '${A}', got '${B.value}'`);this.expect("BLOCK_END")}expectName(A){let B=this.expect("NAME");if(B.value!==A)throw this.error(`Expected '${A}', got '${B.value}'`)}skipToBlockEnd(){while(!this.isAtEnd()&&!this.check("BLOCK_END"))this.advance();if(this.check("BLOCK_END"))this.advance()}isAtEnd(){return this.peek().type==="EOF"}peek(){return this.tokens[this.current]}peekNext(){if(this.current+1>=this.tokens.length)return null;return this.tokens[this.current+1]}advance(){if(!this.isAtEnd())this.current++;return this.tokens[this.current-1]}check(A){if(this.isAtEnd())return!1;return this.peek().type===A}match(A){if(this.check(A))return this.advance(),!0;return!1}expect(A){if(this.check(A))return this.advance();let B=this.peek();throw this.error(`Expected ${A}, got ${B.type} (${B.value})`)}error(A){let B=this.peek();return new I(A,{line:B.line,column:B.column,source:this.source})}}function f(A,B={}){return new y(B).compile(A)}class y{options;indent=0;varCounter=0;loopStack=[];localVars=[];constructor(A={}){this.options={functionName:A.functionName??"render",inlineHelpers:A.inlineHelpers??!0,minify:A.minify??!1,autoescape:A.autoescape??!0}}pushScope(){this.localVars.push(new Set)}popScope(){this.localVars.pop()}addLocalVar(A){if(this.localVars.length>0)this.localVars[this.localVars.length-1].add(A)}isLocalVar(A){for(let B=this.localVars.length-1;B>=0;B--)if(this.localVars[B].has(A))return!0;return!1}compile(A){let B=this.compileNodes(A.body),R=this.options.minify?"":`
|
|
13
|
+
`;return`function ${this.options.functionName}(__ctx) {${R} let __out = '';${R}`+B+` return __out;${R}}`}compileNodes(A){return A.map((B)=>this.compileNode(B)).join("")}compileNode(A){switch(A.type){case"Text":return this.compileText(A);case"Output":return this.compileOutput(A);case"If":return this.compileIf(A);case"For":return this.compileFor(A);case"Set":return this.compileSet(A);case"With":return this.compileWith(A);case"Comment":return"";case"Extends":case"Block":case"Include":throw Error(`AOT compilation does not support '${A.type}' - use Environment.render() for templates with inheritance`);case"Url":case"Static":throw Error(`AOT compilation does not support '${A.type}' tag - use Environment.render() with urlResolver/staticResolver`);default:throw Error(`Unknown node type in AOT compiler: ${A.type}`)}}compileText(A){return` __out += ${JSON.stringify(A.value)};${this.nl()}`}compileOutput(A){let B=this.compileExpr(A.expression);if(this.options.autoescape&&!this.isMarkedSafe(A.expression))return` __out += escape(${B});${this.nl()}`;return` __out += (${B}) ?? '';${this.nl()}`}compileIf(A){let B="",R=this.compileExpr(A.test);B+=` if (isTruthy(${R})) {${this.nl()}`,B+=this.compileNodes(A.body),B+=" }";for(let O of A.elifs){let K=this.compileExpr(O.test);B+=` else if (isTruthy(${K})) {${this.nl()}`,B+=this.compileNodes(O.body),B+=" }"}if(A.else_.length>0)B+=` else {${this.nl()}`,B+=this.compileNodes(A.else_),B+=" }";return B+=this.nl(),B}compileFor(A){let B=this.genVar("iter"),R=this.genVar("i"),O=this.genVar("len"),K=this.genVar("loop"),Q=Array.isArray(A.target)?A.target[0]:A.target,M=Array.isArray(A.target)&&A.target[1]?A.target[1]:null,$=this.loopStack.length>0?this.loopStack[this.loopStack.length-1]:null,Z=this.compileExpr(A.iter),U="";if(U+=` const ${B} = toArray(${Z});${this.nl()}`,U+=` const ${O} = ${B}.length;${this.nl()}`,A.else_.length>0)U+=` if (${O} === 0) {${this.nl()}`,U+=this.compileNodes(A.else_),U+=` } else {${this.nl()}`;if(U+=` for (let ${R} = 0; ${R} < ${O}; ${R}++) {${this.nl()}`,M)U+=` const ${Q} = ${B}[${R}][0];${this.nl()}`,U+=` const ${M} = ${B}[${R}][1];${this.nl()}`;else U+=` const ${Q} = ${B}[${R}];${this.nl()}`;if(U+=` const ${K} = {${this.nl()}`,U+=` counter: ${R} + 1,${this.nl()}`,U+=` counter0: ${R},${this.nl()}`,U+=` revcounter: ${O} - ${R},${this.nl()}`,U+=` revcounter0: ${O} - ${R} - 1,${this.nl()}`,U+=` first: ${R} === 0,${this.nl()}`,U+=` last: ${R} === ${O} - 1,${this.nl()}`,U+=` length: ${O},${this.nl()}`,U+=` index: ${R} + 1,${this.nl()}`,U+=` index0: ${R},${this.nl()}`,$)U+=` parentloop: ${$},${this.nl()}`,U+=` parent: ${$}${this.nl()}`;else U+=` parentloop: null,${this.nl()}`,U+=` parent: null${this.nl()}`;U+=` };${this.nl()}`,U+=` const forloop = ${K};${this.nl()}`,U+=` const loop = ${K};${this.nl()}`,this.loopStack.push(K);let G=this.compileNodes(A.body);if(U+=G.replace(new RegExp(`__ctx\\.${Q}`,"g"),Q),this.loopStack.pop(),U+=` }${this.nl()}`,A.else_.length>0)U+=` }${this.nl()}`;return U}compileSet(A){let B=this.compileExpr(A.value);return` const ${A.target} = ${B};${this.nl()}`}compileWith(A){let B=` {${this.nl()}`;this.pushScope();for(let{target:R,value:O}of A.assignments){let K=this.compileExpr(O);B+=` const ${R} = ${K};${this.nl()}`,this.addLocalVar(R)}return B+=this.compileNodes(A.body),B+=` }${this.nl()}`,this.popScope(),B}compileExpr(A){switch(A.type){case"Name":return this.compileName(A);case"Literal":return this.compileLiteral(A);case"Array":return this.compileArray(A);case"Object":return this.compileObject(A);case"BinaryOp":return this.compileBinaryOp(A);case"UnaryOp":return this.compileUnaryOp(A);case"Compare":return this.compileCompare(A);case"GetAttr":return this.compileGetAttr(A);case"GetItem":return this.compileGetItem(A);case"FilterExpr":return this.compileFilter(A);case"TestExpr":return this.compileTest(A);case"Conditional":return this.compileConditional(A);default:return"undefined"}}compileName(A){if(A.name==="true"||A.name==="True")return"true";if(A.name==="false"||A.name==="False")return"false";if(A.name==="none"||A.name==="None"||A.name==="null")return"null";if(A.name==="forloop"||A.name==="loop")return A.name;if(this.isLocalVar(A.name))return A.name;return`__ctx.${A.name}`}compileLiteral(A){if(typeof A.value==="string")return JSON.stringify(A.value);return String(A.value)}compileArray(A){return`[${A.elements.map((R)=>this.compileExpr(R)).join(", ")}]`}compileObject(A){return`{${A.pairs.map(({key:R,value:O})=>{let K=this.compileExpr(R),Q=this.compileExpr(O);return`[${K}]: ${Q}`}).join(", ")}}`}compileBinaryOp(A){let B=this.compileExpr(A.left),R=this.compileExpr(A.right);switch(A.operator){case"and":return`(${B} && ${R})`;case"or":return`(${B} || ${R})`;case"~":return`(String(${B}) + String(${R}))`;case"in":return`(Array.isArray(${R}) ? ${R}.includes(${B}) : String(${R}).includes(String(${B})))`;case"not in":return`!(Array.isArray(${R}) ? ${R}.includes(${B}) : String(${R}).includes(String(${B})))`;default:return`(${B} ${A.operator} ${R})`}}compileUnaryOp(A){let B=this.compileExpr(A.operand);switch(A.operator){case"not":return`!isTruthy(${B})`;case"-":return`-(${B})`;case"+":return`+(${B})`;default:return B}}compileCompare(A){let B=this.compileExpr(A.left);for(let{operator:R,right:O}of A.ops){let K=this.compileExpr(O);switch(R){case"==":case"===":B=`(${B} === ${K})`;break;case"!=":case"!==":B=`(${B} !== ${K})`;break;default:B=`(${B} ${R} ${K})`}}return B}compileGetAttr(A){return`${this.compileExpr(A.object)}?.${A.attribute}`}compileGetItem(A){let B=this.compileExpr(A.object),R=this.compileExpr(A.index);return`${B}?.[${R}]`}compileFilter(A){let B=this.compileExpr(A.node),R=A.args.map((O)=>this.compileExpr(O));switch(A.filter){case"upper":return`String(${B}).toUpperCase()`;case"lower":return`String(${B}).toLowerCase()`;case"title":return`String(${B}).replace(/\\b\\w/g, c => c.toUpperCase())`;case"trim":return`String(${B}).trim()`;case"length":return`(${B}?.length ?? Object.keys(${B} ?? {}).length)`;case"first":return`(${B})?.[0]`;case"last":return`(${B})?.[(${B})?.length - 1]`;case"default":return`((${B}) ?? ${R[0]??'""'})`;case"safe":return`{ __safe: true, value: String(${B}) }`;case"escape":case"e":return`escape(${B})`;case"join":return`(${B} ?? []).join(${R[0]??'""'})`;case"abs":return`Math.abs(${B})`;case"round":return R.length?`Number(${B}).toFixed(${R[0]})`:`Math.round(${B})`;case"int":return`parseInt(${B}, 10)`;case"float":return`parseFloat(${B})`;case"floatformat":return`Number(${B}).toFixed(${R[0]??1})`;case"filesizeformat":return`applyFilter('filesizeformat', ${B})`;default:let O=R.length?", "+R.join(", "):"";return`applyFilter('${A.filter}', ${B}${O})`}}compileTest(A){let B=this.compileExpr(A.node),R=A.args.map((K)=>this.compileExpr(K)),O=A.negated?"!":"";switch(A.test){case"defined":return`${O}(${B} !== undefined)`;case"undefined":return`${O}(${B} === undefined)`;case"none":return`${O}(${B} === null)`;case"even":return`${O}(${B} % 2 === 0)`;case"odd":return`${O}(${B} % 2 !== 0)`;case"divisibleby":return`${O}(${B} % ${R[0]} === 0)`;case"empty":return`${O}((${B} == null) || (${B}.length === 0) || (Object.keys(${B}).length === 0))`;case"iterable":return`${O}(Array.isArray(${B}) || typeof ${B} === 'string')`;case"number":return`${O}(typeof ${B} === 'number' && !isNaN(${B}))`;case"string":return`${O}(typeof ${B} === 'string')`;default:let K=R.length?", "+R.join(", "):"";return`${O}applyTest('${A.test}', ${B}${K})`}}compileConditional(A){let B=this.compileExpr(A.test),R=this.compileExpr(A.trueExpr),O=this.compileExpr(A.falseExpr);return`(isTruthy(${B}) ? ${R} : ${O})`}isMarkedSafe(A){if(A.type==="FilterExpr")return A.filter==="safe";return!1}genVar(A){return`__${A}${this.varCounter++}`}nl(){return this.options.minify?"":`
|
|
14
|
+
`}}function k(A,B){return new g(B).flatten(A)}function N(A){return new m().check(A)}class g{loader;maxDepth;blocks=new Map;depth=0;constructor(A){this.loader=A.loader,this.maxDepth=A.maxDepth??10}flatten(A){return this.blocks.clear(),this.depth=0,this.processTemplate(A)}processTemplate(A,B=!0){if(this.depth>this.maxDepth)throw Error(`Maximum template inheritance depth (${this.maxDepth}) exceeded`);this.collectBlocks(A.body,B);let R=this.findExtends(A.body);if(R){let O=this.getStaticTemplateName(R.template),K=this.loader.load(O),Q=this.loader.parse(K);this.depth++;let M=this.processTemplate(Q,!1);return this.depth--,{type:"Template",body:this.replaceBlocks(M.body),line:A.line,column:A.column}}return{type:"Template",body:this.processNodes(A.body),line:A.line,column:A.column}}collectBlocks(A,B=!0){for(let R of A){if(R.type==="Block"){let O=R;if(B||!this.blocks.has(O.name))this.blocks.set(O.name,O)}this.collectBlocksFromNode(R,B)}}collectBlocksFromNode(A,B=!0){switch(A.type){case"If":{let R=A;this.collectBlocks(R.body,B);for(let O of R.elifs)this.collectBlocks(O.body,B);this.collectBlocks(R.else_,B);break}case"For":{let R=A;this.collectBlocks(R.body,B),this.collectBlocks(R.else_,B);break}case"With":{let R=A;this.collectBlocks(R.body,B);break}case"Block":{let R=A;this.collectBlocks(R.body,B);break}}}findExtends(A){for(let B of A)if(B.type==="Extends")return B;return null}processNodes(A){let B=[];for(let R of A){if(R.type==="Extends")continue;if(R.type==="Include"){let K=R,Q=this.inlineInclude(K);B.push(...Q);continue}if(R.type==="Block"){let K=R,Q=this.blocks.get(K.name);if(Q&&Q!==K)B.push(...this.processNodes(Q.body));else B.push(...this.processNodes(K.body));continue}let O=this.processNode(R);if(O)B.push(O)}return B}processNode(A){switch(A.type){case"If":{let B=A;return{...B,body:this.processNodes(B.body),elifs:B.elifs.map((R)=>({test:R.test,body:this.processNodes(R.body)})),else_:this.processNodes(B.else_)}}case"For":{let B=A;return{...B,body:this.processNodes(B.body),else_:this.processNodes(B.else_)}}case"With":{let B=A;return{...B,body:this.processNodes(B.body)}}default:return A}}replaceBlocks(A){return this.processNodes(A)}inlineInclude(A){let B=this.getStaticTemplateName(A.template);try{let R=this.loader.load(B),O=this.loader.parse(R);this.depth++;let K=this.processTemplate(O);if(this.depth--,A.context&&Object.keys(A.context).length>0)return[{type:"With",assignments:Object.entries(A.context).map(([M,$])=>({target:M,value:$})),body:K.body,line:A.line,column:A.column}];return K.body}catch(R){if(A.ignoreMissing)return[];throw R}}getStaticTemplateName(A){if(A.type==="Literal"){let B=A;if(typeof B.value==="string")return B.value}throw Error(`AOT compilation requires static template names. Found dynamic expression at line ${A.line}. Use Environment.render() for dynamic template names.`)}}class m{check(A){return this.checkNodes(A.body)}checkNodes(A){for(let B of A){let R=this.checkNode(B);if(!R.canFlatten)return R}return{canFlatten:!0}}checkNode(A){switch(A.type){case"Extends":{let B=A;if(!this.isStaticName(B.template))return{canFlatten:!1,reason:`Dynamic extends at line ${A.line} - use static string literal`};break}case"Include":{let B=A;if(!this.isStaticName(B.template))return{canFlatten:!1,reason:`Dynamic include at line ${A.line} - use static string literal`};break}case"If":{let B=A,R=this.checkNodes(B.body);if(!R.canFlatten)return R;for(let O of B.elifs)if(R=this.checkNodes(O.body),!R.canFlatten)return R;if(R=this.checkNodes(B.else_),!R.canFlatten)return R;break}case"For":{let B=A,R=this.checkNodes(B.body);if(!R.canFlatten)return R;if(R=this.checkNodes(B.else_),!R.canFlatten)return R;break}case"With":{let B=A,R=this.checkNodes(B.body);if(!R.canFlatten)return R;break}case"Block":{let B=A,R=this.checkNodes(B.body);if(!R.canFlatten)return R;break}}return{canFlatten:!0}}isStaticName(A){return A.type==="Literal"&&typeof A.value==="string"}}var i="0.1.1",J={reset:"\x1B[0m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",cyan:"\x1B[36m",dim:"\x1B[2m"};function E(A){console.log(A)}function L(A){console.log(`${J.green}\u2713${J.reset} ${A}`)}function _(A){console.log(`${J.yellow}\u26A0${J.reset} ${A}`)}function F(A){console.error(`${J.red}\u2717${J.reset} ${A}`)}function w(){console.log(`
|
|
15
15
|
${J.cyan}binja${J.reset} - High-performance template compiler
|
|
16
16
|
|
|
17
17
|
${J.yellow}Usage:${J.reset}
|
|
@@ -50,7 +50,7 @@ ${J.yellow}Output:${J.reset}
|
|
|
50
50
|
${J.dim}// Usage${J.reset}
|
|
51
51
|
import { render } from './dist/templates/home.js'
|
|
52
52
|
const html = render({ title: 'Hello' })
|
|
53
|
-
`)}function u(A){let B={output:"",minify:!1,watch:!1,extensions:[".html",".jinja",".jinja2"],verbose:!1},R="",
|
|
53
|
+
`)}function u(A){let B={output:"",minify:!1,watch:!1,extensions:[".html",".jinja",".jinja2"],verbose:!1},R="",O="";for(let K=0;K<A.length;K++){let Q=A[K];if(Q==="compile"||Q==="check"||Q==="watch"){if(R=Q,Q==="watch")B.watch=!0,R="compile"}else if(Q==="-o"||Q==="--output")B.output=A[++K];else if(Q==="-n"||Q==="--name")B.name=A[++K];else if(Q==="-m"||Q==="--minify")B.minify=!0;else if(Q==="-w"||Q==="--watch")B.watch=!0;else if(Q==="-v"||Q==="--verbose")B.verbose=!0;else if(Q==="-e"||Q==="--ext")B.extensions=A[++K].split(",").map((M)=>M.startsWith(".")?M:`.${M}`);else if(Q==="--help"||Q==="-h")w(),process.exit(0);else if(Q==="--version"||Q==="-V")console.log(`binja v${i}`),process.exit(0);else if(!Q.startsWith("-")&&!O)O=Q}return{command:R,source:O,options:B}}function V(A,B){return{load(R){let O=Y.resolve(A,R);for(let K of[...B,""]){let Q=O+K;if(X.existsSync(Q))return X.readFileSync(Q,"utf-8")}throw Error(`Template not found: ${R}`)},parse(R){let K=new D(R).tokenize();return new S(K).parse()}}}function s(A,B){return`// Generated by binja - DO NOT EDIT
|
|
54
54
|
// Source: binja compile
|
|
55
55
|
|
|
56
56
|
const escape = (v) => {
|
|
@@ -94,5 +94,5 @@ ${A}
|
|
|
94
94
|
|
|
95
95
|
export { ${B} as render };
|
|
96
96
|
export default ${B};
|
|
97
|
-
`}async function b(A,B,R,
|
|
98
|
-
Stopped watching.`),process.exit(0)})}async function n(){let A=process.argv.slice(2);if(A.length===0)w(),process.exit(0);let{command:B,source:R,options:
|
|
97
|
+
`}async function b(A,B,R,O){try{let K=X.readFileSync(A,"utf-8"),Q=V(R,O.extensions),M=Q.parse(K),$=N(M),Z=M;if(!$.canFlatten){if(O.verbose)_(`${Y.basename(A)}: ${$.reason} - compiling without inheritance resolution`)}else Z=k(M,{loader:Q});let U=Y.relative(R,A),G=O.name||"render"+U.replace(/\.[^.]+$/,"").replace(/[^a-zA-Z0-9]/g,"_").replace(/^_+|_+$/g,"").replace(/_([a-z])/g,(o,c)=>c.toUpperCase()),W=f(Z,{functionName:G,minify:O.minify}),H=U.replace(/\.[^.]+$/,".js"),z=Y.join(B,H),C=Y.dirname(z);if(!X.existsSync(C))X.mkdirSync(C,{recursive:!0});let j=s(W,G);return X.writeFileSync(z,j),{success:!0,outputPath:z}}catch(K){return{success:!1,error:K.message}}}async function T(A,B,R){let O=0,K=0,Q=[];function M($){let Z=X.readdirSync($,{withFileTypes:!0});for(let U of Z){let G=Y.join($,U.name);if(U.isDirectory())M(G);else if(U.isFile()){let W=Y.extname(U.name);if(R.extensions.includes(W))Q.push(G)}}}M(A);for(let $ of Q){let Z=await b($,B,A,R);if(Z.success){if(O++,R.verbose)L(`${Y.relative(A,$)} \u2192 ${Y.relative(process.cwd(),Z.outputPath)}`)}else K++,F(`${Y.relative(A,$)}: ${Z.error}`)}return{compiled:O,failed:K}}async function r(A,B){let R=V(A,B.extensions),O=0,K=0,Q=0;function M($){let Z=X.readdirSync($,{withFileTypes:!0});for(let U of Z){let G=Y.join($,U.name);if(U.isDirectory())M(G);else if(U.isFile()){let W=Y.extname(U.name);if(B.extensions.includes(W)){O++;try{let H=X.readFileSync(G,"utf-8"),z=R.parse(H),C=N(z),j=Y.relative(A,G);if(C.canFlatten)K++,L(`${j}`);else Q++,_(`${j}: ${C.reason}`)}catch(H){Q++,F(`${Y.relative(A,G)}: ${H.message}`)}}}}}if(M(A),E(""),E(`Total: ${O} templates`),E(`${J.green}AOT compatible: ${K}${J.reset}`),Q>0)E(`${J.yellow}Require runtime: ${Q}${J.reset}`)}async function l(A,B,R){E(`${J.cyan}Watching${J.reset} ${A} for changes...`),E(`${J.dim}Press Ctrl+C to stop${J.reset}`),E("");let{compiled:O,failed:K}=await T(A,B,{...R,verbose:!0});E(""),E(`Compiled ${O} templates${K>0?`, ${K} failed`:""}`),E("");let Q=X.watch(A,{recursive:!0},async(M,$)=>{if(!$)return;let Z=Y.extname($);if(!R.extensions.includes(Z))return;let U=Y.join(A,$);if(!X.existsSync(U))return;E(`${J.dim}[${new Date().toLocaleTimeString()}]${J.reset} ${$} changed`);let G=await b(U,B,A,R);if(G.success)L(`Compiled ${$}`);else F(`${$}: ${G.error}`)});process.on("SIGINT",()=>{Q.close(),E(`
|
|
98
|
+
Stopped watching.`),process.exit(0)})}async function n(){let A=process.argv.slice(2);if(A.length===0)w(),process.exit(0);let{command:B,source:R,options:O}=u(A);if(!B)F('No command specified. Use "compile" or "check".'),w(),process.exit(1);if(!R)F("No source path specified."),process.exit(1);let K=Y.resolve(R);if(!X.existsSync(K))F(`Source not found: ${R}`),process.exit(1);let Q=X.statSync(K).isDirectory();if(B==="check")if(Q)await r(K,O);else{let M=V(Y.dirname(K),O.extensions),$=X.readFileSync(K,"utf-8"),Z=M.parse($),U=N(Z);if(U.canFlatten)L(`${R} can be AOT compiled`);else _(`${R}: ${U.reason}`)}else if(B==="compile"){if(!O.output)F("Output directory required. Use -o <dir>"),process.exit(1);let M=Y.resolve(O.output);if(O.watch){if(!Q)F("Watch mode requires a directory, not a single file."),process.exit(1);await l(K,M,O)}else if(Q){let $=Date.now(),{compiled:Z,failed:U}=await T(K,M,O),G=Date.now()-$;if(E(""),U===0)L(`Compiled ${Z} templates in ${G}ms`);else _(`Compiled ${Z} templates, ${U} failed (${G}ms)`)}else{let $=await b(K,M,Y.dirname(K),O);if($.success)L(`Compiled to ${$.outputPath}`);else F($.error),process.exit(1)}}}n().catch((A)=>{F(A.message),process.exit(1)});
|
package/dist/engines/index.d.ts
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Twig Engine
|
|
3
|
+
* PHP/Symfony compatible template engine
|
|
4
|
+
*
|
|
5
|
+
* Twig is nearly identical to Jinja2 with a few syntax differences:
|
|
6
|
+
* - Ternary: cond ? x : y (instead of x if cond else y)
|
|
7
|
+
* - Null coalesce: x ?? y
|
|
8
|
+
* - Some filter name differences (e -> escape, raw -> safe)
|
|
9
|
+
*/
|
|
10
|
+
import type { TemplateNode } from '../../parser/nodes';
|
|
11
|
+
export { TwigLexer } from './lexer';
|
|
12
|
+
export { TwigParser, TWIG_FILTER_MAP } from './parser';
|
|
13
|
+
/**
|
|
14
|
+
* Parse a Twig template string into an AST
|
|
15
|
+
*/
|
|
16
|
+
export declare function parse(source: string): TemplateNode;
|
|
17
|
+
/**
|
|
18
|
+
* Compile a Twig template to a render function
|
|
19
|
+
*/
|
|
20
|
+
export declare function compile(source: string): (context: Record<string, any>) => Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Render a Twig template with the given context
|
|
23
|
+
*/
|
|
24
|
+
export declare function render(source: string, context?: Record<string, any>): Promise<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Engine descriptor for MultiEngine
|
|
27
|
+
*/
|
|
28
|
+
export declare const engine: {
|
|
29
|
+
name: string;
|
|
30
|
+
extensions: string[];
|
|
31
|
+
parse: typeof parse;
|
|
32
|
+
compile: typeof compile;
|
|
33
|
+
render: typeof render;
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Twig Lexer
|
|
3
|
+
* Tokenizes Twig template syntax (very similar to Jinja2)
|
|
4
|
+
* PHP/Symfony compatible implementation
|
|
5
|
+
*
|
|
6
|
+
* Twig is nearly identical to Jinja2, so we extend the base lexer
|
|
7
|
+
* and add Twig-specific token handling.
|
|
8
|
+
*/
|
|
9
|
+
import { Lexer, TokenType } from '../../lexer';
|
|
10
|
+
import type { Token } from '../../lexer/tokens';
|
|
11
|
+
export { TokenType };
|
|
12
|
+
export type { Token };
|
|
13
|
+
export declare class TwigLexer extends Lexer {
|
|
14
|
+
constructor(source: string);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=lexer.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Twig Parser
|
|
3
|
+
* Extends the Jinja2 parser with Twig-specific syntax
|
|
4
|
+
*
|
|
5
|
+
* Key differences from Jinja2:
|
|
6
|
+
* - Ternary: cond ? x : y (instead of x if cond else y)
|
|
7
|
+
* - Null coalesce: x ?? y
|
|
8
|
+
* - Some filter name differences
|
|
9
|
+
*/
|
|
10
|
+
import { Parser } from '../../parser';
|
|
11
|
+
import { Token } from '../../lexer/tokens';
|
|
12
|
+
import type { TemplateNode } from '../../parser/nodes';
|
|
13
|
+
export declare const TWIG_FILTER_MAP: Record<string, string>;
|
|
14
|
+
export declare class TwigParser extends Parser {
|
|
15
|
+
constructor(tokens: Token[], source?: string);
|
|
16
|
+
parse(): TemplateNode;
|
|
17
|
+
private transformTwigAST;
|
|
18
|
+
private transformNode;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=parser.d.ts.map
|