hermes-parser 0.28.1 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/HermesASTAdapter.js +1 -2
- package/dist/HermesASTAdapter.js.flow +2 -5
- package/dist/HermesParserNodeDeserializers.js +3 -2
- package/dist/HermesParserWASM.js +1 -1
- package/dist/HermesToESTreeAdapter.js +12 -0
- package/dist/HermesToESTreeAdapter.js.flow +10 -0
- package/dist/babel/TransformESTreeToBabel.js.flow +21 -19
- package/dist/estree/StripComponentSyntax.js.flow +6 -5
- package/dist/estree/TransformMatchSyntax.js +4 -15
- package/dist/estree/TransformMatchSyntax.js.flow +18 -19
- package/dist/generated/ESTreeVisitorKeys.js +1 -1
- package/dist/generated/ParserVisitorKeys.js +1 -1
- package/dist/index.js.flow +1 -1
- package/dist/src/HermesASTAdapter.js +192 -0
- package/dist/src/HermesParser.js +108 -0
- package/dist/src/HermesParserDecodeUTF8String.js +68 -0
- package/dist/src/HermesParserDeserializer.js +243 -0
- package/dist/src/HermesParserNodeDeserializers.js +2473 -0
- package/dist/src/HermesToESTreeAdapter.js +453 -0
- package/dist/src/ParserOptions.js +18 -0
- package/dist/src/babel/TransformESTreeToBabel.js +1104 -0
- package/dist/src/estree/StripComponentSyntax.js +788 -0
- package/dist/src/estree/StripFlowTypes.js +175 -0
- package/dist/src/estree/StripFlowTypesForBabel.js +215 -0
- package/dist/src/estree/TransformMatchSyntax.js +1005 -0
- package/dist/src/generated/ESTreeVisitorKeys.js +220 -0
- package/dist/src/generated/ParserVisitorKeys.js +790 -0
- package/dist/src/getModuleDocblock.js +112 -0
- package/dist/src/index.js +153 -0
- package/dist/src/transform/SimpleTransform.js +120 -0
- package/dist/src/transform/astArrayMutationHelpers.js +62 -0
- package/dist/src/transform/astNodeMutationHelpers.js +195 -0
- package/dist/src/traverse/SimpleTraverser.js +137 -0
- package/dist/src/traverse/getVisitorKeys.js +37 -0
- package/dist/src/utils/Builders.js +191 -0
- package/dist/src/utils/GenID.js +41 -0
- package/dist/src/utils/createSyntaxError.js +25 -0
- package/dist/src/utils/mutateESTreeASTForPrettier.js +127 -0
- package/dist/utils/Builders.js +17 -0
- package/dist/utils/Builders.js.flow +20 -0
- package/package.json +2 -2
|
@@ -0,0 +1,1005 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
'use strict';
|
|
11
|
+
/**
|
|
12
|
+
* Transform match expressions and statements.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
Object.defineProperty(exports, "__esModule", {
|
|
16
|
+
value: true
|
|
17
|
+
});
|
|
18
|
+
exports.transformProgram = transformProgram;
|
|
19
|
+
|
|
20
|
+
var _SimpleTransform = require("../transform/SimpleTransform");
|
|
21
|
+
|
|
22
|
+
var _astNodeMutationHelpers = require("../transform/astNodeMutationHelpers");
|
|
23
|
+
|
|
24
|
+
var _createSyntaxError = require("../utils/createSyntaxError");
|
|
25
|
+
|
|
26
|
+
var _Builders = require("../utils/Builders");
|
|
27
|
+
|
|
28
|
+
var _GenID = require("../utils/GenID");
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generated identifiers.
|
|
32
|
+
* `GenID` is initialized in the transform.
|
|
33
|
+
*/
|
|
34
|
+
let GenID = null;
|
|
35
|
+
|
|
36
|
+
function genIdent() {
|
|
37
|
+
if (GenID == null) {
|
|
38
|
+
throw Error('GenID must be initialized at the start of the transform.');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (0, _Builders.ident)(GenID.genID());
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A series of properties.
|
|
45
|
+
* When combined with the match argument (the root expression), provides the
|
|
46
|
+
* location of to be tested against, or location to be extracted to a binding.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
function objKeyToString(node) {
|
|
51
|
+
switch (node.type) {
|
|
52
|
+
case 'Identifier':
|
|
53
|
+
return node.name;
|
|
54
|
+
|
|
55
|
+
case 'Literal':
|
|
56
|
+
{
|
|
57
|
+
const {
|
|
58
|
+
value
|
|
59
|
+
} = node;
|
|
60
|
+
|
|
61
|
+
if (typeof value === 'number') {
|
|
62
|
+
return String(value);
|
|
63
|
+
} else if (typeof value === 'string') {
|
|
64
|
+
return value;
|
|
65
|
+
} else {
|
|
66
|
+
return node.raw;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function convertMemberPattern(pattern) {
|
|
73
|
+
const {
|
|
74
|
+
base,
|
|
75
|
+
property,
|
|
76
|
+
loc,
|
|
77
|
+
range
|
|
78
|
+
} = pattern;
|
|
79
|
+
const object = base.type === 'MatchIdentifierPattern' ? base.id : convertMemberPattern(base);
|
|
80
|
+
|
|
81
|
+
if (property.type === 'Identifier') {
|
|
82
|
+
return {
|
|
83
|
+
type: 'MemberExpression',
|
|
84
|
+
object,
|
|
85
|
+
property,
|
|
86
|
+
computed: false,
|
|
87
|
+
optional: false,
|
|
88
|
+
...(0, _Builders.etc)({
|
|
89
|
+
loc,
|
|
90
|
+
range
|
|
91
|
+
})
|
|
92
|
+
};
|
|
93
|
+
} else {
|
|
94
|
+
return {
|
|
95
|
+
type: 'MemberExpression',
|
|
96
|
+
object,
|
|
97
|
+
property,
|
|
98
|
+
computed: true,
|
|
99
|
+
optional: false,
|
|
100
|
+
...(0, _Builders.etc)({
|
|
101
|
+
loc,
|
|
102
|
+
range
|
|
103
|
+
})
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function checkDuplicateBindingName(seenBindingNames, node, name) {
|
|
109
|
+
if (seenBindingNames.has(name)) {
|
|
110
|
+
throw (0, _createSyntaxError.createSyntaxError)(node, `Duplicate variable name '${name}' in match case pattern.`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
seenBindingNames.add(name);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function checkBindingKind(node, kind) {
|
|
117
|
+
if (kind === 'var') {
|
|
118
|
+
throw (0, _createSyntaxError.createSyntaxError)(node, `'var' bindings are not allowed. Use 'const' or 'let'.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Does an object property's pattern require a `prop-exists` condition added?
|
|
123
|
+
* If the pattern is a literal like `0`, then it's not required, since the `eq`
|
|
124
|
+
* condition implies the prop exists. However, if we could be doing an equality
|
|
125
|
+
* check against `undefined`, then it is required, since that will be true even
|
|
126
|
+
* if the property doesn't exist.
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
function needsPropExistsCond(pattern) {
|
|
131
|
+
switch (pattern.type) {
|
|
132
|
+
case 'MatchWildcardPattern':
|
|
133
|
+
case 'MatchBindingPattern':
|
|
134
|
+
case 'MatchIdentifierPattern':
|
|
135
|
+
case 'MatchMemberPattern':
|
|
136
|
+
return true;
|
|
137
|
+
|
|
138
|
+
case 'MatchLiteralPattern':
|
|
139
|
+
case 'MatchUnaryPattern':
|
|
140
|
+
case 'MatchObjectPattern':
|
|
141
|
+
case 'MatchArrayPattern':
|
|
142
|
+
return false;
|
|
143
|
+
|
|
144
|
+
case 'MatchAsPattern':
|
|
145
|
+
{
|
|
146
|
+
const {
|
|
147
|
+
pattern: asPattern
|
|
148
|
+
} = pattern;
|
|
149
|
+
return needsPropExistsCond(asPattern);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
case 'MatchOrPattern':
|
|
153
|
+
{
|
|
154
|
+
const {
|
|
155
|
+
patterns
|
|
156
|
+
} = pattern;
|
|
157
|
+
return patterns.some(needsPropExistsCond);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Analyzes a match pattern, and produced both the conditions and bindings
|
|
163
|
+
* produced by that pattern.
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
function analyzePattern(pattern, key, seenBindingNames) {
|
|
168
|
+
switch (pattern.type) {
|
|
169
|
+
case 'MatchWildcardPattern':
|
|
170
|
+
{
|
|
171
|
+
return {
|
|
172
|
+
conditions: [],
|
|
173
|
+
bindings: []
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
case 'MatchLiteralPattern':
|
|
178
|
+
{
|
|
179
|
+
const {
|
|
180
|
+
literal
|
|
181
|
+
} = pattern;
|
|
182
|
+
const condition = {
|
|
183
|
+
type: 'eq',
|
|
184
|
+
key,
|
|
185
|
+
arg: literal
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
conditions: [condition],
|
|
189
|
+
bindings: []
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
case 'MatchUnaryPattern':
|
|
194
|
+
{
|
|
195
|
+
const {
|
|
196
|
+
operator,
|
|
197
|
+
argument,
|
|
198
|
+
loc,
|
|
199
|
+
range
|
|
200
|
+
} = pattern;
|
|
201
|
+
|
|
202
|
+
if (argument.value === 0) {
|
|
203
|
+
// We haven't decided whether we will compare these using `===` or `Object.is`
|
|
204
|
+
throw (0, _createSyntaxError.createSyntaxError)(pattern, `'+0' and '-0' are not yet supported in match unary patterns.`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const arg = {
|
|
208
|
+
type: 'UnaryExpression',
|
|
209
|
+
operator,
|
|
210
|
+
argument,
|
|
211
|
+
prefix: true,
|
|
212
|
+
...(0, _Builders.etc)({
|
|
213
|
+
loc,
|
|
214
|
+
range
|
|
215
|
+
})
|
|
216
|
+
};
|
|
217
|
+
const condition = {
|
|
218
|
+
type: 'eq',
|
|
219
|
+
key,
|
|
220
|
+
arg
|
|
221
|
+
};
|
|
222
|
+
return {
|
|
223
|
+
conditions: [condition],
|
|
224
|
+
bindings: []
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
case 'MatchIdentifierPattern':
|
|
229
|
+
{
|
|
230
|
+
const {
|
|
231
|
+
id
|
|
232
|
+
} = pattern;
|
|
233
|
+
const condition = id.name === 'NaN' ? {
|
|
234
|
+
type: 'is-nan',
|
|
235
|
+
key
|
|
236
|
+
} : {
|
|
237
|
+
type: 'eq',
|
|
238
|
+
key,
|
|
239
|
+
arg: id
|
|
240
|
+
};
|
|
241
|
+
return {
|
|
242
|
+
conditions: [condition],
|
|
243
|
+
bindings: []
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
case 'MatchMemberPattern':
|
|
248
|
+
{
|
|
249
|
+
const arg = convertMemberPattern(pattern);
|
|
250
|
+
const condition = {
|
|
251
|
+
type: 'eq',
|
|
252
|
+
key,
|
|
253
|
+
arg
|
|
254
|
+
};
|
|
255
|
+
return {
|
|
256
|
+
conditions: [condition],
|
|
257
|
+
bindings: []
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
case 'MatchBindingPattern':
|
|
262
|
+
{
|
|
263
|
+
const {
|
|
264
|
+
id,
|
|
265
|
+
kind
|
|
266
|
+
} = pattern;
|
|
267
|
+
checkDuplicateBindingName(seenBindingNames, pattern, id.name);
|
|
268
|
+
checkBindingKind(pattern, kind);
|
|
269
|
+
const binding = {
|
|
270
|
+
type: 'id',
|
|
271
|
+
key,
|
|
272
|
+
kind,
|
|
273
|
+
id
|
|
274
|
+
};
|
|
275
|
+
return {
|
|
276
|
+
conditions: [],
|
|
277
|
+
bindings: [binding]
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
case 'MatchAsPattern':
|
|
282
|
+
{
|
|
283
|
+
const {
|
|
284
|
+
pattern: asPattern,
|
|
285
|
+
target
|
|
286
|
+
} = pattern;
|
|
287
|
+
|
|
288
|
+
if (asPattern.type === 'MatchBindingPattern') {
|
|
289
|
+
throw (0, _createSyntaxError.createSyntaxError)(pattern, `Match 'as' patterns are not allowed directly on binding patterns.`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const {
|
|
293
|
+
conditions,
|
|
294
|
+
bindings
|
|
295
|
+
} = analyzePattern(asPattern, key, seenBindingNames);
|
|
296
|
+
const [id, kind] = target.type === 'MatchBindingPattern' ? [target.id, target.kind] : [target, 'const'];
|
|
297
|
+
checkDuplicateBindingName(seenBindingNames, pattern, id.name);
|
|
298
|
+
checkBindingKind(pattern, kind);
|
|
299
|
+
const binding = {
|
|
300
|
+
type: 'id',
|
|
301
|
+
key,
|
|
302
|
+
kind,
|
|
303
|
+
id
|
|
304
|
+
};
|
|
305
|
+
return {
|
|
306
|
+
conditions,
|
|
307
|
+
bindings: bindings.concat(binding)
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
case 'MatchArrayPattern':
|
|
312
|
+
{
|
|
313
|
+
const {
|
|
314
|
+
elements,
|
|
315
|
+
rest
|
|
316
|
+
} = pattern;
|
|
317
|
+
const lengthOp = rest == null ? 'eq' : 'gte';
|
|
318
|
+
const conditions = [{
|
|
319
|
+
type: 'array',
|
|
320
|
+
key,
|
|
321
|
+
length: elements.length,
|
|
322
|
+
lengthOp
|
|
323
|
+
}];
|
|
324
|
+
const bindings = [];
|
|
325
|
+
elements.forEach((element, i) => {
|
|
326
|
+
const elementKey = key.concat((0, _Builders.numberLiteral)(i));
|
|
327
|
+
const {
|
|
328
|
+
conditions: childConditions,
|
|
329
|
+
bindings: childBindings
|
|
330
|
+
} = analyzePattern(element, elementKey, seenBindingNames);
|
|
331
|
+
conditions.push(...childConditions);
|
|
332
|
+
bindings.push(...childBindings);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
if (rest != null && rest.argument != null) {
|
|
336
|
+
const {
|
|
337
|
+
id,
|
|
338
|
+
kind
|
|
339
|
+
} = rest.argument;
|
|
340
|
+
checkDuplicateBindingName(seenBindingNames, rest.argument, id.name);
|
|
341
|
+
checkBindingKind(pattern, kind);
|
|
342
|
+
bindings.push({
|
|
343
|
+
type: 'array-rest',
|
|
344
|
+
key,
|
|
345
|
+
exclude: elements.length,
|
|
346
|
+
kind,
|
|
347
|
+
id
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
conditions,
|
|
353
|
+
bindings
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
case 'MatchObjectPattern':
|
|
358
|
+
{
|
|
359
|
+
const {
|
|
360
|
+
properties,
|
|
361
|
+
rest
|
|
362
|
+
} = pattern;
|
|
363
|
+
const conditions = [{
|
|
364
|
+
type: 'object',
|
|
365
|
+
key
|
|
366
|
+
}];
|
|
367
|
+
const bindings = [];
|
|
368
|
+
const objKeys = [];
|
|
369
|
+
const seenNames = new Set();
|
|
370
|
+
properties.forEach(prop => {
|
|
371
|
+
const {
|
|
372
|
+
key: objKey,
|
|
373
|
+
pattern: propPattern
|
|
374
|
+
} = prop;
|
|
375
|
+
objKeys.push(objKey);
|
|
376
|
+
const name = objKeyToString(objKey);
|
|
377
|
+
|
|
378
|
+
if (seenNames.has(name)) {
|
|
379
|
+
throw (0, _createSyntaxError.createSyntaxError)(propPattern, `Duplicate property name '${name}' in match object pattern.`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
seenNames.add(name);
|
|
383
|
+
const propKey = key.concat(objKey);
|
|
384
|
+
|
|
385
|
+
if (needsPropExistsCond(propPattern)) {
|
|
386
|
+
conditions.push({
|
|
387
|
+
type: 'prop-exists',
|
|
388
|
+
key,
|
|
389
|
+
propName: name
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const {
|
|
394
|
+
conditions: childConditions,
|
|
395
|
+
bindings: childBindings
|
|
396
|
+
} = analyzePattern(propPattern, propKey, seenBindingNames);
|
|
397
|
+
conditions.push(...childConditions);
|
|
398
|
+
bindings.push(...childBindings);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
if (rest != null && rest.argument != null) {
|
|
402
|
+
const {
|
|
403
|
+
id,
|
|
404
|
+
kind
|
|
405
|
+
} = rest.argument;
|
|
406
|
+
checkDuplicateBindingName(seenBindingNames, rest.argument, id.name);
|
|
407
|
+
checkBindingKind(pattern, kind);
|
|
408
|
+
bindings.push({
|
|
409
|
+
type: 'object-rest',
|
|
410
|
+
key,
|
|
411
|
+
exclude: objKeys,
|
|
412
|
+
kind,
|
|
413
|
+
id
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
conditions,
|
|
419
|
+
bindings
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
case 'MatchOrPattern':
|
|
424
|
+
{
|
|
425
|
+
const {
|
|
426
|
+
patterns
|
|
427
|
+
} = pattern;
|
|
428
|
+
let hasWildcard = false;
|
|
429
|
+
const orConditions = patterns.map(subpattern => {
|
|
430
|
+
const {
|
|
431
|
+
conditions,
|
|
432
|
+
bindings
|
|
433
|
+
} = analyzePattern(subpattern, key, seenBindingNames);
|
|
434
|
+
|
|
435
|
+
if (bindings.length > 0) {
|
|
436
|
+
// We will implement this in the future.
|
|
437
|
+
throw (0, _createSyntaxError.createSyntaxError)(pattern, `Bindings in match 'or' patterns are not yet supported.`);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (conditions.length === 0) {
|
|
441
|
+
hasWildcard = true;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return conditions;
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
if (hasWildcard) {
|
|
448
|
+
return {
|
|
449
|
+
conditions: [],
|
|
450
|
+
bindings: []
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return {
|
|
455
|
+
conditions: [{
|
|
456
|
+
type: 'or',
|
|
457
|
+
orConditions
|
|
458
|
+
}],
|
|
459
|
+
bindings: []
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function expressionOfKey(root, key) {
|
|
466
|
+
return key.reduce((acc, prop) => prop.type === 'Identifier' ? {
|
|
467
|
+
type: 'MemberExpression',
|
|
468
|
+
object: acc,
|
|
469
|
+
property: (0, _astNodeMutationHelpers.shallowCloneNode)(prop),
|
|
470
|
+
computed: false,
|
|
471
|
+
optional: false,
|
|
472
|
+
...(0, _Builders.etc)()
|
|
473
|
+
} : {
|
|
474
|
+
type: 'MemberExpression',
|
|
475
|
+
object: acc,
|
|
476
|
+
property: (0, _astNodeMutationHelpers.shallowCloneNode)(prop),
|
|
477
|
+
computed: true,
|
|
478
|
+
optional: false,
|
|
479
|
+
...(0, _Builders.etc)()
|
|
480
|
+
}, (0, _astNodeMutationHelpers.deepCloneNode)(root));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function testsOfCondition(root, condition) {
|
|
484
|
+
switch (condition.type) {
|
|
485
|
+
case 'eq':
|
|
486
|
+
{
|
|
487
|
+
// <x> === <arg>
|
|
488
|
+
const {
|
|
489
|
+
key,
|
|
490
|
+
arg
|
|
491
|
+
} = condition;
|
|
492
|
+
return [{
|
|
493
|
+
type: 'BinaryExpression',
|
|
494
|
+
left: expressionOfKey(root, key),
|
|
495
|
+
right: arg,
|
|
496
|
+
operator: '===',
|
|
497
|
+
...(0, _Builders.etc)()
|
|
498
|
+
}];
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
case 'is-nan':
|
|
502
|
+
{
|
|
503
|
+
// Number.isNaN(<x>)
|
|
504
|
+
const {
|
|
505
|
+
key
|
|
506
|
+
} = condition;
|
|
507
|
+
const callee = {
|
|
508
|
+
type: 'MemberExpression',
|
|
509
|
+
object: (0, _Builders.ident)('Number'),
|
|
510
|
+
property: (0, _Builders.ident)('isNaN'),
|
|
511
|
+
computed: false,
|
|
512
|
+
optional: false,
|
|
513
|
+
...(0, _Builders.etc)()
|
|
514
|
+
};
|
|
515
|
+
return [(0, _Builders.callExpression)(callee, [expressionOfKey(root, key)])];
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
case 'array':
|
|
519
|
+
{
|
|
520
|
+
// Array.isArray(<x>) && <x>.length === <length>
|
|
521
|
+
const {
|
|
522
|
+
key,
|
|
523
|
+
length,
|
|
524
|
+
lengthOp
|
|
525
|
+
} = condition;
|
|
526
|
+
const operator = lengthOp === 'eq' ? '===' : '>=';
|
|
527
|
+
const isArray = (0, _Builders.callExpression)({
|
|
528
|
+
type: 'MemberExpression',
|
|
529
|
+
object: (0, _Builders.ident)('Array'),
|
|
530
|
+
property: (0, _Builders.ident)('isArray'),
|
|
531
|
+
computed: false,
|
|
532
|
+
optional: false,
|
|
533
|
+
...(0, _Builders.etc)()
|
|
534
|
+
}, [expressionOfKey(root, key)]);
|
|
535
|
+
const lengthCheck = {
|
|
536
|
+
type: 'BinaryExpression',
|
|
537
|
+
left: {
|
|
538
|
+
type: 'MemberExpression',
|
|
539
|
+
object: expressionOfKey(root, key),
|
|
540
|
+
property: (0, _Builders.ident)('length'),
|
|
541
|
+
computed: false,
|
|
542
|
+
optional: false,
|
|
543
|
+
...(0, _Builders.etc)()
|
|
544
|
+
},
|
|
545
|
+
right: (0, _Builders.numberLiteral)(length),
|
|
546
|
+
operator,
|
|
547
|
+
...(0, _Builders.etc)()
|
|
548
|
+
};
|
|
549
|
+
return [isArray, lengthCheck];
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
case 'object':
|
|
553
|
+
{
|
|
554
|
+
// (typeof <x> === 'object' && <x> !== null) || typeof <x> === 'function'
|
|
555
|
+
const {
|
|
556
|
+
key
|
|
557
|
+
} = condition;
|
|
558
|
+
const typeofObject = (0, _Builders.typeofExpression)(expressionOfKey(root, key), 'object');
|
|
559
|
+
const typeofFunction = (0, _Builders.typeofExpression)(expressionOfKey(root, key), 'function');
|
|
560
|
+
const notNull = {
|
|
561
|
+
type: 'BinaryExpression',
|
|
562
|
+
left: expressionOfKey(root, key),
|
|
563
|
+
right: (0, _Builders.nullLiteral)(),
|
|
564
|
+
operator: '!==',
|
|
565
|
+
...(0, _Builders.etc)()
|
|
566
|
+
};
|
|
567
|
+
return [(0, _Builders.disjunction)([(0, _Builders.conjunction)([typeofObject, notNull]), typeofFunction])];
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
case 'prop-exists':
|
|
571
|
+
{
|
|
572
|
+
// <propName> in <x>
|
|
573
|
+
const {
|
|
574
|
+
key,
|
|
575
|
+
propName
|
|
576
|
+
} = condition;
|
|
577
|
+
const inObject = {
|
|
578
|
+
type: 'BinaryExpression',
|
|
579
|
+
left: (0, _Builders.stringLiteral)(propName),
|
|
580
|
+
right: expressionOfKey(root, key),
|
|
581
|
+
operator: 'in',
|
|
582
|
+
...(0, _Builders.etc)()
|
|
583
|
+
};
|
|
584
|
+
return [inObject];
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
case 'or':
|
|
588
|
+
{
|
|
589
|
+
// <a> || <b> || ...
|
|
590
|
+
const {
|
|
591
|
+
orConditions
|
|
592
|
+
} = condition;
|
|
593
|
+
const tests = orConditions.map(conditions => (0, _Builders.conjunction)(testsOfConditions(root, conditions)));
|
|
594
|
+
return [(0, _Builders.disjunction)(tests)];
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function testsOfConditions(root, conditions) {
|
|
600
|
+
return conditions.flatMap(condition => testsOfCondition(root, condition));
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function statementsOfBindings(root, bindings) {
|
|
604
|
+
return bindings.map(binding => {
|
|
605
|
+
switch (binding.type) {
|
|
606
|
+
case 'id':
|
|
607
|
+
{
|
|
608
|
+
// const <id> = <x>;
|
|
609
|
+
const {
|
|
610
|
+
key,
|
|
611
|
+
kind,
|
|
612
|
+
id
|
|
613
|
+
} = binding;
|
|
614
|
+
return (0, _Builders.variableDeclaration)(kind, id, expressionOfKey(root, key));
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
case 'array-rest':
|
|
618
|
+
{
|
|
619
|
+
// const <id> = <x>.slice(<exclude>);
|
|
620
|
+
const {
|
|
621
|
+
key,
|
|
622
|
+
kind,
|
|
623
|
+
id,
|
|
624
|
+
exclude
|
|
625
|
+
} = binding;
|
|
626
|
+
const init = (0, _Builders.callExpression)({
|
|
627
|
+
type: 'MemberExpression',
|
|
628
|
+
object: expressionOfKey(root, key),
|
|
629
|
+
property: (0, _Builders.ident)('slice'),
|
|
630
|
+
computed: false,
|
|
631
|
+
optional: false,
|
|
632
|
+
...(0, _Builders.etc)()
|
|
633
|
+
}, [(0, _Builders.numberLiteral)(exclude)]);
|
|
634
|
+
return (0, _Builders.variableDeclaration)(kind, id, init);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
case 'object-rest':
|
|
638
|
+
{
|
|
639
|
+
// const {a: _, b: _, ...<id>} = <x>;
|
|
640
|
+
const {
|
|
641
|
+
key,
|
|
642
|
+
kind,
|
|
643
|
+
id,
|
|
644
|
+
exclude
|
|
645
|
+
} = binding;
|
|
646
|
+
const destructuring = {
|
|
647
|
+
type: 'ObjectPattern',
|
|
648
|
+
properties: exclude.map(prop => prop.type === 'Identifier' ? {
|
|
649
|
+
type: 'Property',
|
|
650
|
+
key: (0, _astNodeMutationHelpers.shallowCloneNode)(prop),
|
|
651
|
+
value: genIdent(),
|
|
652
|
+
kind: 'init',
|
|
653
|
+
computed: false,
|
|
654
|
+
method: false,
|
|
655
|
+
shorthand: false,
|
|
656
|
+
...(0, _Builders.etc)(),
|
|
657
|
+
parent: _Builders.EMPTY_PARENT
|
|
658
|
+
} : {
|
|
659
|
+
type: 'Property',
|
|
660
|
+
key: (0, _astNodeMutationHelpers.shallowCloneNode)(prop),
|
|
661
|
+
value: genIdent(),
|
|
662
|
+
kind: 'init',
|
|
663
|
+
computed: true,
|
|
664
|
+
method: false,
|
|
665
|
+
shorthand: false,
|
|
666
|
+
...(0, _Builders.etc)(),
|
|
667
|
+
parent: _Builders.EMPTY_PARENT
|
|
668
|
+
}).concat({
|
|
669
|
+
type: 'RestElement',
|
|
670
|
+
argument: id,
|
|
671
|
+
...(0, _Builders.etc)()
|
|
672
|
+
}),
|
|
673
|
+
typeAnnotation: null,
|
|
674
|
+
...(0, _Builders.etc)()
|
|
675
|
+
};
|
|
676
|
+
return (0, _Builders.variableDeclaration)(kind, destructuring, expressionOfKey(root, key));
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* For throwing an error if no cases are matched.
|
|
683
|
+
*/
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
const fallthroughErrorMsgText = `Match: No case succesfully matched. Make exhaustive or add a wildcard case using '_'.`;
|
|
687
|
+
|
|
688
|
+
function fallthroughErrorMsg(value) {
|
|
689
|
+
return {
|
|
690
|
+
type: 'BinaryExpression',
|
|
691
|
+
operator: '+',
|
|
692
|
+
left: (0, _Builders.stringLiteral)(`${fallthroughErrorMsgText} Argument: `),
|
|
693
|
+
right: value,
|
|
694
|
+
...(0, _Builders.etc)()
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function fallthroughError(value) {
|
|
699
|
+
return (0, _Builders.throwStatement)(fallthroughErrorMsg(value));
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* If the argument has no side-effects (ignoring getters). Either an identifier
|
|
703
|
+
* or member expression with identifier root and non-computed/literal properties.
|
|
704
|
+
*/
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
function calculateSimpleArgument(node) {
|
|
708
|
+
switch (node.type) {
|
|
709
|
+
case 'Identifier':
|
|
710
|
+
case 'Super':
|
|
711
|
+
return true;
|
|
712
|
+
|
|
713
|
+
case 'MemberExpression':
|
|
714
|
+
{
|
|
715
|
+
const {
|
|
716
|
+
object,
|
|
717
|
+
property,
|
|
718
|
+
computed
|
|
719
|
+
} = node;
|
|
720
|
+
|
|
721
|
+
if (computed && property.type !== 'Literal') {
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return calculateSimpleArgument(object);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
default:
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Analyze the match cases and return information we will use to build the result.
|
|
734
|
+
*/
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
function analyzeCases(cases) {
|
|
738
|
+
let hasBindings = false;
|
|
739
|
+
let hasWildcard = false;
|
|
740
|
+
const analyses = [];
|
|
741
|
+
|
|
742
|
+
for (let i = 0; i < cases.length; i++) {
|
|
743
|
+
const {
|
|
744
|
+
pattern,
|
|
745
|
+
guard,
|
|
746
|
+
body
|
|
747
|
+
} = cases[i];
|
|
748
|
+
const {
|
|
749
|
+
conditions,
|
|
750
|
+
bindings
|
|
751
|
+
} = analyzePattern(pattern, [], new Set());
|
|
752
|
+
hasBindings = hasBindings || bindings.length > 0;
|
|
753
|
+
analyses.push({
|
|
754
|
+
conditions,
|
|
755
|
+
bindings,
|
|
756
|
+
guard,
|
|
757
|
+
body
|
|
758
|
+
}); // This case catches everything, no reason to continue.
|
|
759
|
+
|
|
760
|
+
if (conditions.length === 0 && guard == null) {
|
|
761
|
+
hasWildcard = true;
|
|
762
|
+
break;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return {
|
|
767
|
+
hasBindings,
|
|
768
|
+
hasWildcard,
|
|
769
|
+
analyses
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Match expression transform entry point.
|
|
774
|
+
*/
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
function mapMatchExpression(node) {
|
|
778
|
+
const {
|
|
779
|
+
argument,
|
|
780
|
+
cases
|
|
781
|
+
} = node;
|
|
782
|
+
const {
|
|
783
|
+
hasBindings,
|
|
784
|
+
hasWildcard,
|
|
785
|
+
analyses
|
|
786
|
+
} = analyzeCases(cases);
|
|
787
|
+
const isSimpleArgument = calculateSimpleArgument(argument);
|
|
788
|
+
const genRoot = !isSimpleArgument ? genIdent() : null;
|
|
789
|
+
const root = genRoot == null ? argument : genRoot; // No bindings and a simple argument means we can use nested conditional
|
|
790
|
+
// expressions.
|
|
791
|
+
|
|
792
|
+
if (!hasBindings && isSimpleArgument) {
|
|
793
|
+
const wildcardAnalaysis = hasWildcard ? analyses.pop() : null;
|
|
794
|
+
const lastBody = wildcardAnalaysis != null ? wildcardAnalaysis.body : (0, _Builders.iife)([fallthroughError((0, _astNodeMutationHelpers.shallowCloneNode)(root))]);
|
|
795
|
+
return analyses.reverse().reduce((acc, analysis) => {
|
|
796
|
+
const {
|
|
797
|
+
conditions,
|
|
798
|
+
guard,
|
|
799
|
+
body
|
|
800
|
+
} = analysis;
|
|
801
|
+
const tests = testsOfConditions(root, conditions);
|
|
802
|
+
|
|
803
|
+
if (guard != null) {
|
|
804
|
+
tests.push(guard);
|
|
805
|
+
} // <tests> ? <body> : <acc>
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
return {
|
|
809
|
+
type: 'ConditionalExpression',
|
|
810
|
+
test: (0, _Builders.conjunction)(tests),
|
|
811
|
+
consequent: body,
|
|
812
|
+
alternate: acc,
|
|
813
|
+
...(0, _Builders.etc)()
|
|
814
|
+
};
|
|
815
|
+
}, lastBody);
|
|
816
|
+
} // There are bindings, so we produce an immediately invoked arrow expression.
|
|
817
|
+
// If the original argument is simple, no need for a new variable.
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
const statements = analyses.map(({
|
|
821
|
+
conditions,
|
|
822
|
+
bindings,
|
|
823
|
+
guard,
|
|
824
|
+
body
|
|
825
|
+
}) => {
|
|
826
|
+
const returnNode = {
|
|
827
|
+
type: 'ReturnStatement',
|
|
828
|
+
argument: body,
|
|
829
|
+
...(0, _Builders.etc)()
|
|
830
|
+
}; // If we have a guard, then we use a nested if statement
|
|
831
|
+
// `if (<guard>) return <body>`
|
|
832
|
+
|
|
833
|
+
const bodyNode = guard == null ? returnNode : {
|
|
834
|
+
type: 'IfStatement',
|
|
835
|
+
test: guard,
|
|
836
|
+
consequent: returnNode,
|
|
837
|
+
...(0, _Builders.etc)()
|
|
838
|
+
};
|
|
839
|
+
const bindingNodes = statementsOfBindings(root, bindings);
|
|
840
|
+
const caseBody = bindingNodes.concat(bodyNode);
|
|
841
|
+
|
|
842
|
+
if (conditions.length > 0) {
|
|
843
|
+
const tests = testsOfConditions(root, conditions);
|
|
844
|
+
return {
|
|
845
|
+
type: 'IfStatement',
|
|
846
|
+
test: (0, _Builders.conjunction)(tests),
|
|
847
|
+
consequent: {
|
|
848
|
+
type: 'BlockStatement',
|
|
849
|
+
body: caseBody,
|
|
850
|
+
...(0, _Builders.etc)()
|
|
851
|
+
},
|
|
852
|
+
...(0, _Builders.etc)()
|
|
853
|
+
};
|
|
854
|
+
} else {
|
|
855
|
+
// No conditions, so no if statement
|
|
856
|
+
if (bindingNodes.length > 0) {
|
|
857
|
+
// Bindings require a block to introduce a new scope
|
|
858
|
+
return {
|
|
859
|
+
type: 'BlockStatement',
|
|
860
|
+
body: caseBody,
|
|
861
|
+
...(0, _Builders.etc)()
|
|
862
|
+
};
|
|
863
|
+
} else {
|
|
864
|
+
return bodyNode;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
if (!hasWildcard) {
|
|
870
|
+
statements.push(fallthroughError((0, _astNodeMutationHelpers.shallowCloneNode)(root)));
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const [params, args] = genRoot == null ? [[], []] : [[genRoot], [argument]]; // `((<params>) => { ... })(<args>)`, or
|
|
874
|
+
// `(() => { ... })()` if is simple argument.
|
|
875
|
+
|
|
876
|
+
return (0, _Builders.iife)(statements, params, args);
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Match statement transform entry point.
|
|
880
|
+
*/
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
function mapMatchStatement(node) {
|
|
884
|
+
const {
|
|
885
|
+
argument,
|
|
886
|
+
cases
|
|
887
|
+
} = node;
|
|
888
|
+
const {
|
|
889
|
+
hasWildcard,
|
|
890
|
+
analyses
|
|
891
|
+
} = analyzeCases(cases);
|
|
892
|
+
const topLabel = genIdent();
|
|
893
|
+
const isSimpleArgument = calculateSimpleArgument(argument);
|
|
894
|
+
const genRoot = !isSimpleArgument ? genIdent() : null;
|
|
895
|
+
const root = genRoot == null ? argument : genRoot;
|
|
896
|
+
const statements = [];
|
|
897
|
+
|
|
898
|
+
if (genRoot != null) {
|
|
899
|
+
statements.push((0, _Builders.variableDeclaration)('const', genRoot, argument));
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
analyses.forEach(({
|
|
903
|
+
conditions,
|
|
904
|
+
bindings,
|
|
905
|
+
guard,
|
|
906
|
+
body
|
|
907
|
+
}) => {
|
|
908
|
+
const breakNode = {
|
|
909
|
+
type: 'BreakStatement',
|
|
910
|
+
label: (0, _astNodeMutationHelpers.shallowCloneNode)(topLabel),
|
|
911
|
+
...(0, _Builders.etc)()
|
|
912
|
+
};
|
|
913
|
+
const bodyStatements = body.body.concat(breakNode); // If we have a guard, then we use a nested if statement
|
|
914
|
+
// `if (<guard>) return <body>`
|
|
915
|
+
|
|
916
|
+
const guardedBodyStatements = guard == null ? bodyStatements : [{
|
|
917
|
+
type: 'IfStatement',
|
|
918
|
+
test: guard,
|
|
919
|
+
consequent: {
|
|
920
|
+
type: 'BlockStatement',
|
|
921
|
+
body: bodyStatements,
|
|
922
|
+
...(0, _Builders.etc)()
|
|
923
|
+
},
|
|
924
|
+
...(0, _Builders.etc)()
|
|
925
|
+
}];
|
|
926
|
+
const bindingNodes = statementsOfBindings(root, bindings);
|
|
927
|
+
const caseBody = bindingNodes.concat(guardedBodyStatements);
|
|
928
|
+
|
|
929
|
+
if (conditions.length > 0) {
|
|
930
|
+
const tests = testsOfConditions(root, conditions);
|
|
931
|
+
statements.push({
|
|
932
|
+
type: 'IfStatement',
|
|
933
|
+
test: (0, _Builders.conjunction)(tests),
|
|
934
|
+
consequent: {
|
|
935
|
+
type: 'BlockStatement',
|
|
936
|
+
body: caseBody,
|
|
937
|
+
...(0, _Builders.etc)()
|
|
938
|
+
},
|
|
939
|
+
...(0, _Builders.etc)()
|
|
940
|
+
});
|
|
941
|
+
} else {
|
|
942
|
+
// No conditions, so no if statement
|
|
943
|
+
statements.push({
|
|
944
|
+
type: 'BlockStatement',
|
|
945
|
+
body: caseBody,
|
|
946
|
+
...(0, _Builders.etc)()
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
if (!hasWildcard) {
|
|
952
|
+
statements.push(fallthroughError((0, _astNodeMutationHelpers.shallowCloneNode)(root)));
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
return {
|
|
956
|
+
type: 'LabeledStatement',
|
|
957
|
+
label: topLabel,
|
|
958
|
+
body: {
|
|
959
|
+
type: 'BlockStatement',
|
|
960
|
+
body: statements,
|
|
961
|
+
...(0, _Builders.etc)()
|
|
962
|
+
},
|
|
963
|
+
...(0, _Builders.etc)()
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
function transformProgram(program, _options) {
|
|
968
|
+
// Initialize so each file transformed starts freshly incrementing the
|
|
969
|
+
// variable name counter, and has its own usage tracking.
|
|
970
|
+
GenID = (0, _GenID.createGenID)('m');
|
|
971
|
+
return _SimpleTransform.SimpleTransform.transformProgram(program, {
|
|
972
|
+
transform(node) {
|
|
973
|
+
switch (node.type) {
|
|
974
|
+
case 'MatchExpression':
|
|
975
|
+
{
|
|
976
|
+
return mapMatchExpression(node);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
case 'MatchStatement':
|
|
980
|
+
{
|
|
981
|
+
return mapMatchStatement(node);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
case 'Identifier':
|
|
985
|
+
{
|
|
986
|
+
// A rudimentary check to avoid some collisions with our generated
|
|
987
|
+
// variable names. Ideally, we would have access a scope analyzer
|
|
988
|
+
// inside the transform instead.
|
|
989
|
+
if (GenID == null) {
|
|
990
|
+
throw Error('GenID must be initialized at the start of the transform.');
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
GenID.addUsage(node.name);
|
|
994
|
+
return node;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
default:
|
|
998
|
+
{
|
|
999
|
+
return node;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
});
|
|
1005
|
+
}
|