react / wstein / node_modules / jest-cli / node_modules / istanbul / node_modules / handlebars / dist / cjs / handlebars / compiler / compiler.js
80728 views"use strict";1var Exception = require("../exception")["default"];2var isArray = require("../utils").isArray;3var indexOf = require("../utils").indexOf;4var AST = require("./ast")["default"];56var slice = [].slice;789function Compiler() {}1011exports.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a12// function in a context. This is necessary for mustache compatibility, which13// requires that context functions in blocks are evaluated by blockHelperMissing,14// and then proceed as if the resulting value was provided to blockHelperMissing.1516Compiler.prototype = {17compiler: Compiler,1819equals: function(other) {20var len = this.opcodes.length;21if (other.opcodes.length !== len) {22return false;23}2425for (var i = 0; i < len; i++) {26var opcode = this.opcodes[i],27otherOpcode = other.opcodes[i];28if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) {29return false;30}31}3233// We know that length is the same between the two arrays because they are directly tied34// to the opcode behavior above.35len = this.children.length;36for (i = 0; i < len; i++) {37if (!this.children[i].equals(other.children[i])) {38return false;39}40}4142return true;43},4445guid: 0,4647compile: function(program, options) {48this.sourceNode = [];49this.opcodes = [];50this.children = [];51this.options = options;52this.stringParams = options.stringParams;53this.trackIds = options.trackIds;5455options.blockParams = options.blockParams || [];5657// These changes will propagate to the other compiler components58var knownHelpers = options.knownHelpers;59options.knownHelpers = {60'helperMissing': true,61'blockHelperMissing': true,62'each': true,63'if': true,64'unless': true,65'with': true,66'log': true,67'lookup': true68};69if (knownHelpers) {70for (var name in knownHelpers) {71options.knownHelpers[name] = knownHelpers[name];72}73}7475return this.accept(program);76},7778compileProgram: function(program) {79var result = new this.compiler().compile(program, this.options);80var guid = this.guid++;8182this.usePartial = this.usePartial || result.usePartial;8384this.children[guid] = result;85this.useDepths = this.useDepths || result.useDepths;8687return guid;88},8990accept: function(node) {91this.sourceNode.unshift(node);92var ret = this[node.type](node);93this.sourceNode.shift();94return ret;95},9697Program: function(program) {98this.options.blockParams.unshift(program.blockParams);99100var body = program.body;101for(var i=0, l=body.length; i<l; i++) {102this.accept(body[i]);103}104105this.options.blockParams.shift();106107this.isSimple = l === 1;108this.blockParams = program.blockParams ? program.blockParams.length : 0;109110return this;111},112113BlockStatement: function(block) {114transformLiteralToPath(block);115116var program = block.program,117inverse = block.inverse;118119program = program && this.compileProgram(program);120inverse = inverse && this.compileProgram(inverse);121122var type = this.classifySexpr(block);123124if (type === 'helper') {125this.helperSexpr(block, program, inverse);126} else if (type === 'simple') {127this.simpleSexpr(block);128129// now that the simple mustache is resolved, we need to130// evaluate it by executing `blockHelperMissing`131this.opcode('pushProgram', program);132this.opcode('pushProgram', inverse);133this.opcode('emptyHash');134this.opcode('blockValue', block.path.original);135} else {136this.ambiguousSexpr(block, program, inverse);137138// now that the simple mustache is resolved, we need to139// evaluate it by executing `blockHelperMissing`140this.opcode('pushProgram', program);141this.opcode('pushProgram', inverse);142this.opcode('emptyHash');143this.opcode('ambiguousBlockValue');144}145146this.opcode('append');147},148149PartialStatement: function(partial) {150this.usePartial = true;151152var params = partial.params;153if (params.length > 1) {154throw new Exception('Unsupported number of partial arguments: ' + params.length, partial);155} else if (!params.length) {156params.push({type: 'PathExpression', parts: [], depth: 0});157}158159var partialName = partial.name.original,160isDynamic = partial.name.type === 'SubExpression';161if (isDynamic) {162this.accept(partial.name);163}164165this.setupFullMustacheParams(partial, undefined, undefined, true);166167var indent = partial.indent || '';168if (this.options.preventIndent && indent) {169this.opcode('appendContent', indent);170indent = '';171}172173this.opcode('invokePartial', isDynamic, partialName, indent);174this.opcode('append');175},176177MustacheStatement: function(mustache) {178this.SubExpression(mustache);179180if(mustache.escaped && !this.options.noEscape) {181this.opcode('appendEscaped');182} else {183this.opcode('append');184}185},186187ContentStatement: function(content) {188if (content.value) {189this.opcode('appendContent', content.value);190}191},192193CommentStatement: function() {},194195SubExpression: function(sexpr) {196transformLiteralToPath(sexpr);197var type = this.classifySexpr(sexpr);198199if (type === 'simple') {200this.simpleSexpr(sexpr);201} else if (type === 'helper') {202this.helperSexpr(sexpr);203} else {204this.ambiguousSexpr(sexpr);205}206},207ambiguousSexpr: function(sexpr, program, inverse) {208var path = sexpr.path,209name = path.parts[0],210isBlock = program != null || inverse != null;211212this.opcode('getContext', path.depth);213214this.opcode('pushProgram', program);215this.opcode('pushProgram', inverse);216217this.accept(path);218219this.opcode('invokeAmbiguous', name, isBlock);220},221222simpleSexpr: function(sexpr) {223this.accept(sexpr.path);224this.opcode('resolvePossibleLambda');225},226227helperSexpr: function(sexpr, program, inverse) {228var params = this.setupFullMustacheParams(sexpr, program, inverse),229path = sexpr.path,230name = path.parts[0];231232if (this.options.knownHelpers[name]) {233this.opcode('invokeKnownHelper', params.length, name);234} else if (this.options.knownHelpersOnly) {235throw new Exception("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);236} else {237path.falsy = true;238239this.accept(path);240this.opcode('invokeHelper', params.length, path.original, AST.helpers.simpleId(path));241}242},243244PathExpression: function(path) {245this.addDepth(path.depth);246this.opcode('getContext', path.depth);247248var name = path.parts[0],249scoped = AST.helpers.scopedId(path),250blockParamId = !path.depth && !scoped && this.blockParamIndex(name);251252if (blockParamId) {253this.opcode('lookupBlockParam', blockParamId, path.parts);254} else if (!name) {255// Context reference, i.e. `{{foo .}}` or `{{foo ..}}`256this.opcode('pushContext');257} else if (path.data) {258this.options.data = true;259this.opcode('lookupData', path.depth, path.parts);260} else {261this.opcode('lookupOnContext', path.parts, path.falsy, scoped);262}263},264265StringLiteral: function(string) {266this.opcode('pushString', string.value);267},268269NumberLiteral: function(number) {270this.opcode('pushLiteral', number.value);271},272273BooleanLiteral: function(bool) {274this.opcode('pushLiteral', bool.value);275},276277Hash: function(hash) {278var pairs = hash.pairs, i, l;279280this.opcode('pushHash');281282for (i=0, l=pairs.length; i<l; i++) {283this.pushParam(pairs[i].value);284}285while (i--) {286this.opcode('assignToHash', pairs[i].key);287}288this.opcode('popHash');289},290291// HELPERS292opcode: function(name) {293this.opcodes.push({ opcode: name, args: slice.call(arguments, 1), loc: this.sourceNode[0].loc });294},295296addDepth: function(depth) {297if (!depth) {298return;299}300301this.useDepths = true;302},303304classifySexpr: function(sexpr) {305var isSimple = AST.helpers.simpleId(sexpr.path);306307var isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]);308309// a mustache is an eligible helper if:310// * its id is simple (a single part, not `this` or `..`)311var isHelper = !isBlockParam && AST.helpers.helperExpression(sexpr);312313// if a mustache is an eligible helper but not a definite314// helper, it is ambiguous, and will be resolved in a later315// pass or at runtime.316var isEligible = !isBlockParam && (isHelper || isSimple);317318var options = this.options;319320// if ambiguous, we can possibly resolve the ambiguity now321// An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc.322if (isEligible && !isHelper) {323var name = sexpr.path.parts[0];324325if (options.knownHelpers[name]) {326isHelper = true;327} else if (options.knownHelpersOnly) {328isEligible = false;329}330}331332if (isHelper) { return 'helper'; }333else if (isEligible) { return 'ambiguous'; }334else { return 'simple'; }335},336337pushParams: function(params) {338for(var i=0, l=params.length; i<l; i++) {339this.pushParam(params[i]);340}341},342343pushParam: function(val) {344var value = val.value != null ? val.value : val.original || '';345346if (this.stringParams) {347if (value.replace) {348value = value349.replace(/^(\.?\.\/)*/g, '')350.replace(/\//g, '.');351}352353if(val.depth) {354this.addDepth(val.depth);355}356this.opcode('getContext', val.depth || 0);357this.opcode('pushStringParam', value, val.type);358359if (val.type === 'SubExpression') {360// SubExpressions get evaluated and passed in361// in string params mode.362this.accept(val);363}364} else {365if (this.trackIds) {366var blockParamIndex;367if (val.parts && !AST.helpers.scopedId(val) && !val.depth) {368blockParamIndex = this.blockParamIndex(val.parts[0]);369}370if (blockParamIndex) {371var blockParamChild = val.parts.slice(1).join('.');372this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild);373} else {374value = val.original || value;375if (value.replace) {376value = value377.replace(/^\.\//g, '')378.replace(/^\.$/g, '');379}380381this.opcode('pushId', val.type, value);382}383}384this.accept(val);385}386},387388setupFullMustacheParams: function(sexpr, program, inverse, omitEmpty) {389var params = sexpr.params;390this.pushParams(params);391392this.opcode('pushProgram', program);393this.opcode('pushProgram', inverse);394395if (sexpr.hash) {396this.accept(sexpr.hash);397} else {398this.opcode('emptyHash', omitEmpty);399}400401return params;402},403404blockParamIndex: function(name) {405for (var depth = 0, len = this.options.blockParams.length; depth < len; depth++) {406var blockParams = this.options.blockParams[depth],407param = blockParams && indexOf(blockParams, name);408if (blockParams && param >= 0) {409return [depth, param];410}411}412}413};414415function precompile(input, options, env) {416if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {417throw new Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);418}419420options = options || {};421if (!('data' in options)) {422options.data = true;423}424if (options.compat) {425options.useDepths = true;426}427428var ast = env.parse(input, options);429var environment = new env.Compiler().compile(ast, options);430return new env.JavaScriptCompiler().compile(environment, options);431}432433exports.precompile = precompile;function compile(input, options, env) {434if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {435throw new Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);436}437438options = options || {};439440if (!('data' in options)) {441options.data = true;442}443if (options.compat) {444options.useDepths = true;445}446447var compiled;448449function compileInput() {450var ast = env.parse(input, options);451var environment = new env.Compiler().compile(ast, options);452var templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true);453return env.template(templateSpec);454}455456// Template is only compiled on first use and cached after that point.457var ret = function(context, options) {458if (!compiled) {459compiled = compileInput();460}461return compiled.call(this, context, options);462};463ret._setup = function(options) {464if (!compiled) {465compiled = compileInput();466}467return compiled._setup(options);468};469ret._child = function(i, data, blockParams, depths) {470if (!compiled) {471compiled = compileInput();472}473return compiled._child(i, data, blockParams, depths);474};475return ret;476}477478exports.compile = compile;function argEquals(a, b) {479if (a === b) {480return true;481}482483if (isArray(a) && isArray(b) && a.length === b.length) {484for (var i = 0; i < a.length; i++) {485if (!argEquals(a[i], b[i])) {486return false;487}488}489return true;490}491}492493function transformLiteralToPath(sexpr) {494if (!sexpr.path.parts) {495var literal = sexpr.path;496// Casting to string here to make false and 0 literal values play nicely with the rest497// of the system.498sexpr.path = new AST.PathExpression(false, 0, [literal.original+''], literal.original+'', literal.log);499}500}501502