Mario Romano 29df96a085 react app
2016-04-06 17:52:19 +01:00

159 lines
4.2 KiB
JavaScript

/**
* 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 function 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 destructuringVisitors = require('./es6-destructuring-visitors');
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
/**
* @public
*/
function visitArrowFunction(traverse, node, path, state) {
var notInExpression = (path[0].type === Syntax.ExpressionStatement);
// Wrap a function into a grouping operator, if it's not
// in the expression position.
if (notInExpression) {
utils.append('(', state);
}
utils.append('function', state);
renderParams(traverse, node, path, 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.
var containsBindingSyntax =
utils.containsChildMatching(node.body, function(node) {
return node.type === Syntax.ThisExpression
|| (node.type === Syntax.Identifier
&& node.name === "super");
});
if (containsBindingSyntax) {
utils.append('.bind(this)', state);
}
utils.catchupWhiteSpace(node.range[1], state);
// Close wrapper if not in the expression.
if (notInExpression) {
utils.append(')', state);
}
return false;
}
function renderParams(traverse, node, path, 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) {
path.unshift(node);
traverse(node.params, path, state);
path.unshift();
}
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);
// Special handling of rest param.
if (node.rest) {
utils.append(
restParamVisitors.renderRestParamSetup(node, state),
state
);
}
// Special handling of destructured params.
destructuringVisitors.renderDestructuredComponents(
node,
utils.updateState(state, {
localScope: {
parentNode: state.parentNode,
parentScope: state.parentScope,
identifiers: state.identifiers,
tempVarIndex: 0
}
})
);
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
];