1'use strict';
2
3function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); }
4function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
5var parser = require('./parser');
6var transformer = require('./transformer');
7var nodes = require('./nodes');
8var _require = require('./lib'),
9  TemplateError = _require.TemplateError;
10var _require2 = require('./runtime'),
11  Frame = _require2.Frame;
12var _require3 = require('./object'),
13  Obj = _require3.Obj;
14
15// These are all the same for now, but shouldn't be passed straight
16// through
17var compareOps = {
18  '==': '==',
19  '===': '===',
20  '!=': '!=',
21  '!==': '!==',
22  '<': '<',
23  '>': '>',
24  '<=': '<=',
25  '>=': '>='
26};
27var Compiler = /*#__PURE__*/function (_Obj) {
28  _inheritsLoose(Compiler, _Obj);
29  function Compiler() {
30    return _Obj.apply(this, arguments) || this;
31  }
32  var _proto = Compiler.prototype;
33  _proto.init = function init(templateName, throwOnUndefined) {
34    this.templateName = templateName;
35    this.codebuf = [];
36    this.lastId = 0;
37    this.buffer = null;
38    this.bufferStack = [];
39    this._scopeClosers = '';
40    this.inBlock = false;
41    this.throwOnUndefined = throwOnUndefined;
42  };
43  _proto.fail = function fail(msg, lineno, colno) {
44    if (lineno !== undefined) {
45      lineno += 1;
46    }
47    if (colno !== undefined) {
48      colno += 1;
49    }
50    throw new TemplateError(msg, lineno, colno);
51  };
52  _proto._pushBuffer = function _pushBuffer() {
53    var id = this._tmpid();
54    this.bufferStack.push(this.buffer);
55    this.buffer = id;
56    this._emit("var " + this.buffer + " = \"\";");
57    return id;
58  };
59  _proto._popBuffer = function _popBuffer() {
60    this.buffer = this.bufferStack.pop();
61  };
62  _proto._emit = function _emit(code) {
63    this.codebuf.push(code);
64  };
65  _proto._emitLine = function _emitLine(code) {
66    this._emit(code + '\n');
67  };
68  _proto._emitLines = function _emitLines() {
69    var _this = this;
70    for (var _len = arguments.length, lines = new Array(_len), _key = 0; _key < _len; _key++) {
71      lines[_key] = arguments[_key];
72    }
73    lines.forEach(function (line) {
74      return _this._emitLine(line);
75    });
76  };
77  _proto._emitFuncBegin = function _emitFuncBegin(node, name) {
78    this.buffer = 'output';
79    this._scopeClosers = '';
80    this._emitLine("function " + name + "(env, context, frame, runtime, cb) {");
81    this._emitLine("var lineno = " + node.lineno + ";");
82    this._emitLine("var colno = " + node.colno + ";");
83    this._emitLine("var " + this.buffer + " = \"\";");
84    this._emitLine('try {');
85  };
86  _proto._emitFuncEnd = function _emitFuncEnd(noReturn) {
87    if (!noReturn) {
88      this._emitLine('cb(null, ' + this.buffer + ');');
89    }
90    this._closeScopeLevels();
91    this._emitLine('} catch (e) {');
92    this._emitLine('  cb(runtime.handleError(e, lineno, colno));');
93    this._emitLine('}');
94    this._emitLine('}');
95    this.buffer = null;
96  };
97  _proto._addScopeLevel = function _addScopeLevel() {
98    this._scopeClosers += '})';
99  };
100  _proto._closeScopeLevels = function _closeScopeLevels() {
101    this._emitLine(this._scopeClosers + ';');
102    this._scopeClosers = '';
103  };
104  _proto._withScopedSyntax = function _withScopedSyntax(func) {
105    var _scopeClosers = this._scopeClosers;
106    this._scopeClosers = '';
107    func.call(this);
108    this._closeScopeLevels();
109    this._scopeClosers = _scopeClosers;
110  };
111  _proto._makeCallback = function _makeCallback(res) {
112    var err = this._tmpid();
113    return 'function(' + err + (res ? ',' + res : '') + ') {\n' + 'if(' + err + ') { cb(' + err + '); return; }';
114  };
115  _proto._tmpid = function _tmpid() {
116    this.lastId++;
117    return 't_' + this.lastId;
118  };
119  _proto._templateName = function _templateName() {
120    return this.templateName == null ? 'undefined' : JSON.stringify(this.templateName);
121  };
122  _proto._compileChildren = function _compileChildren(node, frame) {
123    var _this2 = this;
124    node.children.forEach(function (child) {
125      _this2.compile(child, frame);
126    });
127  };
128  _proto._compileAggregate = function _compileAggregate(node, frame, startChar, endChar) {
129    var _this3 = this;
130    if (startChar) {
131      this._emit(startChar);
132    }
133    node.children.forEach(function (child, i) {
134      if (i > 0) {
135        _this3._emit(',');
136      }
137      _this3.compile(child, frame);
138    });
139    if (endChar) {
140      this._emit(endChar);
141    }
142  };
143  _proto._compileExpression = function _compileExpression(node, frame) {
144    // TODO: I'm not really sure if this type check is worth it or
145    // not.
146    this.assertType(node, nodes.Literal, nodes.Symbol, nodes.Group, nodes.Array, nodes.Dict, nodes.FunCall, nodes.Caller, nodes.Filter, nodes.LookupVal, nodes.Compare, nodes.InlineIf, nodes.In, nodes.Is, nodes.And, nodes.Or, nodes.Not, nodes.Add, nodes.Concat, nodes.Sub, nodes.Mul, nodes.Div, nodes.FloorDiv, nodes.Mod, nodes.Pow, nodes.Neg, nodes.Pos, nodes.Compare, nodes.NodeList);
147    this.compile(node, frame);
148  };
149  _proto.assertType = function assertType(node) {
150    for (var _len2 = arguments.length, types = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
151      types[_key2 - 1] = arguments[_key2];
152    }
153    if (!types.some(function (t) {
154      return node instanceof t;
155    })) {
156      this.fail("assertType: invalid type: " + node.typename, node.lineno, node.colno);
157    }
158  };
159  _proto.compileCallExtension = function compileCallExtension(node, frame, async) {
160    var _this4 = this;
161    var args = node.args;
162    var contentArgs = node.contentArgs;
163    var autoescape = typeof node.autoescape === 'boolean' ? node.autoescape : true;
164    if (!async) {
165      this._emit(this.buffer + " += runtime.suppressValue(");
166    }
167    this._emit("env.getExtension(\"" + node.extName + "\")[\"" + node.prop + "\"](");
168    this._emit('context');
169    if (args || contentArgs) {
170      this._emit(',');
171    }
172    if (args) {
173      if (!(args instanceof nodes.NodeList)) {
174        this.fail('compileCallExtension: arguments must be a NodeList, ' + 'use `parser.parseSignature`');
175      }
176      args.children.forEach(function (arg, i) {
177        // Tag arguments are passed normally to the call. Note
178        // that keyword arguments are turned into a single js
179        // object as the last argument, if they exist.
180        _this4._compileExpression(arg, frame);
181        if (i !== args.children.length - 1 || contentArgs.length) {
182          _this4._emit(',');
183        }
184      });
185    }
186    if (contentArgs.length) {
187      contentArgs.forEach(function (arg, i) {
188        if (i > 0) {
189          _this4._emit(',');
190        }
191        if (arg) {
192          _this4._emitLine('function(cb) {');
193          _this4._emitLine('if(!cb) { cb = function(err) { if(err) { throw err; }}}');
194          var id = _this4._pushBuffer();
195          _this4._withScopedSyntax(function () {
196            _this4.compile(arg, frame);
197            _this4._emitLine("cb(null, " + id + ");");
198          });
199          _this4._popBuffer();
200          _this4._emitLine("return " + id + ";");
201          _this4._emitLine('}');
202        } else {
203          _this4._emit('null');
204        }
205      });
206    }
207    if (async) {
208      var res = this._tmpid();
209      this._emitLine(', ' + this._makeCallback(res));
210      this._emitLine(this.buffer + " += runtime.suppressValue(" + res + ", " + autoescape + " && env.opts.autoescape);");
211      this._addScopeLevel();
212    } else {
213      this._emit(')');
214      this._emit(", " + autoescape + " && env.opts.autoescape);\n");
215    }
216  };
217  _proto.compileCallExtensionAsync = function compileCallExtensionAsync(node, frame) {
218    this.compileCallExtension(node, frame, true);
219  };
220  _proto.compileNodeList = function compileNodeList(node, frame) {
221    this._compileChildren(node, frame);
222  };
223  _proto.compileLiteral = function compileLiteral(node) {
224    if (typeof node.value === 'string') {
225      var val = node.value.replace(/\\/g, '\\\\');
226      val = val.replace(/"/g, '\\"');
227      val = val.replace(/\n/g, '\\n');
228      val = val.replace(/\r/g, '\\r');
229      val = val.replace(/\t/g, '\\t');
230      val = val.replace(/\u2028/g, "\\u2028");
231      this._emit("\"" + val + "\"");
232    } else if (node.value === null) {
233      this._emit('null');
234    } else {
235      this._emit(node.value.toString());
236    }
237  };
238  _proto.compileSymbol = function compileSymbol(node, frame) {
239    var name = node.value;
240    var v = frame.lookup(name);
241    if (v) {
242      this._emit(v);
243    } else {
244      this._emit('runtime.contextOrFrameLookup(' + 'context, frame, "' + name + '")');
245    }
246  };
247  _proto.compileGroup = function compileGroup(node, frame) {
248    this._compileAggregate(node, frame, '(', ')');
249  };
250  _proto.compileArray = function compileArray(node, frame) {
251    this._compileAggregate(node, frame, '[', ']');
252  };
253  _proto.compileDict = function compileDict(node, frame) {
254    this._compileAggregate(node, frame, '{', '}');
255  };
256  _proto.compilePair = function compilePair(node, frame) {
257    var key = node.key;
258    var val = node.value;
259    if (key instanceof nodes.Symbol) {
260      key = new nodes.Literal(key.lineno, key.colno, key.value);
261    } else if (!(key instanceof nodes.Literal && typeof key.value === 'string')) {
262      this.fail('compilePair: Dict keys must be strings or names', key.lineno, key.colno);
263    }
264    this.compile(key, frame);
265    this._emit(': ');
266    this._compileExpression(val, frame);
267  };
268  _proto.compileInlineIf = function compileInlineIf(node, frame) {
269    this._emit('(');
270    this.compile(node.cond, frame);
271    this._emit('?');
272    this.compile(node.body, frame);
273    this._emit(':');
274    if (node.else_ !== null) {
275      this.compile(node.else_, frame);
276    } else {
277      this._emit('""');
278    }
279    this._emit(')');
280  };
281  _proto.compileIn = function compileIn(node, frame) {
282    this._emit('runtime.inOperator(');
283    this.compile(node.left, frame);
284    this._emit(',');
285    this.compile(node.right, frame);
286    this._emit(')');
287  };
288  _proto.compileIs = function compileIs(node, frame) {
289    // first, we need to try to get the name of the test function, if it's a
290    // callable (i.e., has args) and not a symbol.
291    var right = node.right.name ? node.right.name.value
292    // otherwise go with the symbol value
293    : node.right.value;
294    this._emit('env.getTest("' + right + '").call(context, ');
295    this.compile(node.left, frame);
296    // compile the arguments for the callable if they exist
297    if (node.right.args) {
298      this._emit(',');
299      this.compile(node.right.args, frame);
300    }
301    this._emit(') === true');
302  };
303  _proto._binOpEmitter = function _binOpEmitter(node, frame, str) {
304    this.compile(node.left, frame);
305    this._emit(str);
306    this.compile(node.right, frame);
307  }
308
309  // ensure concatenation instead of addition
310  // by adding empty string in between
311  ;
312  _proto.compileOr = function compileOr(node, frame) {
313    return this._binOpEmitter(node, frame, ' || ');
314  };
315  _proto.compileAnd = function compileAnd(node, frame) {
316    return this._binOpEmitter(node, frame, ' && ');
317  };
318  _proto.compileAdd = function compileAdd(node, frame) {
319    return this._binOpEmitter(node, frame, ' + ');
320  };
321  _proto.compileConcat = function compileConcat(node, frame) {
322    return this._binOpEmitter(node, frame, ' + "" + ');
323  };
324  _proto.compileSub = function compileSub(node, frame) {
325    return this._binOpEmitter(node, frame, ' - ');
326  };
327  _proto.compileMul = function compileMul(node, frame) {
328    return this._binOpEmitter(node, frame, ' * ');
329  };
330  _proto.compileDiv = function compileDiv(node, frame) {
331    return this._binOpEmitter(node, frame, ' / ');
332  };
333  _proto.compileMod = function compileMod(node, frame) {
334    return this._binOpEmitter(node, frame, ' % ');
335  };
336  _proto.compileNot = function compileNot(node, frame) {
337    this._emit('!');
338    this.compile(node.target, frame);
339  };
340  _proto.compileFloorDiv = function compileFloorDiv(node, frame) {
341    this._emit('Math.floor(');
342    this.compile(node.left, frame);
343    this._emit(' / ');
344    this.compile(node.right, frame);
345    this._emit(')');
346  };
347  _proto.compilePow = function compilePow(node, frame) {
348    this._emit('Math.pow(');
349    this.compile(node.left, frame);
350    this._emit(', ');
351    this.compile(node.right, frame);
352    this._emit(')');
353  };
354  _proto.compileNeg = function compileNeg(node, frame) {
355    this._emit('-');
356    this.compile(node.target, frame);
357  };
358  _proto.compilePos = function compilePos(node, frame) {
359    this._emit('+');
360    this.compile(node.target, frame);
361  };
362  _proto.compileCompare = function compileCompare(node, frame) {
363    var _this5 = this;
364    this.compile(node.expr, frame);
365    node.ops.forEach(function (op) {
366      _this5._emit(" " + compareOps[op.type] + " ");
367      _this5.compile(op.expr, frame);
368    });
369  };
370  _proto.compileLookupVal = function compileLookupVal(node, frame) {
371    this._emit('runtime.memberLookup((');
372    this._compileExpression(node.target, frame);
373    this._emit('),');
374    this._compileExpression(node.val, frame);
375    this._emit(')');
376  };
377  _proto._getNodeName = function _getNodeName(node) {
378    switch (node.typename) {
379      case 'Symbol':
380        return node.value;
381      case 'FunCall':
382        return 'the return value of (' + this._getNodeName(node.name) + ')';
383      case 'LookupVal':
384        return this._getNodeName(node.target) + '["' + this._getNodeName(node.val) + '"]';
385      case 'Literal':
386        return node.value.toString();
387      default:
388        return '--expression--';
389    }
390  };
391  _proto.compileFunCall = function compileFunCall(node, frame) {
392    // Keep track of line/col info at runtime by settings
393    // variables within an expression. An expression in javascript
394    // like (x, y, z) returns the last value, and x and y can be
395    // anything
396    this._emit('(lineno = ' + node.lineno + ', colno = ' + node.colno + ', ');
397    this._emit('runtime.callWrap(');
398    // Compile it as normal.
399    this._compileExpression(node.name, frame);
400
401    // Output the name of what we're calling so we can get friendly errors
402    // if the lookup fails.
403    this._emit(', "' + this._getNodeName(node.name).replace(/"/g, '\\"') + '", context, ');
404    this._compileAggregate(node.args, frame, '[', '])');
405    this._emit(')');
406  };
407  _proto.compileFilter = function compileFilter(node, frame) {
408    var name = node.name;
409    this.assertType(name, nodes.Symbol);
410    this._emit('env.getFilter("' + name.value + '").call(context, ');
411    this._compileAggregate(node.args, frame);
412    this._emit(')');
413  };
414  _proto.compileFilterAsync = function compileFilterAsync(node, frame) {
415    var name = node.name;
416    var symbol = node.symbol.value;
417    this.assertType(name, nodes.Symbol);
418    frame.set(symbol, symbol);
419    this._emit('env.getFilter("' + name.value + '").call(context, ');
420    this._compileAggregate(node.args, frame);
421    this._emitLine(', ' + this._makeCallback(symbol));
422    this._addScopeLevel();
423  };
424  _proto.compileKeywordArgs = function compileKeywordArgs(node, frame) {
425    this._emit('runtime.makeKeywordArgs(');
426    this.compileDict(node, frame);
427    this._emit(')');
428  };
429  _proto.compileSet = function compileSet(node, frame) {
430    var _this6 = this;
431    var ids = [];
432
433    // Lookup the variable names for each identifier and create
434    // new ones if necessary
435    node.targets.forEach(function (target) {
436      var name = target.value;
437      var id = frame.lookup(name);
438      if (id === null || id === undefined) {
439        id = _this6._tmpid();
440
441        // Note: This relies on js allowing scope across
442        // blocks, in case this is created inside an `if`
443        _this6._emitLine('var ' + id + ';');
444      }
445      ids.push(id);
446    });
447    if (node.value) {
448      this._emit(ids.join(' = ') + ' = ');
449      this._compileExpression(node.value, frame);
450      this._emitLine(';');
451    } else {
452      this._emit(ids.join(' = ') + ' = ');
453      this.compile(node.body, frame);
454      this._emitLine(';');
455    }
456    node.targets.forEach(function (target, i) {
457      var id = ids[i];
458      var name = target.value;
459
460      // We are running this for every var, but it's very
461      // uncommon to assign to multiple vars anyway
462      _this6._emitLine("frame.set(\"" + name + "\", " + id + ", true);");
463      _this6._emitLine('if(frame.topLevel) {');
464      _this6._emitLine("context.setVariable(\"" + name + "\", " + id + ");");
465      _this6._emitLine('}');
466      if (name.charAt(0) !== '_') {
467        _this6._emitLine('if(frame.topLevel) {');
468        _this6._emitLine("context.addExport(\"" + name + "\", " + id + ");");
469        _this6._emitLine('}');
470      }
471    });
472  };
473  _proto.compileSwitch = function compileSwitch(node, frame) {
474    var _this7 = this;
475    this._emit('switch (');
476    this.compile(node.expr, frame);
477    this._emit(') {');
478    node.cases.forEach(function (c, i) {
479      _this7._emit('case ');
480      _this7.compile(c.cond, frame);
481      _this7._emit(': ');
482      _this7.compile(c.body, frame);
483      // preserve fall-throughs
484      if (c.body.children.length) {
485        _this7._emitLine('break;');
486      }
487    });
488    if (node.default) {
489      this._emit('default:');
490      this.compile(node.default, frame);
491    }
492    this._emit('}');
493  };
494  _proto.compileIf = function compileIf(node, frame, async) {
495    var _this8 = this;
496    this._emit('if(');
497    this._compileExpression(node.cond, frame);
498    this._emitLine(') {');
499    this._withScopedSyntax(function () {
500      _this8.compile(node.body, frame);
501      if (async) {
502        _this8._emit('cb()');
503      }
504    });
505    if (node.else_) {
506      this._emitLine('}\nelse {');
507      this._withScopedSyntax(function () {
508        _this8.compile(node.else_, frame);
509        if (async) {
510          _this8._emit('cb()');
511        }
512      });
513    } else if (async) {
514      this._emitLine('}\nelse {');
515      this._emit('cb()');
516    }
517    this._emitLine('}');
518  };
519  _proto.compileIfAsync = function compileIfAsync(node, frame) {
520    this._emit('(function(cb) {');
521    this.compileIf(node, frame, true);
522    this._emit('})(' + this._makeCallback());
523    this._addScopeLevel();
524  };
525  _proto._emitLoopBindings = function _emitLoopBindings(node, arr, i, len) {
526    var _this9 = this;
527    var bindings = [{
528      name: 'index',
529      val: i + " + 1"
530    }, {
531      name: 'index0',
532      val: i
533    }, {
534      name: 'revindex',
535      val: len + " - " + i
536    }, {
537      name: 'revindex0',
538      val: len + " - " + i + " - 1"
539    }, {
540      name: 'first',
541      val: i + " === 0"
542    }, {
543      name: 'last',
544      val: i + " === " + len + " - 1"
545    }, {
546      name: 'length',
547      val: len
548    }];
549    bindings.forEach(function (b) {
550      _this9._emitLine("frame.set(\"loop." + b.name + "\", " + b.val + ");");
551    });
552  };
553  _proto.compileFor = function compileFor(node, frame) {
554    var _this10 = this;
555    // Some of this code is ugly, but it keeps the generated code
556    // as fast as possible. ForAsync also shares some of this, but
557    // not much.
558
559    var i = this._tmpid();
560    var len = this._tmpid();
561    var arr = this._tmpid();
562    frame = frame.push();
563    this._emitLine('frame = frame.push();');
564    this._emit("var " + arr + " = ");
565    this._compileExpression(node.arr, frame);
566    this._emitLine(';');
567    this._emit("if(" + arr + ") {");
568    this._emitLine(arr + ' = runtime.fromIterator(' + arr + ');');
569
570    // If multiple names are passed, we need to bind them
571    // appropriately
572    if (node.name instanceof nodes.Array) {
573      this._emitLine("var " + i + ";");
574
575      // The object could be an arroy or object. Note that the
576      // body of the loop is duplicated for each condition, but
577      // we are optimizing for speed over size.
578      this._emitLine("if(runtime.isArray(" + arr + ")) {");
579      this._emitLine("var " + len + " = " + arr + ".length;");
580      this._emitLine("for(" + i + "=0; " + i + " < " + arr + ".length; " + i + "++) {");
581
582      // Bind each declared var
583      node.name.children.forEach(function (child, u) {
584        var tid = _this10._tmpid();
585        _this10._emitLine("var " + tid + " = " + arr + "[" + i + "][" + u + "];");
586        _this10._emitLine("frame.set(\"" + child + "\", " + arr + "[" + i + "][" + u + "]);");
587        frame.set(node.name.children[u].value, tid);
588      });
589      this._emitLoopBindings(node, arr, i, len);
590      this._withScopedSyntax(function () {
591        _this10.compile(node.body, frame);
592      });
593      this._emitLine('}');
594      this._emitLine('} else {');
595      // Iterate over the key/values of an object
596      var _node$name$children = node.name.children,
597        key = _node$name$children[0],
598        val = _node$name$children[1];
599      var k = this._tmpid();
600      var v = this._tmpid();
601      frame.set(key.value, k);
602      frame.set(val.value, v);
603      this._emitLine(i + " = -1;");
604      this._emitLine("var " + len + " = runtime.keys(" + arr + ").length;");
605      this._emitLine("for(var " + k + " in " + arr + ") {");
606      this._emitLine(i + "++;");
607      this._emitLine("var " + v + " = " + arr + "[" + k + "];");
608      this._emitLine("frame.set(\"" + key.value + "\", " + k + ");");
609      this._emitLine("frame.set(\"" + val.value + "\", " + v + ");");
610      this._emitLoopBindings(node, arr, i, len);
611      this._withScopedSyntax(function () {
612        _this10.compile(node.body, frame);
613      });
614      this._emitLine('}');
615      this._emitLine('}');
616    } else {
617      // Generate a typical array iteration
618      var _v = this._tmpid();
619      frame.set(node.name.value, _v);
620      this._emitLine("var " + len + " = " + arr + ".length;");
621      this._emitLine("for(var " + i + "=0; " + i + " < " + arr + ".length; " + i + "++) {");
622      this._emitLine("var " + _v + " = " + arr + "[" + i + "];");
623      this._emitLine("frame.set(\"" + node.name.value + "\", " + _v + ");");
624      this._emitLoopBindings(node, arr, i, len);
625      this._withScopedSyntax(function () {
626        _this10.compile(node.body, frame);
627      });
628      this._emitLine('}');
629    }
630    this._emitLine('}');
631    if (node.else_) {
632      this._emitLine('if (!' + len + ') {');
633      this.compile(node.else_, frame);
634      this._emitLine('}');
635    }
636    this._emitLine('frame = frame.pop();');
637  };
638  _proto._compileAsyncLoop = function _compileAsyncLoop(node, frame, parallel) {
639    var _this11 = this;
640    // This shares some code with the For tag, but not enough to
641    // worry about. This iterates across an object asynchronously,
642    // but not in parallel.
643
644    var i = this._tmpid();
645    var len = this._tmpid();
646    var arr = this._tmpid();
647    var asyncMethod = parallel ? 'asyncAll' : 'asyncEach';
648    frame = frame.push();
649    this._emitLine('frame = frame.push();');
650    this._emit('var ' + arr + ' = runtime.fromIterator(');
651    this._compileExpression(node.arr, frame);
652    this._emitLine(');');
653    if (node.name instanceof nodes.Array) {
654      var arrayLen = node.name.children.length;
655      this._emit("runtime." + asyncMethod + "(" + arr + ", " + arrayLen + ", function(");
656      node.name.children.forEach(function (name) {
657        _this11._emit(name.value + ",");
658      });
659      this._emit(i + ',' + len + ',next) {');
660      node.name.children.forEach(function (name) {
661        var id = name.value;
662        frame.set(id, id);
663        _this11._emitLine("frame.set(\"" + id + "\", " + id + ");");
664      });
665    } else {
666      var id = node.name.value;
667      this._emitLine("runtime." + asyncMethod + "(" + arr + ", 1, function(" + id + ", " + i + ", " + len + ",next) {");
668      this._emitLine('frame.set("' + id + '", ' + id + ');');
669      frame.set(id, id);
670    }
671    this._emitLoopBindings(node, arr, i, len);
672    this._withScopedSyntax(function () {
673      var buf;
674      if (parallel) {
675        buf = _this11._pushBuffer();
676      }
677      _this11.compile(node.body, frame);
678      _this11._emitLine('next(' + i + (buf ? ',' + buf : '') + ');');
679      if (parallel) {
680        _this11._popBuffer();
681      }
682    });
683    var output = this._tmpid();
684    this._emitLine('}, ' + this._makeCallback(output));
685    this._addScopeLevel();
686    if (parallel) {
687      this._emitLine(this.buffer + ' += ' + output + ';');
688    }
689    if (node.else_) {
690      this._emitLine('if (!' + arr + '.length) {');
691      this.compile(node.else_, frame);
692      this._emitLine('}');
693    }
694    this._emitLine('frame = frame.pop();');
695  };
696  _proto.compileAsyncEach = function compileAsyncEach(node, frame) {
697    this._compileAsyncLoop(node, frame);
698  };
699  _proto.compileAsyncAll = function compileAsyncAll(node, frame) {
700    this._compileAsyncLoop(node, frame, true);
701  };
702  _proto._compileMacro = function _compileMacro(node, frame) {
703    var _this12 = this;
704    var args = [];
705    var kwargs = null;
706    var funcId = 'macro_' + this._tmpid();
707    var keepFrame = frame !== undefined;
708
709    // Type check the definition of the args
710    node.args.children.forEach(function (arg, i) {
711      if (i === node.args.children.length - 1 && arg instanceof nodes.Dict) {
712        kwargs = arg;
713      } else {
714        _this12.assertType(arg, nodes.Symbol);
715        args.push(arg);
716      }
717    });
718    var realNames = [].concat(args.map(function (n) {
719      return "l_" + n.value;
720    }), ['kwargs']);
721
722    // Quoted argument names
723    var argNames = args.map(function (n) {
724      return "\"" + n.value + "\"";
725    });
726    var kwargNames = (kwargs && kwargs.children || []).map(function (n) {
727      return "\"" + n.key.value + "\"";
728    });
729
730    // We pass a function to makeMacro which destructures the
731    // arguments so support setting positional args with keywords
732    // args and passing keyword args as positional args
733    // (essentially default values). See runtime.js.
734    var currFrame;
735    if (keepFrame) {
736      currFrame = frame.push(true);
737    } else {
738      currFrame = new Frame();
739    }
740    this._emitLines("var " + funcId + " = runtime.makeMacro(", "[" + argNames.join(', ') + "], ", "[" + kwargNames.join(', ') + "], ", "function (" + realNames.join(', ') + ") {", 'var callerFrame = frame;', 'frame = ' + (keepFrame ? 'frame.push(true);' : 'new runtime.Frame();'), 'kwargs = kwargs || {};', 'if (Object.prototype.hasOwnProperty.call(kwargs, "caller")) {', 'frame.set("caller", kwargs.caller); }');
741
742    // Expose the arguments to the template. Don't need to use
743    // random names because the function
744    // will create a new run-time scope for us
745    args.forEach(function (arg) {
746      _this12._emitLine("frame.set(\"" + arg.value + "\", l_" + arg.value + ");");
747      currFrame.set(arg.value, "l_" + arg.value);
748    });
749
750    // Expose the keyword arguments
751    if (kwargs) {
752      kwargs.children.forEach(function (pair) {
753        var name = pair.key.value;
754        _this12._emit("frame.set(\"" + name + "\", ");
755        _this12._emit("Object.prototype.hasOwnProperty.call(kwargs, \"" + name + "\")");
756        _this12._emit(" ? kwargs[\"" + name + "\"] : ");
757        _this12._compileExpression(pair.value, currFrame);
758        _this12._emit(');');
759      });
760    }
761    var bufferId = this._pushBuffer();
762    this._withScopedSyntax(function () {
763      _this12.compile(node.body, currFrame);
764    });
765    this._emitLine('frame = ' + (keepFrame ? 'frame.pop();' : 'callerFrame;'));
766    this._emitLine("return new runtime.SafeString(" + bufferId + ");");
767    this._emitLine('});');
768    this._popBuffer();
769    return funcId;
770  };
771  _proto.compileMacro = function compileMacro(node, frame) {
772    var funcId = this._compileMacro(node);
773
774    // Expose the macro to the templates
775    var name = node.name.value;
776    frame.set(name, funcId);
777    if (frame.parent) {
778      this._emitLine("frame.set(\"" + name + "\", " + funcId + ");");
779    } else {
780      if (node.name.value.charAt(0) !== '_') {
781        this._emitLine("context.addExport(\"" + name + "\");");
782      }
783      this._emitLine("context.setVariable(\"" + name + "\", " + funcId + ");");
784    }
785  };
786  _proto.compileCaller = function compileCaller(node, frame) {
787    // basically an anonymous "macro expression"
788    this._emit('(function (){');
789    var funcId = this._compileMacro(node, frame);
790    this._emit("return " + funcId + ";})()");
791  };
792  _proto._compileGetTemplate = function _compileGetTemplate(node, frame, eagerCompile, ignoreMissing) {
793    var parentTemplateId = this._tmpid();
794    var parentName = this._templateName();
795    var cb = this._makeCallback(parentTemplateId);
796    var eagerCompileArg = eagerCompile ? 'true' : 'false';
797    var ignoreMissingArg = ignoreMissing ? 'true' : 'false';
798    this._emit('env.getTemplate(');
799    this._compileExpression(node.template, frame);
800    this._emitLine(", " + eagerCompileArg + ", " + parentName + ", " + ignoreMissingArg + ", " + cb);
801    return parentTemplateId;
802  };
803  _proto.compileImport = function compileImport(node, frame) {
804    var target = node.target.value;
805    var id = this._compileGetTemplate(node, frame, false, false);
806    this._addScopeLevel();
807    this._emitLine(id + '.getExported(' + (node.withContext ? 'context.getVariables(), frame, ' : '') + this._makeCallback(id));
808    this._addScopeLevel();
809    frame.set(target, id);
810    if (frame.parent) {
811      this._emitLine("frame.set(\"" + target + "\", " + id + ");");
812    } else {
813      this._emitLine("context.setVariable(\"" + target + "\", " + id + ");");
814    }
815  };
816  _proto.compileFromImport = function compileFromImport(node, frame) {
817    var _this13 = this;
818    var importedId = this._compileGetTemplate(node, frame, false, false);
819    this._addScopeLevel();
820    this._emitLine(importedId + '.getExported(' + (node.withContext ? 'context.getVariables(), frame, ' : '') + this._makeCallback(importedId));
821    this._addScopeLevel();
822    node.names.children.forEach(function (nameNode) {
823      var name;
824      var alias;
825      var id = _this13._tmpid();
826      if (nameNode instanceof nodes.Pair) {
827        name = nameNode.key.value;
828        alias = nameNode.value.value;
829      } else {
830        name = nameNode.value;
831        alias = name;
832      }
833      _this13._emitLine("if(Object.prototype.hasOwnProperty.call(" + importedId + ", \"" + name + "\")) {");
834      _this13._emitLine("var " + id + " = " + importedId + "." + name + ";");
835      _this13._emitLine('} else {');
836      _this13._emitLine("cb(new Error(\"cannot import '" + name + "'\")); return;");
837      _this13._emitLine('}');
838      frame.set(alias, id);
839      if (frame.parent) {
840        _this13._emitLine("frame.set(\"" + alias + "\", " + id + ");");
841      } else {
842        _this13._emitLine("context.setVariable(\"" + alias + "\", " + id + ");");
843      }
844    });
845  };
846  _proto.compileBlock = function compileBlock(node) {
847    var id = this._tmpid();
848
849    // If we are executing outside a block (creating a top-level
850    // block), we really don't want to execute its code because it
851    // will execute twice: once when the child template runs and
852    // again when the parent template runs. Note that blocks
853    // within blocks will *always* execute immediately *and*
854    // wherever else they are invoked (like used in a parent
855    // template). This may have behavioral differences from jinja
856    // because blocks can have side effects, but it seems like a
857    // waste of performance to always execute huge top-level
858    // blocks twice
859    if (!this.inBlock) {
860      this._emit('(parentTemplate ? function(e, c, f, r, cb) { cb(""); } : ');
861    }
862    this._emit("context.getBlock(\"" + node.name.value + "\")");
863    if (!this.inBlock) {
864      this._emit(')');
865    }
866    this._emitLine('(env, context, frame, runtime, ' + this._makeCallback(id));
867    this._emitLine(this.buffer + " += " + id + ";");
868    this._addScopeLevel();
869  };
870  _proto.compileSuper = function compileSuper(node, frame) {
871    var name = node.blockName.value;
872    var id = node.symbol.value;
873    var cb = this._makeCallback(id);
874    this._emitLine("context.getSuper(env, \"" + name + "\", b_" + name + ", frame, runtime, " + cb);
875    this._emitLine(id + " = runtime.markSafe(" + id + ");");
876    this._addScopeLevel();
877    frame.set(id, id);
878  };
879  _proto.compileExtends = function compileExtends(node, frame) {
880    var k = this._tmpid();
881    var parentTemplateId = this._compileGetTemplate(node, frame, true, false);
882
883    // extends is a dynamic tag and can occur within a block like
884    // `if`, so if this happens we need to capture the parent
885    // template in the top-level scope
886    this._emitLine("parentTemplate = " + parentTemplateId);
887    this._emitLine("for(var " + k + " in parentTemplate.blocks) {");
888    this._emitLine("context.addBlock(" + k + ", parentTemplate.blocks[" + k + "]);");
889    this._emitLine('}');
890    this._addScopeLevel();
891  };
892  _proto.compileInclude = function compileInclude(node, frame) {
893    this._emitLine('var tasks = [];');
894    this._emitLine('tasks.push(');
895    this._emitLine('function(callback) {');
896    var id = this._compileGetTemplate(node, frame, false, node.ignoreMissing);
897    this._emitLine("callback(null," + id + ");});");
898    this._emitLine('});');
899    var id2 = this._tmpid();
900    this._emitLine('tasks.push(');
901    this._emitLine('function(template, callback){');
902    this._emitLine('template.render(context.getVariables(), frame, ' + this._makeCallback(id2));
903    this._emitLine('callback(null,' + id2 + ');});');
904    this._emitLine('});');
905    this._emitLine('tasks.push(');
906    this._emitLine('function(result, callback){');
907    this._emitLine(this.buffer + " += result;");
908    this._emitLine('callback(null);');
909    this._emitLine('});');
910    this._emitLine('env.waterfall(tasks, function(){');
911    this._addScopeLevel();
912  };
913  _proto.compileTemplateData = function compileTemplateData(node, frame) {
914    this.compileLiteral(node, frame);
915  };
916  _proto.compileCapture = function compileCapture(node, frame) {
917    var _this14 = this;
918    // we need to temporarily override the current buffer id as 'output'
919    // so the set block writes to the capture output instead of the buffer
920    var buffer = this.buffer;
921    this.buffer = 'output';
922    this._emitLine('(function() {');
923    this._emitLine('var output = "";');
924    this._withScopedSyntax(function () {
925      _this14.compile(node.body, frame);
926    });
927    this._emitLine('return output;');
928    this._emitLine('})()');
929    // and of course, revert back to the old buffer id
930    this.buffer = buffer;
931  };
932  _proto.compileOutput = function compileOutput(node, frame) {
933    var _this15 = this;
934    var children = node.children;
935    children.forEach(function (child) {
936      // TemplateData is a special case because it is never
937      // autoescaped, so simply output it for optimization
938      if (child instanceof nodes.TemplateData) {
939        if (child.value) {
940          _this15._emit(_this15.buffer + " += ");
941          _this15.compileLiteral(child, frame);
942          _this15._emitLine(';');
943        }
944      } else {
945        _this15._emit(_this15.buffer + " += runtime.suppressValue(");
946        if (_this15.throwOnUndefined) {
947          _this15._emit('runtime.ensureDefined(');
948        }
949        _this15.compile(child, frame);
950        if (_this15.throwOnUndefined) {
951          _this15._emit("," + node.lineno + "," + node.colno + ")");
952        }
953        _this15._emit(', env.opts.autoescape);\n');
954      }
955    });
956  };
957  _proto.compileRoot = function compileRoot(node, frame) {
958    var _this16 = this;
959    if (frame) {
960      this.fail('compileRoot: root node can\'t have frame');
961    }
962    frame = new Frame();
963    this._emitFuncBegin(node, 'root');
964    this._emitLine('var parentTemplate = null;');
965    this._compileChildren(node, frame);
966    this._emitLine('if(parentTemplate) {');
967    this._emitLine('parentTemplate.rootRenderFunc(env, context, frame, runtime, cb);');
968    this._emitLine('} else {');
969    this._emitLine("cb(null, " + this.buffer + ");");
970    this._emitLine('}');
971    this._emitFuncEnd(true);
972    this.inBlock = true;
973    var blockNames = [];
974    var blocks = node.findAll(nodes.Block);
975    blocks.forEach(function (block, i) {
976      var name = block.name.value;
977      if (blockNames.indexOf(name) !== -1) {
978        throw new Error("Block \"" + name + "\" defined more than once.");
979      }
980      blockNames.push(name);
981      _this16._emitFuncBegin(block, "b_" + name);
982      var tmpFrame = new Frame();
983      _this16._emitLine('var frame = frame.push(true);');
984      _this16.compile(block.body, tmpFrame);
985      _this16._emitFuncEnd();
986    });
987    this._emitLine('return {');
988    blocks.forEach(function (block, i) {
989      var blockName = "b_" + block.name.value;
990      _this16._emitLine(blockName + ": " + blockName + ",");
991    });
992    this._emitLine('root: root\n};');
993  };
994  _proto.compile = function compile(node, frame) {
995    var _compile = this['compile' + node.typename];
996    if (_compile) {
997      _compile.call(this, node, frame);
998    } else {
999      this.fail("compile: Cannot compile node: " + node.typename, node.lineno, node.colno);
1000    }
1001  };
1002  _proto.getCode = function getCode() {
1003    return this.codebuf.join('');
1004  };
1005  return Compiler;
1006}(Obj);
1007module.exports = {
1008  compile: function compile(src, asyncFilters, extensions, name, opts) {
1009    if (opts === void 0) {
1010      opts = {};
1011    }
1012    var c = new Compiler(name, opts.throwOnUndefined);
1013
1014    // Run the extension preprocessors against the source.
1015    var preprocessors = (extensions || []).map(function (ext) {
1016      return ext.preprocess;
1017    }).filter(function (f) {
1018      return !!f;
1019    });
1020    var processedSrc = preprocessors.reduce(function (s, processor) {
1021      return processor(s);
1022    }, src);
1023    c.compile(transformer.transform(parser.parse(processedSrc, extensions, opts), asyncFilters, name));
1024    return c.getCode();
1025  },
1026  Compiler: Compiler
1027};