/** * Copyright 2013 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /*global exports:true*/ /** * Desugars ES6 Arrow functions to ES3 function expressions. * If the function contains `this` expression -- automatically * binds the funciton to current value of `this`. * * Single parameter, simple expression: * * [1, 2, 3].map(x => x * x); * * [1, 2, 3].map(function(x) { return x * x; }); * * Several parameters, complex block: * * this.users.forEach((user, idx) => { * return this.isActive(idx) && this.send(user); * }); * * this.users.forEach(function(user, idx) { * return this.isActive(idx) && this.send(user); * }.bind(this)); * */ var restParamVisitors = require('./es6-rest-param-visitors'); var Syntax = require('esprima-fb').Syntax; var utils = require('../src/utils'); /** * @public */ function visitArrowFunction(traverse, node, path, state) { // Prologue. utils.append('function', state); renderParams(node, state); // Skip arrow. utils.catchupWhiteSpace(node.body.range[0], state); var renderBody = node.body.type == Syntax.BlockStatement ? renderStatementBody : renderExpressionBody; path.unshift(node); renderBody(traverse, node, path, state); path.shift(); // Bind the function only if `this` value is used // inside it or inside any sub-expression. if (utils.containsChildOfType(node.body, Syntax.ThisExpression)) { utils.append('.bind(this)', state); } return false; } function renderParams(node, state) { // To preserve inline typechecking directives, we // distinguish between parens-free and paranthesized single param. if (isParensFreeSingleParam(node, state) || !node.params.length) { utils.append('(', state); } if (node.params.length !== 0) { utils.catchup(node.params[node.params.length - 1].range[1], state); } utils.append(')', state); } function isParensFreeSingleParam(node, state) { return node.params.length === 1 && state.g.source[state.g.position] !== '('; } function renderExpressionBody(traverse, node, path, state) { // Wrap simple expression bodies into a block // with explicit return statement. utils.append('{', state); if (node.rest) { utils.append( restParamVisitors.renderRestParamSetup(node), state ); } utils.append('return ', state); renderStatementBody(traverse, node, path, state); utils.append(';}', state); } function renderStatementBody(traverse, node, path, state) { traverse(node.body, path, state); utils.catchup(node.body.range[1], state); } visitArrowFunction.test = function(node, path, state) { return node.type === Syntax.ArrowFunctionExpression; }; exports.visitorList = [ visitArrowFunction ];