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

281 lines
8.3 KiB
JavaScript

/**
* Copyright 2014 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*/
/**
* Implements ES6 destructuring assignment and pattern matchng.
*
* function init({port, ip, coords: [x, y]}) {
* return (x && y) ? {id, port} : {ip};
* };
*
* function init($__0) {
* var
* port = $__0.port,
* ip = $__0.ip,
* $__1 = $__0.coords,
* x = $__1[0],
* y = $__1[1];
* return (x && y) ? {id, port} : {ip};
* }
*
* var x, {ip, port} = init({ip, port});
*
* var x, $__0 = init({ip, port}), ip = $__0.ip, port = $__0.port;
*
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
var reservedWordsHelper = require('./reserved-words-helper');
var restParamVisitors = require('./es6-rest-param-visitors');
var restPropertyHelpers = require('./es7-rest-property-helpers');
// -------------------------------------------------------
// 1. Structured variable declarations.
//
// var [a, b] = [b, a];
// var {x, y} = {y, x};
// -------------------------------------------------------
function visitStructuredVariable(traverse, node, path, state) {
// Allocate new temp for the pattern.
utils.append(utils.getTempVar(state.localScope.tempVarIndex) + '=', state);
// Skip the pattern and assign the init to the temp.
utils.catchupWhiteSpace(node.init.range[0], state);
traverse(node.init, path, state);
utils.catchup(node.init.range[1], state);
// Render the destructured data.
utils.append(',' + getDestructuredComponents(node.id, state), state);
state.localScope.tempVarIndex++;
return false;
}
visitStructuredVariable.test = function(node, path, state) {
return node.type === Syntax.VariableDeclarator &&
isStructuredPattern(node.id);
};
function isStructuredPattern(node) {
return node.type === Syntax.ObjectPattern ||
node.type === Syntax.ArrayPattern;
}
// Main function which does actual recursive destructuring
// of nested complex structures.
function getDestructuredComponents(node, state) {
var tmpIndex = state.localScope.tempVarIndex;
var components = [];
var patternItems = getPatternItems(node);
for (var idx = 0; idx < patternItems.length; idx++) {
var item = patternItems[idx];
if (!item) {
continue;
}
if (item.type === Syntax.SpreadElement) {
// Spread/rest of an array.
// TODO(dmitrys): support spread in the middle of a pattern
// and also for function param patterns: [x, ...xs, y]
components.push(item.argument.name +
'=Array.prototype.slice.call(' +
utils.getTempVar(tmpIndex) + ',' + idx + ')'
);
continue;
}
if (item.type === Syntax.SpreadProperty) {
var restExpression = restPropertyHelpers.renderRestExpression(
utils.getTempVar(tmpIndex),
patternItems
);
components.push(item.argument.name + '=' + restExpression);
continue;
}
// Depending on pattern type (Array or Object), we get
// corresponding pattern item parts.
var accessor = getPatternItemAccessor(node, item, tmpIndex, idx);
var value = getPatternItemValue(node, item);
// TODO(dmitrys): implement default values: {x, y=5}
if (value.type === Syntax.Identifier) {
// Simple pattern item.
components.push(value.name + '=' + accessor);
} else {
// Complex sub-structure.
components.push(
utils.getTempVar(++state.localScope.tempVarIndex) + '=' + accessor +
',' + getDestructuredComponents(value, state)
);
}
}
return components.join(',');
}
function getPatternItems(node) {
return node.properties || node.elements;
}
function getPatternItemAccessor(node, patternItem, tmpIndex, idx) {
var tmpName = utils.getTempVar(tmpIndex);
if (node.type === Syntax.ObjectPattern) {
if (reservedWordsHelper.isReservedWord(patternItem.key.name)) {
return tmpName + '["' + patternItem.key.name + '"]';
} else if (patternItem.key.type === Syntax.Literal) {
return tmpName + '[' + JSON.stringify(patternItem.key.value) + ']';
} else if (patternItem.key.type === Syntax.Identifier) {
return tmpName + '.' + patternItem.key.name;
}
} else if (node.type === Syntax.ArrayPattern) {
return tmpName + '[' + idx + ']';
}
}
function getPatternItemValue(node, patternItem) {
return node.type === Syntax.ObjectPattern
? patternItem.value
: patternItem;
}
// -------------------------------------------------------
// 2. Assignment expression.
//
// [a, b] = [b, a];
// ({x, y} = {y, x});
// -------------------------------------------------------
function visitStructuredAssignment(traverse, node, path, state) {
var exprNode = node.expression;
utils.append('var ' + utils.getTempVar(state.localScope.tempVarIndex) + '=', state);
utils.catchupWhiteSpace(exprNode.right.range[0], state);
traverse(exprNode.right, path, state);
utils.catchup(exprNode.right.range[1], state);
utils.append(
';' + getDestructuredComponents(exprNode.left, state) + ';',
state
);
utils.catchupWhiteSpace(node.range[1], state);
state.localScope.tempVarIndex++;
return false;
}
visitStructuredAssignment.test = function(node, path, state) {
// We consider the expression statement rather than just assignment
// expression to cover case with object patters which should be
// wrapped in grouping operator: ({x, y} = {y, x});
return node.type === Syntax.ExpressionStatement &&
node.expression.type === Syntax.AssignmentExpression &&
isStructuredPattern(node.expression.left);
};
// -------------------------------------------------------
// 3. Structured parameter.
//
// function foo({x, y}) { ... }
// -------------------------------------------------------
function visitStructuredParameter(traverse, node, path, state) {
utils.append(utils.getTempVar(getParamIndex(node, path)), state);
utils.catchupWhiteSpace(node.range[1], state);
return true;
}
function getParamIndex(paramNode, path) {
var funcNode = path[0];
var tmpIndex = 0;
for (var k = 0; k < funcNode.params.length; k++) {
var param = funcNode.params[k];
if (param === paramNode) {
break;
}
if (isStructuredPattern(param)) {
tmpIndex++;
}
}
return tmpIndex;
}
visitStructuredParameter.test = function(node, path, state) {
return isStructuredPattern(node) && isFunctionNode(path[0]);
};
function isFunctionNode(node) {
return (node.type == Syntax.FunctionDeclaration ||
node.type == Syntax.FunctionExpression ||
node.type == Syntax.MethodDefinition ||
node.type == Syntax.ArrowFunctionExpression);
}
// -------------------------------------------------------
// 4. Function body for structured parameters.
//
// function foo({x, y}) { x; y; }
// -------------------------------------------------------
function visitFunctionBodyForStructuredParameter(traverse, node, path, state) {
var funcNode = path[0];
utils.catchup(funcNode.body.range[0] + 1, state);
renderDestructuredComponents(funcNode, state);
if (funcNode.rest) {
utils.append(
restParamVisitors.renderRestParamSetup(funcNode, state),
state
);
}
return true;
}
function renderDestructuredComponents(funcNode, state) {
var destructuredComponents = [];
for (var k = 0; k < funcNode.params.length; k++) {
var param = funcNode.params[k];
if (isStructuredPattern(param)) {
destructuredComponents.push(
getDestructuredComponents(param, state)
);
state.localScope.tempVarIndex++;
}
}
if (destructuredComponents.length) {
utils.append('var ' + destructuredComponents.join(',') + ';', state);
}
}
visitFunctionBodyForStructuredParameter.test = function(node, path, state) {
return node.type === Syntax.BlockStatement && isFunctionNode(path[0]);
};
exports.visitorList = [
visitStructuredVariable,
visitStructuredAssignment,
visitStructuredParameter,
visitFunctionBodyForStructuredParameter
];
exports.renderDestructuredComponents = renderDestructuredComponents;