1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| import { Environment } from 'nunjucks'; import stripIndent from 'strip-indent';
interface TagFunction { (args: any[], content: string): string; }
class NunjucksTag { public tags: string[]; public fn: TagFunction;
constructor(name: string, fn: TagFunction) { this.tags = [name]; this.fn = fn; }
parse(parser: any, nodes: any, lexer: any) { const node = this._parseArgs(parser, nodes, lexer); return new nodes.CallExtension(this, 'run', node, []); }
_parseArgs(parser: any, nodes: any, lexer: any) { const tag = parser.nextToken(); const node = new nodes.NodeList(tag.lineno, tag.colno); const argarray = new nodes.Array(tag.lineno, tag.colno);
let token; let argitem = '';
while ((token = parser.nextToken(true))) { if (token.type === lexer.TOKEN_WHITESPACE || token.type === lexer.TOKEN_BLOCK_END) { if (argitem !== '') { const argnode = new nodes.Literal(tag.lineno, tag.colno, argitem.trim()); argarray.addChild(argnode); argitem = ''; }
if (token.type === lexer.TOKEN_BLOCK_END) { break; } } else { argitem += token.value; } }
node.addChild(argarray); return node; }
run(context: any, args: any, body: any, callback: any) { return this._run(context, args, ''); }
_run(context: any, args: any, body: any): any { return Reflect.apply(this.fn, context.ctx, [args, body]); } }
const trimBody = (body: () => any) => { return stripIndent(body()).replace(/^\n?|\n?$/g, ''); };
class NunjucksBlock extends NunjucksTag { parse(parser: any, nodes: any, lexer: any) { const node = this._parseArgs(parser, nodes, lexer); const body = this._parseBody(parser, nodes, lexer);
return new nodes.CallExtension(this, 'run', node, [body]); }
_parseBody(parser: any, nodes: any, lexer: any) { const body = parser.parseUntilBlocks(`end${this.tags[0]}`);
parser.advanceAfterBlockEnd(); return body; }
run(context: any, args: any, body: any, callback: any) { return this._run(context, args, trimBody(body)); } }
class Tag { public env: Environment;
constructor() { this.env = new Environment(null, { autoescape: false }); }
register(name: string, fn: TagFunction, ends: boolean = false): void { if (!name) throw new TypeError('name is required'); if (typeof fn !== 'function') throw new TypeError('fn must be a function');
let tag: NunjucksTag;
if (ends) { tag = new NunjucksBlock(name, fn); } else { tag = new NunjucksTag(name, fn); }
this.env.addExtension(name, tag); }
unregister(name: string): void { if (!name) throw new TypeError('name is required');
const { env } = this;
if (env.hasExtension(name)) env.removeExtension(name); } }
export default Tag;
|