Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80540 views
1
/**
2
* Copyright 2014 Facebook, Inc.
3
*
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
7
*
8
* http://www.apache.org/licenses/LICENSE-2.0
9
*
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*/
16
/*global exports:true*/
17
18
/**
19
* Implements ES6 destructuring assignment and pattern matchng.
20
*
21
* function init({port, ip, coords: [x, y]}) {
22
* return (x && y) ? {id, port} : {ip};
23
* };
24
*
25
* function init($__0) {
26
* var
27
* port = $__0.port,
28
* ip = $__0.ip,
29
* $__1 = $__0.coords,
30
* x = $__1[0],
31
* y = $__1[1];
32
* return (x && y) ? {id, port} : {ip};
33
* }
34
*
35
* var x, {ip, port} = init({ip, port});
36
*
37
* var x, $__0 = init({ip, port}), ip = $__0.ip, port = $__0.port;
38
*
39
*/
40
var Syntax = require('esprima-fb').Syntax;
41
var utils = require('../src/utils');
42
43
var reservedWordsHelper = require('./reserved-words-helper');
44
var restParamVisitors = require('./es6-rest-param-visitors');
45
var restPropertyHelpers = require('./es7-rest-property-helpers');
46
47
// -------------------------------------------------------
48
// 1. Structured variable declarations.
49
//
50
// var [a, b] = [b, a];
51
// var {x, y} = {y, x};
52
// -------------------------------------------------------
53
54
function visitStructuredVariable(traverse, node, path, state) {
55
// Allocate new temp for the pattern.
56
utils.append(utils.getTempVar(state.localScope.tempVarIndex) + '=', state);
57
// Skip the pattern and assign the init to the temp.
58
utils.catchupWhiteSpace(node.init.range[0], state);
59
traverse(node.init, path, state);
60
utils.catchup(node.init.range[1], state);
61
// Render the destructured data.
62
utils.append(',' + getDestructuredComponents(node.id, state), state);
63
state.localScope.tempVarIndex++;
64
return false;
65
}
66
67
visitStructuredVariable.test = function(node, path, state) {
68
return node.type === Syntax.VariableDeclarator &&
69
isStructuredPattern(node.id);
70
};
71
72
function isStructuredPattern(node) {
73
return node.type === Syntax.ObjectPattern ||
74
node.type === Syntax.ArrayPattern;
75
}
76
77
// Main function which does actual recursive destructuring
78
// of nested complex structures.
79
function getDestructuredComponents(node, state) {
80
var tmpIndex = state.localScope.tempVarIndex;
81
var components = [];
82
var patternItems = getPatternItems(node);
83
84
for (var idx = 0; idx < patternItems.length; idx++) {
85
var item = patternItems[idx];
86
if (!item) {
87
continue;
88
}
89
90
if (item.type === Syntax.SpreadElement) {
91
// Spread/rest of an array.
92
// TODO(dmitrys): support spread in the middle of a pattern
93
// and also for function param patterns: [x, ...xs, y]
94
components.push(item.argument.name +
95
'=Array.prototype.slice.call(' +
96
utils.getTempVar(tmpIndex) + ',' + idx + ')'
97
);
98
continue;
99
}
100
101
if (item.type === Syntax.SpreadProperty) {
102
var restExpression = restPropertyHelpers.renderRestExpression(
103
utils.getTempVar(tmpIndex),
104
patternItems
105
);
106
components.push(item.argument.name + '=' + restExpression);
107
continue;
108
}
109
110
// Depending on pattern type (Array or Object), we get
111
// corresponding pattern item parts.
112
var accessor = getPatternItemAccessor(node, item, tmpIndex, idx);
113
var value = getPatternItemValue(node, item);
114
115
// TODO(dmitrys): implement default values: {x, y=5}
116
if (value.type === Syntax.Identifier) {
117
// Simple pattern item.
118
components.push(value.name + '=' + accessor);
119
} else {
120
// Complex sub-structure.
121
components.push(
122
utils.getTempVar(++state.localScope.tempVarIndex) + '=' + accessor +
123
',' + getDestructuredComponents(value, state)
124
);
125
}
126
}
127
128
return components.join(',');
129
}
130
131
function getPatternItems(node) {
132
return node.properties || node.elements;
133
}
134
135
function getPatternItemAccessor(node, patternItem, tmpIndex, idx) {
136
var tmpName = utils.getTempVar(tmpIndex);
137
if (node.type === Syntax.ObjectPattern) {
138
if (reservedWordsHelper.isReservedWord(patternItem.key.name)) {
139
return tmpName + '["' + patternItem.key.name + '"]';
140
} else if (patternItem.key.type === Syntax.Literal) {
141
return tmpName + '[' + JSON.stringify(patternItem.key.value) + ']';
142
} else if (patternItem.key.type === Syntax.Identifier) {
143
return tmpName + '.' + patternItem.key.name;
144
}
145
} else if (node.type === Syntax.ArrayPattern) {
146
return tmpName + '[' + idx + ']';
147
}
148
}
149
150
function getPatternItemValue(node, patternItem) {
151
return node.type === Syntax.ObjectPattern
152
? patternItem.value
153
: patternItem;
154
}
155
156
// -------------------------------------------------------
157
// 2. Assignment expression.
158
//
159
// [a, b] = [b, a];
160
// ({x, y} = {y, x});
161
// -------------------------------------------------------
162
163
function visitStructuredAssignment(traverse, node, path, state) {
164
var exprNode = node.expression;
165
utils.append('var ' + utils.getTempVar(state.localScope.tempVarIndex) + '=', state);
166
167
utils.catchupWhiteSpace(exprNode.right.range[0], state);
168
traverse(exprNode.right, path, state);
169
utils.catchup(exprNode.right.range[1], state);
170
171
utils.append(
172
';' + getDestructuredComponents(exprNode.left, state) + ';',
173
state
174
);
175
176
utils.catchupWhiteSpace(node.range[1], state);
177
state.localScope.tempVarIndex++;
178
return false;
179
}
180
181
visitStructuredAssignment.test = function(node, path, state) {
182
// We consider the expression statement rather than just assignment
183
// expression to cover case with object patters which should be
184
// wrapped in grouping operator: ({x, y} = {y, x});
185
return node.type === Syntax.ExpressionStatement &&
186
node.expression.type === Syntax.AssignmentExpression &&
187
isStructuredPattern(node.expression.left);
188
};
189
190
// -------------------------------------------------------
191
// 3. Structured parameter.
192
//
193
// function foo({x, y}) { ... }
194
// -------------------------------------------------------
195
196
function visitStructuredParameter(traverse, node, path, state) {
197
utils.append(utils.getTempVar(getParamIndex(node, path)), state);
198
utils.catchupWhiteSpace(node.range[1], state);
199
return true;
200
}
201
202
function getParamIndex(paramNode, path) {
203
var funcNode = path[0];
204
var tmpIndex = 0;
205
for (var k = 0; k < funcNode.params.length; k++) {
206
var param = funcNode.params[k];
207
if (param === paramNode) {
208
break;
209
}
210
if (isStructuredPattern(param)) {
211
tmpIndex++;
212
}
213
}
214
return tmpIndex;
215
}
216
217
visitStructuredParameter.test = function(node, path, state) {
218
return isStructuredPattern(node) && isFunctionNode(path[0]);
219
};
220
221
function isFunctionNode(node) {
222
return (node.type == Syntax.FunctionDeclaration ||
223
node.type == Syntax.FunctionExpression ||
224
node.type == Syntax.MethodDefinition ||
225
node.type == Syntax.ArrowFunctionExpression);
226
}
227
228
// -------------------------------------------------------
229
// 4. Function body for structured parameters.
230
//
231
// function foo({x, y}) { x; y; }
232
// -------------------------------------------------------
233
234
function visitFunctionBodyForStructuredParameter(traverse, node, path, state) {
235
var funcNode = path[0];
236
237
utils.catchup(funcNode.body.range[0] + 1, state);
238
renderDestructuredComponents(funcNode, state);
239
240
if (funcNode.rest) {
241
utils.append(
242
restParamVisitors.renderRestParamSetup(funcNode, state),
243
state
244
);
245
}
246
247
return true;
248
}
249
250
function renderDestructuredComponents(funcNode, state) {
251
var destructuredComponents = [];
252
253
for (var k = 0; k < funcNode.params.length; k++) {
254
var param = funcNode.params[k];
255
if (isStructuredPattern(param)) {
256
destructuredComponents.push(
257
getDestructuredComponents(param, state)
258
);
259
state.localScope.tempVarIndex++;
260
}
261
}
262
263
if (destructuredComponents.length) {
264
utils.append('var ' + destructuredComponents.join(',') + ';', state);
265
}
266
}
267
268
visitFunctionBodyForStructuredParameter.test = function(node, path, state) {
269
return node.type === Syntax.BlockStatement && isFunctionNode(path[0]);
270
};
271
272
exports.visitorList = [
273
visitStructuredVariable,
274
visitStructuredAssignment,
275
visitStructuredParameter,
276
visitFunctionBodyForStructuredParameter
277
];
278
279
exports.renderDestructuredComponents = renderDestructuredComponents;
280
281
282