esrap 2.2.12 → 2.2.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esrap",
3
- "version": "2.2.12",
3
+ "version": "2.2.13",
4
4
  "description": "Parse in reverse",
5
5
  "repository": {
6
6
  "type": "git",
@@ -653,12 +653,25 @@ export default (options = {}) => {
653
653
 
654
654
  if (node.id) {
655
655
  context.visit(node.id);
656
+ }
657
+
658
+ if (node.typeParameters) {
659
+ context.visit(node.typeParameters);
660
+ }
661
+
662
+ if (node.id || node.typeParameters) {
656
663
  context.write(' ');
657
664
  }
658
665
 
659
666
  if (node.superClass) {
660
667
  context.write('extends ');
668
+ // the `extends` clause is a LeftHandSideExpression; anything lower (a
669
+ // logical/binary/conditional/etc.) must be parenthesized
670
+ const wrap_super =
671
+ EXPRESSIONS_PRECEDENCE[node.superClass.type] < EXPRESSIONS_PRECEDENCE.NewExpression;
672
+ if (wrap_super) context.write('(');
661
673
  context.visit(node.superClass);
674
+ if (wrap_super) context.write(')');
662
675
 
663
676
  // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
664
677
  var type_arguments = node.superTypeParameters ?? node.superTypeArguments;
@@ -795,6 +808,12 @@ export default (options = {}) => {
795
808
  context.visit(node.key);
796
809
  if (node.computed) context.write(']');
797
810
 
811
+ // optional method (`m?()`)
812
+ if (node.optional) context.write('?');
813
+
814
+ // @ts-expect-error `typeParameters` lives on the method node, not its value
815
+ if (node.typeParameters) context.visit(node.typeParameters);
816
+
798
817
  context.write('(');
799
818
  sequence(
800
819
  context,
@@ -827,6 +846,8 @@ export default (options = {}) => {
827
846
 
828
847
  const kw = create_keyword_write(context, node, field_modifiers_keywords_map_ok);
829
848
 
849
+ if (node.declare) kw('declare ');
850
+
830
851
  if (node.accessibility) {
831
852
  kw(node.accessibility + ' ');
832
853
  }
@@ -844,6 +865,10 @@ export default (options = {}) => {
844
865
  kw('static ');
845
866
  }
846
867
 
868
+ if (node.override) kw('override ');
869
+
870
+ if (node.readonly) kw('readonly ');
871
+
847
872
  if (
848
873
  // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
849
874
  node.accessor ||
@@ -861,6 +886,10 @@ export default (options = {}) => {
861
886
  context.visit(node.key);
862
887
  }
863
888
 
889
+ // `x?: T` (optional) / `x!: T` (definite assignment)
890
+ if (node.optional) context.write('?');
891
+ else if (node.definite) context.write('!');
892
+
864
893
  if (node.typeAnnotation) {
865
894
  if (node.type === 'AccessorProperty' || node.type === 'TSAbstractAccessorProperty') {
866
895
  context.visit(node.typeAnnotation);
@@ -1101,7 +1130,14 @@ export default (options = {}) => {
1101
1130
 
1102
1131
  Decorator(node, context) {
1103
1132
  context.write('@');
1133
+ // a decorator must be an identifier/member/call (or parenthesized); anything
1134
+ // else (ternary, logical, assignment, unary, `as`, optional chain…) needs wrapping
1135
+ const wrap =
1136
+ /** @type {string} */ (node.expression.type) === 'ChainExpression' ||
1137
+ EXPRESSIONS_PRECEDENCE[node.expression.type] < EXPRESSIONS_PRECEDENCE.CallExpression;
1138
+ if (wrap) context.write('(');
1104
1139
  context.visit(node.expression);
1140
+ if (wrap) context.write(')');
1105
1141
  context.newline();
1106
1142
  },
1107
1143
 
@@ -1211,12 +1247,8 @@ export default (options = {}) => {
1211
1247
  },
1212
1248
 
1213
1249
  ExpressionStatement(node, context) {
1214
- // wrap when a leading `{`/`function` would otherwise be parsed as a block/declaration
1215
- const wrap =
1216
- node.expression.type === 'ObjectExpression' ||
1217
- (node.expression.type === 'AssignmentExpression' &&
1218
- node.expression.left.type === 'ObjectPattern') ||
1219
- node.expression.type === 'FunctionExpression';
1250
+ // would otherwise be parsed as a block / function / class declaration
1251
+ const wrap = leads_with_curly_or_keyword(node.expression);
1220
1252
  maybe_wrap(context, node.expression, wrap);
1221
1253
  context.write(';');
1222
1254
  },
@@ -1253,7 +1285,9 @@ export default (options = {}) => {
1253
1285
  let name = node.name;
1254
1286
  context.write(name, node);
1255
1287
 
1288
+ // optional parameters (`a?: T`) carry `optional` on the identifier
1256
1289
  if (node.optional) context.write('?');
1290
+
1257
1291
  if (node.typeAnnotation) context.visit(node.typeAnnotation);
1258
1292
  },
1259
1293
 
@@ -1594,7 +1628,12 @@ export default (options = {}) => {
1594
1628
  },
1595
1629
 
1596
1630
  TaggedTemplateExpression(node, context) {
1597
- maybe_wrap(context, node.tag, node.tag.type === 'ChainExpression');
1631
+ // the tag is a LeftHandSideExpression; a lower-precedence tag (logical,
1632
+ // conditional, arrow, `as`, unary…) or an optional chain must be wrapped
1633
+ const wrap =
1634
+ /** @type {string} */ (node.tag.type) === 'ChainExpression' ||
1635
+ EXPRESSIONS_PRECEDENCE[node.tag.type] < EXPRESSIONS_PRECEDENCE.CallExpression;
1636
+ maybe_wrap(context, node.tag, wrap);
1598
1637
  context.visit(node.quasi);
1599
1638
  },
1600
1639
 
@@ -1847,7 +1886,9 @@ export default (options = {}) => {
1847
1886
  },
1848
1887
 
1849
1888
  TSPropertySignature(node, context) {
1889
+ if (node.computed) context.write('[');
1850
1890
  context.visit(node.key);
1891
+ if (node.computed) context.write(']');
1851
1892
  if (node.optional) context.write('?');
1852
1893
  if (node.typeAnnotation) context.visit(node.typeAnnotation);
1853
1894
  },
@@ -1939,6 +1980,11 @@ export default (options = {}) => {
1939
1980
  },
1940
1981
 
1941
1982
  TSTypeParameter(node, context) {
1983
+ // modifiers: `const T`, `in T` / `out T` (variance)
1984
+ if (node.const) context.write('const ');
1985
+ if (node.in) context.write('in ');
1986
+ if (node.out) context.write('out ');
1987
+
1942
1988
  if (node.name && node.name.type) context.visit(node.name);
1943
1989
  // @ts-expect-error type mismatch TSESTree and acorn-typescript?
1944
1990
  else context.write(node.name, node);
@@ -1955,19 +2001,14 @@ export default (options = {}) => {
1955
2001
  },
1956
2002
 
1957
2003
  TSTypePredicate(node, context) {
1958
- if (node.parameterName) {
1959
- context.visit(node.parameterName);
1960
- } else if (node.typeAnnotation) {
1961
- context.visit(node.typeAnnotation);
1962
- }
2004
+ // `asserts` precedes the parameter name; `is <type>` follows it. Forms:
2005
+ // `x is T`, `asserts x is T`, `asserts x`
2006
+ if (node.asserts) context.write('asserts ');
1963
2007
 
1964
- if (node.asserts) {
1965
- context.write(' asserts ');
1966
- } else {
1967
- context.write(' is ');
1968
- }
2008
+ if (node.parameterName) context.visit(node.parameterName);
1969
2009
 
1970
2010
  if (node.typeAnnotation) {
2011
+ context.write(' is ');
1971
2012
  context.visit(node.typeAnnotation.typeAnnotation);
1972
2013
  }
1973
2014
  },
@@ -2005,7 +2046,16 @@ export default (options = {}) => {
2005
2046
  },
2006
2047
 
2007
2048
  TSMappedType(node, context) {
2008
- context.write('{[');
2049
+ context.write('{');
2050
+
2051
+ // `readonly` / `+readonly` / `-readonly` modifier
2052
+ if (node.readonly) {
2053
+ context.write(
2054
+ node.readonly === '-' ? '-readonly ' : node.readonly === '+' ? '+readonly ' : 'readonly '
2055
+ );
2056
+ }
2057
+
2058
+ context.write('[');
2009
2059
 
2010
2060
  const legacy_type_parameter = node.typeParameter;
2011
2061
  const key = node.key ?? legacy_type_parameter?.name;
@@ -2022,8 +2072,19 @@ export default (options = {}) => {
2022
2072
  context.visit(constraint);
2023
2073
  }
2024
2074
 
2075
+ // `as` key remapping
2076
+ if (node.nameType) {
2077
+ context.write(' as ');
2078
+ context.visit(node.nameType);
2079
+ }
2080
+
2025
2081
  context.write(']');
2026
2082
 
2083
+ // `?` / `+?` / `-?` optionality modifier
2084
+ if (node.optional) {
2085
+ context.write(node.optional === '-' ? '-?' : node.optional === '+' ? '+?' : '?');
2086
+ }
2087
+
2027
2088
  if (node.typeAnnotation) {
2028
2089
  context.write(': ');
2029
2090
  context.visit(node.typeAnnotation);
@@ -2033,7 +2094,9 @@ export default (options = {}) => {
2033
2094
  },
2034
2095
 
2035
2096
  TSMethodSignature(node, context) {
2097
+ if (node.computed) context.write('[');
2036
2098
  context.visit(node.key);
2099
+ if (node.computed) context.write(']');
2037
2100
 
2038
2101
  context.write('(');
2039
2102
  sequence(
@@ -2191,8 +2254,17 @@ export default (options = {}) => {
2191
2254
  context.visit(node.id);
2192
2255
  }
2193
2256
 
2194
- if (!node.body) return;
2195
- context.visit(node.body);
2257
+ // a qualified name (`namespace A.B.C`) is represented as nested
2258
+ // `TSModuleDeclaration`s whose body is the next name part, not a block
2259
+ let body = /** @type {any} */ (node.body);
2260
+ while (body && body.type === 'TSModuleDeclaration') {
2261
+ context.write('.');
2262
+ context.visit(body.id);
2263
+ body = body.body;
2264
+ }
2265
+
2266
+ if (!body) return;
2267
+ context.visit(body);
2196
2268
  },
2197
2269
 
2198
2270
  TSNonNullExpression(node, context) {
@@ -2320,19 +2392,20 @@ function operand_needs_wrap(node, parent, is_right) {
2320
2392
  return true;
2321
2393
  }
2322
2394
 
2323
- // `**` can't take a unary/await left operand: `-2 ** 2`, `await x ** 2` are syntax errors
2395
+ // `**` can't take a unary/await/assertion left operand: `-2 ** 2`, `await x ** 2`,
2396
+ // `<T>x ** 2` are syntax errors
2324
2397
  const unary_base_of_pow =
2325
2398
  !is_right &&
2326
2399
  parent.operator === '**' &&
2327
- (node.type === 'UnaryExpression' || node.type === 'AwaitExpression');
2400
+ (node.type === 'UnaryExpression' ||
2401
+ node.type === 'AwaitExpression' ||
2402
+ node.type === 'TSTypeAssertion');
2328
2403
  if (unary_base_of_pow) return true;
2329
2404
 
2330
2405
  const precedence = EXPRESSIONS_PRECEDENCE[node.type];
2331
2406
  const parent_precedence = EXPRESSIONS_PRECEDENCE[parent.type];
2332
2407
  if (precedence !== parent_precedence) return precedence < parent_precedence;
2333
-
2334
2408
  const operator = /** @type {TSESTree.BinaryExpression} */ (node).operator;
2335
-
2336
2409
  if (operator === '**' && parent.operator === '**') {
2337
2410
  // exponentiation is right-associative
2338
2411
  return !is_right;
@@ -2375,6 +2448,95 @@ function has_call_expression(node) {
2375
2448
  return false;
2376
2449
  }
2377
2450
 
2451
+ /**
2452
+ * True when printing `node` as an expression statement would begin with `{`,
2453
+ * `function`, or `class` — which the parser would misread as a block, function
2454
+ * declaration, or class declaration. Walks the left spine following the same
2455
+ * parenthesization the visitors apply, so it stops as soon as a child position
2456
+ * would already be wrapped.
2457
+ * @param {TSESTree.Node} node
2458
+ * @returns {boolean}
2459
+ */
2460
+ function leads_with_curly_or_keyword(node) {
2461
+ while (node) {
2462
+ switch (node.type) {
2463
+ case 'ObjectExpression':
2464
+ case 'ObjectPattern':
2465
+ case 'FunctionExpression':
2466
+ case 'ClassExpression':
2467
+ return true;
2468
+
2469
+ case 'BinaryExpression':
2470
+ case 'LogicalExpression':
2471
+ if (operand_needs_wrap(node.left, node, false)) return false;
2472
+ node = node.left;
2473
+ continue;
2474
+
2475
+ case 'AssignmentExpression':
2476
+ node = node.left;
2477
+ continue;
2478
+
2479
+ case 'ConditionalExpression':
2480
+ if (
2481
+ EXPRESSIONS_PRECEDENCE[node.test.type] <= EXPRESSIONS_PRECEDENCE.ConditionalExpression
2482
+ ) {
2483
+ return false;
2484
+ }
2485
+ node = node.test;
2486
+ continue;
2487
+
2488
+ case 'MemberExpression':
2489
+ if (
2490
+ /** @type {string} */ (node.object.type) === 'ChainExpression' ||
2491
+ EXPRESSIONS_PRECEDENCE[node.object.type] < EXPRESSIONS_PRECEDENCE.MemberExpression
2492
+ ) {
2493
+ return false;
2494
+ }
2495
+ node = node.object;
2496
+ continue;
2497
+
2498
+ case 'CallExpression':
2499
+ if (
2500
+ /** @type {string} */ (node.callee.type) === 'ChainExpression' ||
2501
+ EXPRESSIONS_PRECEDENCE[node.callee.type] < EXPRESSIONS_PRECEDENCE.CallExpression
2502
+ ) {
2503
+ return false;
2504
+ }
2505
+ node = node.callee;
2506
+ continue;
2507
+
2508
+ case 'TaggedTemplateExpression':
2509
+ if (
2510
+ /** @type {string} */ (node.tag.type) === 'ChainExpression' ||
2511
+ EXPRESSIONS_PRECEDENCE[node.tag.type] < EXPRESSIONS_PRECEDENCE.CallExpression
2512
+ ) {
2513
+ return false;
2514
+ }
2515
+ node = node.tag;
2516
+ continue;
2517
+
2518
+ case 'UpdateExpression':
2519
+ if (node.prefix) return false;
2520
+ node = node.argument;
2521
+ continue;
2522
+
2523
+ case 'TSAsExpression':
2524
+ case 'TSSatisfiesExpression':
2525
+ case 'TSNonNullExpression':
2526
+ node = node.expression;
2527
+ continue;
2528
+
2529
+ // a sequence expression always prints its own wrapping parens
2530
+ case 'SequenceExpression':
2531
+ return false;
2532
+
2533
+ default:
2534
+ return false;
2535
+ }
2536
+ }
2537
+ return false;
2538
+ }
2539
+
2378
2540
  /**
2379
2541
  * @param {TSESTree.VariableDeclaration} node
2380
2542
  * @param {Context} context