react / wstein / node_modules / jest-cli / node_modules / istanbul / node_modules / handlebars / dist / amd / handlebars / compiler / compiler.js
80738 viewsdefine(1["../exception","../utils","./ast","exports"],2function(__dependency1__, __dependency2__, __dependency3__, __exports__) {3"use strict";4var Exception = __dependency1__["default"];5var isArray = __dependency2__.isArray;6var indexOf = __dependency2__.indexOf;7var AST = __dependency3__["default"];89var slice = [].slice;101112function Compiler() {}1314__exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a15// function in a context. This is necessary for mustache compatibility, which16// requires that context functions in blocks are evaluated by blockHelperMissing,17// and then proceed as if the resulting value was provided to blockHelperMissing.1819Compiler.prototype = {20compiler: Compiler,2122equals: function(other) {23var len = this.opcodes.length;24if (other.opcodes.length !== len) {25return false;26}2728for (var i = 0; i < len; i++) {29var opcode = this.opcodes[i],30otherOpcode = other.opcodes[i];31if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) {32return false;33}34}3536// We know that length is the same between the two arrays because they are directly tied37// to the opcode behavior above.38len = this.children.length;39for (i = 0; i < len; i++) {40if (!this.children[i].equals(other.children[i])) {41return false;42}43}4445return true;46},4748guid: 0,4950compile: function(program, options) {51this.sourceNode = [];52this.opcodes = [];53this.children = [];54this.options = options;55this.stringParams = options.stringParams;56this.trackIds = options.trackIds;5758options.blockParams = options.blockParams || [];5960// These changes will propagate to the other compiler components61var knownHelpers = options.knownHelpers;62options.knownHelpers = {63'helperMissing': true,64'blockHelperMissing': true,65'each': true,66'if': true,67'unless': true,68'with': true,69'log': true,70'lookup': true71};72if (knownHelpers) {73for (var name in knownHelpers) {74options.knownHelpers[name] = knownHelpers[name];75}76}7778return this.accept(program);79},8081compileProgram: function(program) {82var result = new this.compiler().compile(program, this.options);83var guid = this.guid++;8485this.usePartial = this.usePartial || result.usePartial;8687this.children[guid] = result;88this.useDepths = this.useDepths || result.useDepths;8990return guid;91},9293accept: function(node) {94this.sourceNode.unshift(node);95var ret = this[node.type](node);96this.sourceNode.shift();97return ret;98},99100Program: function(program) {101this.options.blockParams.unshift(program.blockParams);102103var body = program.body;104for(var i=0, l=body.length; i<l; i++) {105this.accept(body[i]);106}107108this.options.blockParams.shift();109110this.isSimple = l === 1;111this.blockParams = program.blockParams ? program.blockParams.length : 0;112113return this;114},115116BlockStatement: function(block) {117transformLiteralToPath(block);118119var program = block.program,120inverse = block.inverse;121122program = program && this.compileProgram(program);123inverse = inverse && this.compileProgram(inverse);124125var type = this.classifySexpr(block);126127if (type === 'helper') {128this.helperSexpr(block, program, inverse);129} else if (type === 'simple') {130this.simpleSexpr(block);131132// now that the simple mustache is resolved, we need to133// evaluate it by executing `blockHelperMissing`134this.opcode('pushProgram', program);135this.opcode('pushProgram', inverse);136this.opcode('emptyHash');137this.opcode('blockValue', block.path.original);138} else {139this.ambiguousSexpr(block, program, inverse);140141// now that the simple mustache is resolved, we need to142// evaluate it by executing `blockHelperMissing`143this.opcode('pushProgram', program);144this.opcode('pushProgram', inverse);145this.opcode('emptyHash');146this.opcode('ambiguousBlockValue');147}148149this.opcode('append');150},151152PartialStatement: function(partial) {153this.usePartial = true;154155var params = partial.params;156if (params.length > 1) {157throw new Exception('Unsupported number of partial arguments: ' + params.length, partial);158} else if (!params.length) {159params.push({type: 'PathExpression', parts: [], depth: 0});160}161162var partialName = partial.name.original,163isDynamic = partial.name.type === 'SubExpression';164if (isDynamic) {165this.accept(partial.name);166}167168this.setupFullMustacheParams(partial, undefined, undefined, true);169170var indent = partial.indent || '';171if (this.options.preventIndent && indent) {172this.opcode('appendContent', indent);173indent = '';174}175176this.opcode('invokePartial', isDynamic, partialName, indent);177this.opcode('append');178},179180MustacheStatement: function(mustache) {181this.SubExpression(mustache);182183if(mustache.escaped && !this.options.noEscape) {184this.opcode('appendEscaped');185} else {186this.opcode('append');187}188},189190ContentStatement: function(content) {191if (content.value) {192this.opcode('appendContent', content.value);193}194},195196CommentStatement: function() {},197198SubExpression: function(sexpr) {199transformLiteralToPath(sexpr);200var type = this.classifySexpr(sexpr);201202if (type === 'simple') {203this.simpleSexpr(sexpr);204} else if (type === 'helper') {205this.helperSexpr(sexpr);206} else {207this.ambiguousSexpr(sexpr);208}209},210ambiguousSexpr: function(sexpr, program, inverse) {211var path = sexpr.path,212name = path.parts[0],213isBlock = program != null || inverse != null;214215this.opcode('getContext', path.depth);216217this.opcode('pushProgram', program);218this.opcode('pushProgram', inverse);219220this.accept(path);221222this.opcode('invokeAmbiguous', name, isBlock);223},224225simpleSexpr: function(sexpr) {226this.accept(sexpr.path);227this.opcode('resolvePossibleLambda');228},229230helperSexpr: function(sexpr, program, inverse) {231var params = this.setupFullMustacheParams(sexpr, program, inverse),232path = sexpr.path,233name = path.parts[0];234235if (this.options.knownHelpers[name]) {236this.opcode('invokeKnownHelper', params.length, name);237} else if (this.options.knownHelpersOnly) {238throw new Exception("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);239} else {240path.falsy = true;241242this.accept(path);243this.opcode('invokeHelper', params.length, path.original, AST.helpers.simpleId(path));244}245},246247PathExpression: function(path) {248this.addDepth(path.depth);249this.opcode('getContext', path.depth);250251var name = path.parts[0],252scoped = AST.helpers.scopedId(path),253blockParamId = !path.depth && !scoped && this.blockParamIndex(name);254255if (blockParamId) {256this.opcode('lookupBlockParam', blockParamId, path.parts);257} else if (!name) {258// Context reference, i.e. `{{foo .}}` or `{{foo ..}}`259this.opcode('pushContext');260} else if (path.data) {261this.options.data = true;262this.opcode('lookupData', path.depth, path.parts);263} else {264this.opcode('lookupOnContext', path.parts, path.falsy, scoped);265}266},267268StringLiteral: function(string) {269this.opcode('pushString', string.value);270},271272NumberLiteral: function(number) {273this.opcode('pushLiteral', number.value);274},275276BooleanLiteral: function(bool) {277this.opcode('pushLiteral', bool.value);278},279280Hash: function(hash) {281var pairs = hash.pairs, i, l;282283this.opcode('pushHash');284285for (i=0, l=pairs.length; i<l; i++) {286this.pushParam(pairs[i].value);287}288while (i--) {289this.opcode('assignToHash', pairs[i].key);290}291this.opcode('popHash');292},293294// HELPERS295opcode: function(name) {296this.opcodes.push({ opcode: name, args: slice.call(arguments, 1), loc: this.sourceNode[0].loc });297},298299addDepth: function(depth) {300if (!depth) {301return;302}303304this.useDepths = true;305},306307classifySexpr: function(sexpr) {308var isSimple = AST.helpers.simpleId(sexpr.path);309310var isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]);311312// a mustache is an eligible helper if:313// * its id is simple (a single part, not `this` or `..`)314var isHelper = !isBlockParam && AST.helpers.helperExpression(sexpr);315316// if a mustache is an eligible helper but not a definite317// helper, it is ambiguous, and will be resolved in a later318// pass or at runtime.319var isEligible = !isBlockParam && (isHelper || isSimple);320321var options = this.options;322323// if ambiguous, we can possibly resolve the ambiguity now324// An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc.325if (isEligible && !isHelper) {326var name = sexpr.path.parts[0];327328if (options.knownHelpers[name]) {329isHelper = true;330} else if (options.knownHelpersOnly) {331isEligible = false;332}333}334335if (isHelper) { return 'helper'; }336else if (isEligible) { return 'ambiguous'; }337else { return 'simple'; }338},339340pushParams: function(params) {341for(var i=0, l=params.length; i<l; i++) {342this.pushParam(params[i]);343}344},345346pushParam: function(val) {347var value = val.value != null ? val.value : val.original || '';348349if (this.stringParams) {350if (value.replace) {351value = value352.replace(/^(\.?\.\/)*/g, '')353.replace(/\//g, '.');354}355356if(val.depth) {357this.addDepth(val.depth);358}359this.opcode('getContext', val.depth || 0);360this.opcode('pushStringParam', value, val.type);361362if (val.type === 'SubExpression') {363// SubExpressions get evaluated and passed in364// in string params mode.365this.accept(val);366}367} else {368if (this.trackIds) {369var blockParamIndex;370if (val.parts && !AST.helpers.scopedId(val) && !val.depth) {371blockParamIndex = this.blockParamIndex(val.parts[0]);372}373if (blockParamIndex) {374var blockParamChild = val.parts.slice(1).join('.');375this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild);376} else {377value = val.original || value;378if (value.replace) {379value = value380.replace(/^\.\//g, '')381.replace(/^\.$/g, '');382}383384this.opcode('pushId', val.type, value);385}386}387this.accept(val);388}389},390391setupFullMustacheParams: function(sexpr, program, inverse, omitEmpty) {392var params = sexpr.params;393this.pushParams(params);394395this.opcode('pushProgram', program);396this.opcode('pushProgram', inverse);397398if (sexpr.hash) {399this.accept(sexpr.hash);400} else {401this.opcode('emptyHash', omitEmpty);402}403404return params;405},406407blockParamIndex: function(name) {408for (var depth = 0, len = this.options.blockParams.length; depth < len; depth++) {409var blockParams = this.options.blockParams[depth],410param = blockParams && indexOf(blockParams, name);411if (blockParams && param >= 0) {412return [depth, param];413}414}415}416};417418function precompile(input, options, env) {419if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {420throw new Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);421}422423options = options || {};424if (!('data' in options)) {425options.data = true;426}427if (options.compat) {428options.useDepths = true;429}430431var ast = env.parse(input, options);432var environment = new env.Compiler().compile(ast, options);433return new env.JavaScriptCompiler().compile(environment, options);434}435436__exports__.precompile = precompile;function compile(input, options, env) {437if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {438throw new Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);439}440441options = options || {};442443if (!('data' in options)) {444options.data = true;445}446if (options.compat) {447options.useDepths = true;448}449450var compiled;451452function compileInput() {453var ast = env.parse(input, options);454var environment = new env.Compiler().compile(ast, options);455var templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true);456return env.template(templateSpec);457}458459// Template is only compiled on first use and cached after that point.460var ret = function(context, options) {461if (!compiled) {462compiled = compileInput();463}464return compiled.call(this, context, options);465};466ret._setup = function(options) {467if (!compiled) {468compiled = compileInput();469}470return compiled._setup(options);471};472ret._child = function(i, data, blockParams, depths) {473if (!compiled) {474compiled = compileInput();475}476return compiled._child(i, data, blockParams, depths);477};478return ret;479}480481__exports__.compile = compile;function argEquals(a, b) {482if (a === b) {483return true;484}485486if (isArray(a) && isArray(b) && a.length === b.length) {487for (var i = 0; i < a.length; i++) {488if (!argEquals(a[i], b[i])) {489return false;490}491}492return true;493}494}495496function transformLiteralToPath(sexpr) {497if (!sexpr.path.parts) {498var literal = sexpr.path;499// Casting to string here to make false and 0 literal values play nicely with the rest500// of the system.501sexpr.path = new AST.PathExpression(false, 0, [literal.original+''], literal.original+'', literal.log);502}503}504});505506