hermes-parser 0.7.0 → 0.8.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.
@@ -89,6 +89,9 @@ class HermesToBabelAdapter extends _HermesASTAdapter.default {
89
89
  case 'ExportNamedDeclaration':
90
90
  return this.mapExportNamedDeclaration(node);
91
91
 
92
+ case 'ExportNamespaceSpecifier':
93
+ return this.mapExportNamespaceSpecifier(node);
94
+
92
95
  case 'ExportAllDeclaration':
93
96
  return this.mapExportAllDeclaration(node);
94
97
 
@@ -102,6 +105,8 @@ class HermesToBabelAdapter extends _HermesASTAdapter.default {
102
105
  return this.mapJSXStringLiteral(node);
103
106
 
104
107
  case 'PrivateName':
108
+ return this.mapPrivateName(node);
109
+
105
110
  case 'ClassPrivateProperty':
106
111
  return this.mapPrivateProperty(node);
107
112
 
@@ -113,6 +118,9 @@ class HermesToBabelAdapter extends _HermesASTAdapter.default {
113
118
  case 'OptionalIndexedAccessType':
114
119
  return this.mapUnsupportedTypeAnnotation(node);
115
120
 
121
+ case 'BigIntLiteral':
122
+ return this.mapBigIntLiteral(node);
123
+
116
124
  default:
117
125
  return this.mapNodeDefault(node);
118
126
  }
@@ -278,13 +286,14 @@ class HermesToBabelAdapter extends _HermesASTAdapter.default {
278
286
  typeParameters,
279
287
  predicate
280
288
  } = value;
281
- return {
289
+ const newNode = {
282
290
  type: 'ObjectMethod',
283
291
  loc: node.loc,
284
292
  start: node.start,
285
293
  end: node.end,
286
294
  // Non getter or setter methods have `kind = method`
287
295
  kind: node.kind === 'init' ? 'method' : node.kind,
296
+ method: node.kind === 'init' ? true : false,
288
297
  computed: node.computed,
289
298
  key,
290
299
  id,
@@ -296,6 +305,13 @@ class HermesToBabelAdapter extends _HermesASTAdapter.default {
296
305
  typeParameters,
297
306
  predicate
298
307
  };
308
+
309
+ if (node.kind !== 'init') {
310
+ // babel emits an empty variance property on accessors for some reason
311
+ newNode.variance = null;
312
+ }
313
+
314
+ return newNode;
299
315
  } else {
300
316
  // Non-method property nodes should be renamed to ObjectProperty
301
317
  node.type = 'ObjectProperty';
@@ -346,7 +362,15 @@ class HermesToBabelAdapter extends _HermesASTAdapter.default {
346
362
 
347
363
  if (annotation != null) {
348
364
  restElement.typeAnnotation = annotation;
349
- restElement.argument.typeAnnotation = null;
365
+ restElement.argument.typeAnnotation = null; // Unfortunately there's no way for us to recover the end location of
366
+ // the argument for the general case
367
+
368
+ if (restElement.argument.type === 'Identifier') {
369
+ restElement.argument.end = restElement.argument.start + restElement.argument.name.length;
370
+ restElement.argument.loc.end = { ...restElement.argument.loc.start,
371
+ column: restElement.argument.loc.start.column + restElement.argument.name.length
372
+ };
373
+ }
350
374
  }
351
375
 
352
376
  return restElement;
@@ -362,9 +386,13 @@ class HermesToBabelAdapter extends _HermesASTAdapter.default {
362
386
  end: node.end,
363
387
  callee: {
364
388
  type: 'Import',
365
- loc: node.loc,
389
+ loc: { ...node.loc,
390
+ end: { ...node.loc.start,
391
+ column: node.loc.start.column + 'import'.length
392
+ }
393
+ },
366
394
  start: node.start,
367
- end: node.end
395
+ end: node.start + 'import'.length
368
396
  },
369
397
  arguments: [this.mapNode(node.source)]
370
398
  };
@@ -407,6 +435,57 @@ class HermesToBabelAdapter extends _HermesASTAdapter.default {
407
435
  };
408
436
  }
409
437
 
438
+ mapBigIntLiteral(node) {
439
+ const bigint = node.bigint.replace(/n$/, '').replace(/_/, '');
440
+ node.value = typeof BigInt === 'function' ? BigInt(bigint) : null;
441
+ return node;
442
+ }
443
+
444
+ mapPrivateProperty(nodeUnprocessed) {
445
+ const node = this.mapNodeDefault(nodeUnprocessed);
446
+ node.key = {
447
+ type: 'PrivateName',
448
+ id: { ...node.key,
449
+ // babel doesn't include the hash in the identifier
450
+ start: node.key.start + 1,
451
+ loc: { ...node.key.loc,
452
+ start: { ...node.key.loc.start,
453
+ column: node.key.loc.start.column + 1
454
+ }
455
+ }
456
+ },
457
+ start: node.key.start,
458
+ end: node.key.end,
459
+ loc: node.key.loc
460
+ };
461
+ return node;
462
+ }
463
+
464
+ mapPrivateName(node) {
465
+ // babel doesn't include the hash in the identifier
466
+ node.id.start += 1;
467
+ node.id.loc.start.column += 1;
468
+ return node;
469
+ }
470
+
471
+ mapExportNamespaceSpecifier(nodeUnprocessed) {
472
+ const node = this.mapNodeDefault(nodeUnprocessed); // the hermes AST emits the location as the location of the entire export
473
+ // but babel emits the location as *just* the "* as id" bit
474
+ // the end will always align with the end of the identifier (ezpz)
475
+ // but the start will align with the "*" token - which we can't recover from just the AST
476
+ // so we just fudge the start location a bit to get it "good enough"
477
+ // it will be wrong if the AST is anything like "export * as x from 'y'"... but oh well
478
+
479
+ node.start = node.start + 'export '.length;
480
+ node.loc.start.column = node.loc.start.column + 'export '.length;
481
+ node.end = node.exported.end;
482
+ node.loc.end = {
483
+ column: node.exported.loc.end.column,
484
+ line: node.exported.loc.end.line
485
+ };
486
+ return node;
487
+ }
488
+
410
489
  }
411
490
 
412
491
  exports.default = HermesToBabelAdapter;
@@ -21,6 +21,8 @@ import type {HermesNode} from './HermesAST';
21
21
 
22
22
  import HermesASTAdapter from './HermesASTAdapter';
23
23
 
24
+ declare var BigInt: ?(value: $FlowFixMe) => mixed;
25
+
24
26
  export default class HermesToBabelAdapter extends HermesASTAdapter {
25
27
  fixSourceLocation(node: HermesNode): void {
26
28
  const loc = node.loc;
@@ -67,6 +69,8 @@ export default class HermesToBabelAdapter extends HermesASTAdapter {
67
69
  return this.mapExportDefaultDeclaration(node);
68
70
  case 'ExportNamedDeclaration':
69
71
  return this.mapExportNamedDeclaration(node);
72
+ case 'ExportNamespaceSpecifier':
73
+ return this.mapExportNamespaceSpecifier(node);
70
74
  case 'ExportAllDeclaration':
71
75
  return this.mapExportAllDeclaration(node);
72
76
  case 'RestElement':
@@ -76,6 +80,7 @@ export default class HermesToBabelAdapter extends HermesASTAdapter {
76
80
  case 'JSXStringLiteral':
77
81
  return this.mapJSXStringLiteral(node);
78
82
  case 'PrivateName':
83
+ return this.mapPrivateName(node);
79
84
  case 'ClassPrivateProperty':
80
85
  return this.mapPrivateProperty(node);
81
86
  case 'FunctionDeclaration':
@@ -84,6 +89,8 @@ export default class HermesToBabelAdapter extends HermesASTAdapter {
84
89
  case 'IndexedAccessType':
85
90
  case 'OptionalIndexedAccessType':
86
91
  return this.mapUnsupportedTypeAnnotation(node);
92
+ case 'BigIntLiteral':
93
+ return this.mapBigIntLiteral(node);
87
94
  default:
88
95
  return this.mapNodeDefault(node);
89
96
  }
@@ -251,13 +258,14 @@ export default class HermesToBabelAdapter extends HermesASTAdapter {
251
258
  predicate,
252
259
  } = value;
253
260
 
254
- return {
261
+ const newNode: HermesNode = {
255
262
  type: 'ObjectMethod',
256
263
  loc: node.loc,
257
264
  start: node.start,
258
265
  end: node.end,
259
266
  // Non getter or setter methods have `kind = method`
260
267
  kind: node.kind === 'init' ? 'method' : node.kind,
268
+ method: node.kind === 'init' ? true : false,
261
269
  computed: node.computed,
262
270
  key,
263
271
  id,
@@ -269,6 +277,11 @@ export default class HermesToBabelAdapter extends HermesASTAdapter {
269
277
  typeParameters,
270
278
  predicate,
271
279
  };
280
+ if (node.kind !== 'init') {
281
+ // babel emits an empty variance property on accessors for some reason
282
+ newNode.variance = null;
283
+ }
284
+ return newNode;
272
285
  } else {
273
286
  // Non-method property nodes should be renamed to ObjectProperty
274
287
  node.type = 'ObjectProperty';
@@ -322,6 +335,18 @@ export default class HermesToBabelAdapter extends HermesASTAdapter {
322
335
  if (annotation != null) {
323
336
  restElement.typeAnnotation = annotation;
324
337
  restElement.argument.typeAnnotation = null;
338
+ // Unfortunately there's no way for us to recover the end location of
339
+ // the argument for the general case
340
+ if (restElement.argument.type === 'Identifier') {
341
+ restElement.argument.end =
342
+ restElement.argument.start + restElement.argument.name.length;
343
+ restElement.argument.loc.end = {
344
+ ...restElement.argument.loc.start,
345
+ column:
346
+ restElement.argument.loc.start.column +
347
+ restElement.argument.name.length,
348
+ };
349
+ }
325
350
  }
326
351
 
327
352
  return restElement;
@@ -337,9 +362,15 @@ export default class HermesToBabelAdapter extends HermesASTAdapter {
337
362
  end: node.end,
338
363
  callee: {
339
364
  type: 'Import',
340
- loc: node.loc,
365
+ loc: {
366
+ ...node.loc,
367
+ end: {
368
+ ...node.loc.start,
369
+ column: node.loc.start.column + 'import'.length,
370
+ },
371
+ },
341
372
  start: node.start,
342
- end: node.end,
373
+ end: node.start + 'import'.length,
343
374
  },
344
375
  arguments: [this.mapNode(node.source)],
345
376
  };
@@ -380,4 +411,62 @@ export default class HermesToBabelAdapter extends HermesASTAdapter {
380
411
  end: node.end,
381
412
  };
382
413
  }
414
+
415
+ mapBigIntLiteral(node: HermesNode): HermesNode {
416
+ const bigint = node.bigint.replace(/n$/, '').replace(/_/, '');
417
+ node.value = typeof BigInt === 'function' ? BigInt(bigint) : null;
418
+ return node;
419
+ }
420
+
421
+ mapPrivateProperty(nodeUnprocessed: HermesNode): HermesNode {
422
+ const node = this.mapNodeDefault(nodeUnprocessed);
423
+ node.key = {
424
+ type: 'PrivateName',
425
+ id: {
426
+ ...node.key,
427
+ // babel doesn't include the hash in the identifier
428
+ start: node.key.start + 1,
429
+ loc: {
430
+ ...node.key.loc,
431
+ start: {
432
+ ...node.key.loc.start,
433
+ column: node.key.loc.start.column + 1,
434
+ },
435
+ },
436
+ },
437
+ start: node.key.start,
438
+ end: node.key.end,
439
+ loc: node.key.loc,
440
+ };
441
+
442
+ return node;
443
+ }
444
+
445
+ mapPrivateName(node: HermesNode): HermesNode {
446
+ // babel doesn't include the hash in the identifier
447
+ node.id.start += 1;
448
+ node.id.loc.start.column += 1;
449
+ return node;
450
+ }
451
+
452
+ mapExportNamespaceSpecifier(nodeUnprocessed: HermesNode): HermesNode {
453
+ const node = this.mapNodeDefault(nodeUnprocessed);
454
+
455
+ // the hermes AST emits the location as the location of the entire export
456
+ // but babel emits the location as *just* the "* as id" bit
457
+
458
+ // the end will always align with the end of the identifier (ezpz)
459
+ // but the start will align with the "*" token - which we can't recover from just the AST
460
+ // so we just fudge the start location a bit to get it "good enough"
461
+ // it will be wrong if the AST is anything like "export * as x from 'y'"... but oh well
462
+ node.start = node.start + 'export '.length;
463
+ node.loc.start.column = node.loc.start.column + 'export '.length;
464
+ node.end = node.exported.end;
465
+ node.loc.end = {
466
+ column: node.exported.loc.end.column,
467
+ line: node.exported.loc.end.line,
468
+ };
469
+
470
+ return node;
471
+ }
383
472
  }
@@ -7,6 +7,8 @@ exports.default = void 0;
7
7
 
8
8
  var _HermesASTAdapter = _interopRequireDefault(require("./HermesASTAdapter"));
9
9
 
10
+ var _getModuleDocblock = require("./getModuleDocblock");
11
+
10
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
13
 
12
14
  /**
@@ -49,6 +51,8 @@ class HermesToESTreeAdapter extends _HermesASTAdapter.default {
49
51
  end: loc.end
50
52
  };
51
53
  node.range = [loc.rangeStart, loc.rangeEnd];
54
+ delete node.start;
55
+ delete node.end;
52
56
  }
53
57
 
54
58
  mapNode(node) {
@@ -100,12 +104,26 @@ class HermesToESTreeAdapter extends _HermesASTAdapter.default {
100
104
  case 'ExportAllDeclaration':
101
105
  return this.mapExportAllDeclaration(node);
102
106
 
107
+ case 'Property':
108
+ return this.mapProperty(node);
109
+
110
+ case 'FunctionDeclaration':
111
+ case 'FunctionExpression':
112
+ case 'ArrowFunctionExpression':
113
+ return this.mapFunction(node);
114
+
103
115
  case 'PrivateName':
116
+ return this.mapPrivateName(node);
117
+
118
+ case 'ClassProperty':
104
119
  case 'ClassPrivateProperty':
105
- return this.mapPrivateProperty(node);
120
+ return this.mapClassProperty(node);
106
121
 
107
- case 'Property':
108
- return this.mapProperty(node);
122
+ case 'MemberExpression':
123
+ case 'OptionalMemberExpression':
124
+ case 'CallExpression':
125
+ case 'OptionalCallExpression':
126
+ return this.mapChainExpression(node);
109
127
 
110
128
  default:
111
129
  return this.mapNodeDefault(node);
@@ -115,6 +133,7 @@ class HermesToESTreeAdapter extends _HermesASTAdapter.default {
115
133
  mapProgram(node) {
116
134
  node = this.mapNodeDefault(node);
117
135
  node.sourceType = this.getSourceType();
136
+ node.docblock = (0, _getModuleDocblock.getModuleDocblock)(node);
118
137
  return node;
119
138
  }
120
139
 
@@ -246,6 +265,189 @@ class HermesToESTreeAdapter extends _HermesASTAdapter.default {
246
265
  return node;
247
266
  }
248
267
 
268
+ mapFunction(nodeUnprocessed) {
269
+ const node = this.mapNodeDefault(nodeUnprocessed);
270
+
271
+ switch (node.type) {
272
+ case 'FunctionDeclaration':
273
+ case 'FunctionExpression':
274
+ node.expression = false;
275
+ return node;
276
+
277
+ case 'ArrowFunctionExpression':
278
+ node.expression = node.body.type !== 'BlockStatement';
279
+ return node;
280
+ }
281
+
282
+ return node;
283
+ }
284
+
285
+ mapChainExpression(nodeUnprocessed) {
286
+ /*
287
+ NOTE - In the below comments `MemberExpression` and `CallExpression`
288
+ are completely interchangable. For terseness we just reference
289
+ one each time.
290
+ */
291
+
292
+ /*
293
+ Hermes uses the old babel-style AST:
294
+ ```
295
+ (one?.two).three?.four;
296
+ ^^^^^^^^^^^^^^^^^^^^^^ OptionalMemberExpression
297
+ ^^^^^^^^^^^^^^^^ MemberExpression
298
+ ^^^^^^^^ OptionalMemberExpression
299
+ ```
300
+ We need to convert it to the ESTree representation:
301
+ ```
302
+ (one?.two).three?.four;
303
+ ^^^^^^^^^^^^^^^^^^^^^^ ChainExpression
304
+ ^^^^^^^^^^^^^^^^^^^^^^ MemberExpression[optional = true]
305
+ ^^^^^^^^^^^^^^^^ MemberExpression[optional = false]
306
+ ^^^^^^^^ ChainExpression
307
+ ^^^^^^^^ MemberExpression[optional = true]
308
+ ```
309
+ We do this by converting the AST and its children (depth first), and then unwrapping
310
+ the resulting AST as appropriate.
311
+ Put another way:
312
+ 1) traverse to the leaf
313
+ 2) if the current node is an `OptionalMemberExpression`:
314
+ a) if the `.object` is a `ChainExpression`:
315
+ i) unwrap the child (`node.object = child.expression`)
316
+ b) convert this node to a `MemberExpression[optional = true]`
317
+ c) wrap this node (`node = ChainExpression[expression = node]`)
318
+ 3) if the current node is a `MembedExpression`:
319
+ a) convert this node to a `MemberExpression[optional = true]`
320
+ */
321
+ const node = this.mapNodeDefault(nodeUnprocessed);
322
+
323
+ const {
324
+ child,
325
+ childKey,
326
+ isOptional
327
+ } = (() => {
328
+ const isOptional = node.optional === true;
329
+
330
+ if (node.type.endsWith('MemberExpression')) {
331
+ return {
332
+ child: node.object,
333
+ childKey: 'object',
334
+ isOptional
335
+ };
336
+ } else if (node.type.endsWith('CallExpression')) {
337
+ return {
338
+ child: node.callee,
339
+ childKey: 'callee',
340
+ isOptional
341
+ };
342
+ } else {
343
+ return {
344
+ child: node.expression,
345
+ childKey: 'expression',
346
+ isOptional: false
347
+ };
348
+ }
349
+ })();
350
+
351
+ const isChildUnwrappable = child.type === 'ChainExpression' && // (x?.y).z is semantically different to `x?.y.z`.
352
+ // In the un-parenthesised case `.z` is only executed if and only if `x?.y` returns a non-nullish value.
353
+ // In the parenthesised case, `.z` is **always** executed, regardless of the return of `x?.y`.
354
+ // As such the AST is different between the two cases.
355
+ //
356
+ // In the hermes AST - any member part of a non-short-circuited optional chain is represented with `OptionalMemberExpression`
357
+ // so if we see a `MemberExpression`, then we know we've hit a parenthesis boundary.
358
+ node.type !== 'MemberExpression' && node.type !== 'CallExpression';
359
+
360
+ if (node.type.startsWith('Optional')) {
361
+ node.type = node.type.replace('Optional', '');
362
+ node.optional = isOptional;
363
+ } else {
364
+ node.optional = false;
365
+ }
366
+
367
+ if (!isChildUnwrappable && !isOptional) {
368
+ return node;
369
+ }
370
+
371
+ if (isChildUnwrappable) {
372
+ const newChild = child.expression;
373
+ node[childKey] = newChild;
374
+ }
375
+
376
+ return {
377
+ type: 'ChainExpression',
378
+ expression: node,
379
+ loc: node.loc,
380
+ range: node.range
381
+ };
382
+ }
383
+
384
+ mapClassProperty(nodeUnprocessed) {
385
+ const node = this.mapNodeDefault(nodeUnprocessed);
386
+
387
+ const key = (() => {
388
+ if (node.type === 'ClassPrivateProperty') {
389
+ const key = this.mapNodeDefault(node.key);
390
+ return {
391
+ type: 'PrivateIdentifier',
392
+ name: key.name,
393
+ range: key.range,
394
+ loc: key.loc
395
+ };
396
+ }
397
+
398
+ return node.key;
399
+ })();
400
+
401
+ return { ...node,
402
+ computed: node.type === 'ClassPrivateProperty' ? false : node.computed,
403
+ key,
404
+ type: 'PropertyDefinition'
405
+ };
406
+ }
407
+
408
+ mapPrivateName(node) {
409
+ return {
410
+ type: 'PrivateIdentifier',
411
+ name: node.id.name,
412
+ // estree the location refers to the entire string including the hash token
413
+ range: node.range,
414
+ loc: node.loc
415
+ };
416
+ }
417
+
418
+ mapExportNamedDeclaration(nodeUnprocessed) {
419
+ const node = super.mapExportNamedDeclaration(nodeUnprocessed);
420
+ const namespaceSpecifier = node.specifiers.find(spec => spec.type === 'ExportNamespaceSpecifier');
421
+
422
+ if (namespaceSpecifier != null) {
423
+ var _node$exportKind;
424
+
425
+ if (node.specifiers.length !== 1) {
426
+ // this should already a hermes parser error - but let's be absolutely sure we're aligned with the spec
427
+ throw new Error('Cannot use an export all with any other specifiers');
428
+ }
429
+
430
+ return {
431
+ type: 'ExportAllDeclaration',
432
+ source: node.source,
433
+ exportKind: (_node$exportKind = node.exportKind) != null ? _node$exportKind : 'value',
434
+ exported: namespaceSpecifier.exported,
435
+ range: node.range,
436
+ loc: node.loc
437
+ };
438
+ }
439
+
440
+ return node;
441
+ }
442
+
443
+ mapExportAllDeclaration(nodeUnprocessed) {
444
+ var _node$exported;
445
+
446
+ const node = super.mapExportAllDeclaration(nodeUnprocessed);
447
+ node.exported = (_node$exported = node.exported) != null ? _node$exported : null;
448
+ return node;
449
+ }
450
+
249
451
  }
250
452
 
251
453
  exports.default = HermesToESTreeAdapter;