esrap 2.0.1 → 2.1.1

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/README.md CHANGED
@@ -107,6 +107,7 @@ The `context` API has several methods:
107
107
  - `context.write(data: string, node?: BaseNode)` — add a string. If `node` is provided and has a standard `loc` property (with `start` and `end` properties each with a `line` and `column`), a sourcemap mapping will be created
108
108
  - `context.indent()` — increase the indentation level, typically before adding a newline
109
109
  - `context.newline()` — self-explanatory
110
+ - `context.space()` — adds a space character, if it doesn't immediately follow a newline
110
111
  - `context.margin()` — causes the next newline to be repeated (consecutive newlines are otherwise merged into one)
111
112
  - `context.dedent()` — decrease the indentation level (again, typically before adding a newline)
112
113
  - `context.visit(node: BaseNode)` — calls the visitor corresponding to `node.type`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esrap",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "Parse in reverse",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,6 +33,7 @@
33
33
  "@vitest/ui": "^2.1.1",
34
34
  "acorn": "^8.15.0",
35
35
  "dts-buddy": "^0.6.2",
36
+ "oxc-parser": "^0.95.0",
36
37
  "prettier": "^3.0.3",
37
38
  "typescript": "^5.7.2",
38
39
  "vitest": "^2.1.1",
@@ -51,6 +52,8 @@
51
52
  "check": "tsc",
52
53
  "sandbox": "node test/sandbox/index.js",
53
54
  "test": "vitest --run",
54
- "test:ui": "vitest --ui"
55
+ "test:ui": "vitest --ui",
56
+ "format": "pnpm lint --write",
57
+ "lint": "prettier --check . --ignore-path .gitignore --ignore-path .prettierignore"
55
58
  }
56
59
  }
package/src/context.js CHANGED
@@ -5,10 +5,12 @@ export const margin = 0;
5
5
  export const newline = 1;
6
6
  export const indent = 2;
7
7
  export const dedent = 3;
8
+ export const space = 4;
8
9
 
9
10
  export class Context {
10
11
  #visitors;
11
12
  #commands;
13
+ #has_newline = false;
12
14
 
13
15
  multiline = false;
14
16
 
@@ -35,15 +37,23 @@ export class Context {
35
37
  }
36
38
 
37
39
  newline() {
38
- this.multiline = true;
40
+ this.#has_newline = true;
39
41
  this.#commands.push(newline);
40
42
  }
41
43
 
44
+ space() {
45
+ this.#commands.push(space);
46
+ }
47
+
42
48
  /**
43
49
  * @param {Context} context
44
50
  */
45
51
  append(context) {
46
52
  this.#commands.push(context.#commands);
53
+
54
+ if (this.#has_newline) {
55
+ this.multiline = true;
56
+ }
47
57
  }
48
58
 
49
59
  /**
@@ -59,6 +69,10 @@ export class Context {
59
69
  } else {
60
70
  this.#commands.push(content);
61
71
  }
72
+
73
+ if (this.#has_newline) {
74
+ this.multiline = true;
75
+ }
62
76
  }
63
77
 
64
78
  /**
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /** @import { BaseNode, Command, Visitors, PrintOptions } from './types' */
2
2
  import { encode } from '@jridgewell/sourcemap-codec';
3
- import { Context, dedent, indent, margin, newline } from './context.js';
3
+ import { Context, dedent, indent, margin, newline, space } from './context.js';
4
4
 
5
5
  /** @type {(str: string) => string} str */
6
6
  let btoa = () => {
@@ -91,6 +91,7 @@ export function print(node, visitors, opts = {}) {
91
91
 
92
92
  let needs_newline = false;
93
93
  let needs_margin = false;
94
+ let needs_space = false;
94
95
 
95
96
  /** @param {Command} command */
96
97
  function run(command) {
@@ -106,6 +107,8 @@ export function print(node, visitors, opts = {}) {
106
107
  needs_newline = true;
107
108
  } else if (command === margin) {
108
109
  needs_margin = true;
110
+ } else if (command === space) {
111
+ needs_space = true;
109
112
  } else if (command === indent) {
110
113
  current_newline += indent_str;
111
114
  } else if (command === dedent) {
@@ -117,9 +120,11 @@ export function print(node, visitors, opts = {}) {
117
120
 
118
121
  if (needs_newline) {
119
122
  append(needs_margin ? '\n' + current_newline : current_newline);
123
+ } else if (needs_space) {
124
+ append(' ');
120
125
  }
121
126
 
122
- needs_margin = needs_newline = false;
127
+ needs_margin = needs_newline = needs_space = false;
123
128
 
124
129
  if (typeof command === 'string') {
125
130
  append(command);
@@ -139,7 +139,6 @@ export default (options = {}) => {
139
139
  * @param {Context} context
140
140
  * @param {{ line: number, column: number } | null} prev
141
141
  * @param {{ line: number, column: number } | null} next
142
- * @returns {boolean} true if final comment is a line comment
143
142
  */
144
143
  function flush_trailing_comments(context, prev, next) {
145
144
  while (comment_index < comments.length) {
@@ -157,14 +156,14 @@ export default (options = {}) => {
157
156
  comment_index += 1;
158
157
 
159
158
  if (comment.type === 'Line') {
160
- return true;
159
+ context.newline();
160
+ } else {
161
+ continue;
161
162
  }
162
- } else {
163
- break;
164
163
  }
165
- }
166
164
 
167
- return false;
165
+ break;
166
+ }
168
167
  }
169
168
 
170
169
  /**
@@ -226,9 +225,7 @@ export default (options = {}) => {
226
225
  }
227
226
 
228
227
  const next = i === nodes.length - 1 ? until : nodes[i + 1]?.loc?.start || null;
229
- if (child && flush_trailing_comments(child_context, child.loc?.end || null, next)) {
230
- multiline = true;
231
- }
228
+ flush_trailing_comments(child_context, child?.loc?.end || null, next);
232
229
 
233
230
  length += child_context.measure() + 1;
234
231
  multiline ||= child_context.multiline;
@@ -293,7 +290,8 @@ export default (options = {}) => {
293
290
  let prev_type = null;
294
291
  let prev_multiline = false;
295
292
 
296
- for (const child of node.body) {
293
+ for (let i = 0; i < node.body.length; i += 1) {
294
+ const child = node.body[i];
297
295
  if (child.type === 'EmptyStatement') continue;
298
296
 
299
297
  const child_context = context.new();
@@ -309,6 +307,12 @@ export default (options = {}) => {
309
307
 
310
308
  context.append(child_context);
311
309
 
310
+ flush_trailing_comments(
311
+ context,
312
+ child.loc?.end || null,
313
+ node.body[i + 1]?.loc?.end ?? node.loc?.end ?? null
314
+ );
315
+
312
316
  prev_type = child.type;
313
317
  prev_multiline = child_context.multiline;
314
318
  }
@@ -472,9 +476,7 @@ export default (options = {}) => {
472
476
  ? (node.loc?.end ?? null)
473
477
  : (node.arguments[i + 1]?.loc?.start ?? null);
474
478
 
475
- if (flush_trailing_comments(context, arg.loc?.end ?? null, next)) {
476
- child_context.multiline = true;
477
- }
479
+ flush_trailing_comments(context, arg.loc?.end ?? null, next);
478
480
 
479
481
  if (!is_last) context.append(join);
480
482
  }
@@ -503,6 +505,8 @@ export default (options = {}) => {
503
505
  context.write('declare ');
504
506
  }
505
507
 
508
+ if (node.abstract) context.write('abstract ');
509
+
506
510
  context.write('class ');
507
511
 
508
512
  if (node.id) {
@@ -569,6 +573,130 @@ export default (options = {}) => {
569
573
  context.visit(node.body);
570
574
  },
571
575
 
576
+ /**
577
+ * @param {TSESTree.MethodDefinition | TSESTree.TSAbstractMethodDefinition} node
578
+ * @param {Context} context
579
+ */
580
+ 'MethodDefinition|TSAbstractMethodDefinition': (node, context) => {
581
+ if (node.decorators) {
582
+ for (const decorator of node.decorators) {
583
+ context.visit(decorator);
584
+ }
585
+ }
586
+
587
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
588
+ if (node.abstract || node.type === 'TSAbstractMethodDefinition') {
589
+ context.write('abstract ');
590
+ }
591
+
592
+ if (node.static) {
593
+ context.write('static ');
594
+ }
595
+
596
+ if (node.kind === 'get' || node.kind === 'set') {
597
+ // Getter or setter
598
+ context.write(node.kind + ' ');
599
+ }
600
+
601
+ if (node.value.async) {
602
+ context.write('async ');
603
+ }
604
+
605
+ if (node.value.generator) {
606
+ context.write('*');
607
+ }
608
+
609
+ if (node.computed) context.write('[');
610
+ context.visit(node.key);
611
+ if (node.computed) context.write(']');
612
+
613
+ context.write('(');
614
+ sequence(
615
+ context,
616
+ node.value.params,
617
+ (node.value.returnType ?? node.value.body)?.loc?.start ?? node.loc?.end ?? null,
618
+ false
619
+ );
620
+ context.write(')');
621
+
622
+ if (node.value.returnType) context.visit(node.value.returnType);
623
+
624
+ context.write(' ');
625
+
626
+ if (node.value.body) context.visit(node.value.body);
627
+ },
628
+
629
+ /**
630
+ * @param {TSESTree.PropertyDefinition | TSESTree.TSAbstractPropertyDefinition | TSESTree.AccessorProperty | TSESTree.TSAbstractAccessorProperty} node
631
+ * @param {Context} context
632
+ */
633
+ 'PropertyDefinition|TSAbstractPropertyDefinition|AccessorProperty|TSAbstractAccessorProperty': (
634
+ node,
635
+ context
636
+ ) => {
637
+ if (node.decorators) {
638
+ for (const decorator of node.decorators) {
639
+ context.visit(decorator);
640
+ }
641
+ }
642
+
643
+ if (node.accessibility) {
644
+ context.write(node.accessibility + ' ');
645
+ }
646
+
647
+ if (
648
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
649
+ node.abstract ||
650
+ node.type === 'TSAbstractPropertyDefinition' ||
651
+ node.type === 'TSAbstractAccessorProperty'
652
+ ) {
653
+ context.write('abstract ');
654
+ }
655
+
656
+ if (node.static) {
657
+ context.write('static ');
658
+ }
659
+
660
+ if (
661
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
662
+ node.accessor ||
663
+ node.type === 'AccessorProperty' ||
664
+ node.type === 'TSAbstractAccessorProperty'
665
+ ) {
666
+ context.write('accessor ');
667
+ }
668
+
669
+ if (node.computed) {
670
+ context.write('[');
671
+ context.visit(node.key);
672
+ context.write(']');
673
+ } else {
674
+ context.visit(node.key);
675
+ }
676
+
677
+ if (node.typeAnnotation) {
678
+ if (node.type === 'AccessorProperty' || node.type === 'TSAbstractAccessorProperty') {
679
+ context.visit(node.typeAnnotation);
680
+ } else {
681
+ context.write(': ');
682
+ context.visit(node.typeAnnotation.typeAnnotation);
683
+ }
684
+ }
685
+
686
+ if (node.value) {
687
+ context.write(' = ');
688
+ context.visit(node.value);
689
+ }
690
+
691
+ context.write(';');
692
+
693
+ flush_trailing_comments(
694
+ context,
695
+ (node.value ?? node.typeAnnotation ?? node.key).loc?.end ?? null,
696
+ null
697
+ );
698
+ },
699
+
572
700
  /**
573
701
  * @param {TSESTree.RestElement | TSESTree.SpreadElement} node
574
702
  * @param {Context} context
@@ -579,24 +707,80 @@ export default (options = {}) => {
579
707
 
580
708
  // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
581
709
  if (node.typeAnnotation) context.visit(node.typeAnnotation);
710
+ },
711
+
712
+ /**
713
+ * @param {TSESTree.TSConstructSignatureDeclaration | TSESTree.TSCallSignatureDeclaration} node
714
+ * @param {Context} context
715
+ */
716
+ 'TSConstructSignatureDeclaration|TSCallSignatureDeclaration': (node, context) => {
717
+ if (node.type === 'TSConstructSignatureDeclaration') context.write('new');
718
+
719
+ if (node.typeParameters) {
720
+ context.visit(node.typeParameters);
721
+ }
722
+
723
+ context.write('(');
724
+
725
+ sequence(
726
+ context,
727
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
728
+ node.parameters ?? node.params,
729
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
730
+ (node.typeAnnotation ?? node.returnType)?.loc?.start ?? null,
731
+ false
732
+ );
733
+ context.write(')');
734
+
735
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
736
+ if (node.typeAnnotation || node.returnType) {
737
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
738
+ context.visit(node.typeAnnotation ?? node.returnType);
739
+ }
740
+ },
741
+
742
+ /**
743
+ * @param {TSESTree.TSFunctionType | TSESTree.TSConstructorType} node
744
+ * @param {Context} context
745
+ */
746
+ 'TSFunctionType|TSConstructorType': (node, context) => {
747
+ if (node.type === 'TSConstructorType') context.write('new ');
748
+ if (node.typeParameters) context.visit(node.typeParameters);
749
+
750
+ context.write('(');
751
+
752
+ sequence(
753
+ context,
754
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
755
+ node.parameters ?? node.params,
756
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
757
+ node.typeAnnotation?.typeAnnotation?.loc?.start ??
758
+ node.returnType?.typeAnnotation?.loc?.start ??
759
+ null,
760
+ false
761
+ );
762
+
763
+ context.write(') => ');
764
+
765
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
766
+ context.visit(node.typeAnnotation?.typeAnnotation ?? node.returnType?.typeAnnotation);
582
767
  }
583
768
  };
584
769
 
585
770
  return {
586
771
  _(node, context, visit) {
587
- const is_statement = /(Statement|Declaration)$/.test(node.type);
588
-
589
772
  if (node.loc) {
590
773
  flush_comments_until(context, null, node.loc.start, true);
591
774
  }
592
775
 
593
776
  visit(node);
594
-
595
- if (is_statement && node.loc) {
596
- flush_trailing_comments(context, node.loc.end, null);
597
- }
598
777
  },
599
778
 
779
+ AccessorProperty:
780
+ shared[
781
+ 'PropertyDefinition|TSAbstractPropertyDefinition|AccessorProperty|TSAbstractAccessorProperty'
782
+ ],
783
+
600
784
  ArrayExpression: shared['ArrayExpression|ArrayPattern'],
601
785
 
602
786
  ArrayPattern: shared['ArrayExpression|ArrayPattern'],
@@ -802,7 +986,11 @@ export default (options = {}) => {
802
986
 
803
987
  context.visit(node.local);
804
988
 
805
- if (node.local.name !== node.exported.name) {
989
+ if (
990
+ node.local.type === 'Identifier' &&
991
+ node.exported.type === 'Identifier' &&
992
+ node.local.name !== node.exported.name
993
+ ) {
806
994
  context.write(' as ');
807
995
  context.visit(node.exported);
808
996
  }
@@ -868,7 +1056,8 @@ export default (options = {}) => {
868
1056
  context.visit(node.consequent);
869
1057
 
870
1058
  if (node.alternate) {
871
- context.write(' else ');
1059
+ context.space();
1060
+ context.write('else ');
872
1061
  context.visit(node.alternate);
873
1062
  }
874
1063
  },
@@ -948,11 +1137,19 @@ export default (options = {}) => {
948
1137
  context.visit(node.arguments[index]);
949
1138
  }
950
1139
  }
1140
+ if (node.options) {
1141
+ context.write(', ');
1142
+ context.visit(node.options);
1143
+ }
951
1144
  context.write(')');
952
1145
  },
953
1146
 
954
1147
  ImportSpecifier(node, context) {
955
- if (node.local.name !== node.imported.name) {
1148
+ if (
1149
+ node.local.type === 'Identifier' &&
1150
+ node.imported.type === 'Identifier' &&
1151
+ node.local.name !== node.imported.name
1152
+ ) {
956
1153
  context.visit(node.imported);
957
1154
  context.write(' as ');
958
1155
  }
@@ -1008,49 +1205,7 @@ export default (options = {}) => {
1008
1205
  context.visit(node.property);
1009
1206
  },
1010
1207
 
1011
- MethodDefinition(node, context) {
1012
- if (node.decorators) {
1013
- for (const decorator of node.decorators) {
1014
- context.visit(decorator);
1015
- }
1016
- }
1017
-
1018
- if (node.static) {
1019
- context.write('static ');
1020
- }
1021
-
1022
- if (node.kind === 'get' || node.kind === 'set') {
1023
- // Getter or setter
1024
- context.write(node.kind + ' ');
1025
- }
1026
-
1027
- if (node.value.async) {
1028
- context.write('async ');
1029
- }
1030
-
1031
- if (node.value.generator) {
1032
- context.write('*');
1033
- }
1034
-
1035
- if (node.computed) context.write('[');
1036
- context.visit(node.key);
1037
- if (node.computed) context.write(']');
1038
-
1039
- context.write('(');
1040
- sequence(
1041
- context,
1042
- node.value.params,
1043
- (node.value.returnType ?? node.value.body)?.loc?.start ?? node.loc?.end ?? null,
1044
- false
1045
- );
1046
- context.write(')');
1047
-
1048
- if (node.value.returnType) context.visit(node.value.returnType);
1049
-
1050
- context.write(' ');
1051
-
1052
- if (node.value.body) context.visit(node.value.body);
1053
- },
1208
+ MethodDefinition: shared['MethodDefinition|TSAbstractMethodDefinition'],
1054
1209
 
1055
1210
  NewExpression: shared['CallExpression|NewExpression'],
1056
1211
 
@@ -1070,7 +1225,9 @@ export default (options = {}) => {
1070
1225
 
1071
1226
  // @ts-expect-error this isn't a real node type, but Acorn produces it
1072
1227
  ParenthesizedExpression(node, context) {
1073
- return context.visit(node.expression);
1228
+ context.write('(');
1229
+ context.visit(node.expression);
1230
+ context.write(')');
1074
1231
  },
1075
1232
 
1076
1233
  PrivateIdentifier(node, context) {
@@ -1127,47 +1284,10 @@ export default (options = {}) => {
1127
1284
  }
1128
1285
  },
1129
1286
 
1130
- PropertyDefinition(node, context) {
1131
- if (node.decorators) {
1132
- for (const decorator of node.decorators) {
1133
- context.visit(decorator);
1134
- }
1135
- }
1136
-
1137
- if (node.accessibility) {
1138
- context.write(node.accessibility + ' ');
1139
- }
1140
-
1141
- if (node.static) {
1142
- context.write('static ');
1143
- }
1144
-
1145
- if (node.computed) {
1146
- context.write('[');
1147
- context.visit(node.key);
1148
- context.write(']');
1149
- } else {
1150
- context.visit(node.key);
1151
- }
1152
-
1153
- if (node.typeAnnotation) {
1154
- context.write(': ');
1155
- context.visit(node.typeAnnotation.typeAnnotation);
1156
- }
1157
-
1158
- if (node.value) {
1159
- context.write(' = ');
1160
- context.visit(node.value);
1161
- }
1162
-
1163
- context.write(';');
1164
-
1165
- flush_trailing_comments(
1166
- context,
1167
- (node.value ?? node.typeAnnotation ?? node.key).loc?.end ?? null,
1168
- null
1169
- );
1170
- },
1287
+ PropertyDefinition:
1288
+ shared[
1289
+ 'PropertyDefinition|TSAbstractPropertyDefinition|AccessorProperty|TSAbstractAccessorProperty'
1290
+ ],
1171
1291
 
1172
1292
  RestElement: shared['RestElement|SpreadElement'],
1173
1293
 
@@ -1372,6 +1492,18 @@ export default (options = {}) => {
1372
1492
  }
1373
1493
  },
1374
1494
 
1495
+ TSAbstractMethodDefinition: shared['MethodDefinition|TSAbstractMethodDefinition'],
1496
+
1497
+ TSAbstractAccessorProperty:
1498
+ shared[
1499
+ 'PropertyDefinition|TSAbstractPropertyDefinition|AccessorProperty|TSAbstractAccessorProperty'
1500
+ ],
1501
+
1502
+ TSAbstractPropertyDefinition:
1503
+ shared[
1504
+ 'PropertyDefinition|TSAbstractPropertyDefinition|AccessorProperty|TSAbstractAccessorProperty'
1505
+ ],
1506
+
1375
1507
  TSDeclareFunction(node, context) {
1376
1508
  context.write('declare ');
1377
1509
 
@@ -1445,6 +1577,18 @@ export default (options = {}) => {
1445
1577
  context.write('undefined', node);
1446
1578
  },
1447
1579
 
1580
+ TSObjectKeyword(node, context) {
1581
+ context.write('object', node);
1582
+ },
1583
+
1584
+ TSBigIntKeyword(node, context) {
1585
+ context.write('bigint', node);
1586
+ },
1587
+
1588
+ TSIntrinsicKeyword(node, context) {
1589
+ context.write('intrinsic', node);
1590
+ },
1591
+
1448
1592
  TSArrayType(node, context) {
1449
1593
  context.visit(node.elementType);
1450
1594
  context.write('[]');
@@ -1475,11 +1619,70 @@ export default (options = {}) => {
1475
1619
  }
1476
1620
  },
1477
1621
 
1622
+ TSTypeOperator(node, context) {
1623
+ context.write(node.operator + ' ');
1624
+ if (node.typeAnnotation) {
1625
+ context.visit(node.typeAnnotation);
1626
+ }
1627
+ },
1628
+
1629
+ TSTemplateLiteralType(node, context) {
1630
+ context.write('`');
1631
+ const { quasis, types } = node;
1632
+ for (let i = 0; i < types.length; i++) {
1633
+ const raw = quasis[i].value.raw;
1634
+
1635
+ context.write(raw + '${');
1636
+ context.visit(types[i]);
1637
+ context.write('}');
1638
+
1639
+ if (/\n/.test(raw)) context.multiline = true;
1640
+ }
1641
+ context.write('`');
1642
+ },
1643
+
1644
+ TSParameterProperty(node, context) {
1645
+ if (node.accessibility) {
1646
+ context.write(node.accessibility + ' ');
1647
+ }
1648
+
1649
+ if (node.readonly) {
1650
+ context.write('readonly ');
1651
+ }
1652
+
1653
+ context.visit(node.parameter);
1654
+ },
1655
+
1656
+ TSExportAssignment(node, context) {
1657
+ context.write('export = ');
1658
+ context.visit(node.expression);
1659
+ context.write(';');
1660
+ },
1661
+
1662
+ TSNamespaceExportDeclaration(node, context) {
1663
+ context.write('export as namespace ');
1664
+ context.visit(node.id);
1665
+ context.write(';');
1666
+ },
1667
+
1478
1668
  //@ts-expect-error I don't know why, but this is relied upon in the tests, but doesn't exist in the TSESTree types
1479
1669
  TSExpressionWithTypeArguments(node, context) {
1480
1670
  context.visit(node.expression);
1481
1671
  },
1482
1672
 
1673
+ TSTypeAssertion(node, context) {
1674
+ context.write('<');
1675
+ context.visit(node.typeAnnotation);
1676
+ context.write('>');
1677
+ if (EXPRESSIONS_PRECEDENCE[node.expression.type] < EXPRESSIONS_PRECEDENCE.TSTypeAssertion) {
1678
+ context.write('(');
1679
+ context.visit(node.expression);
1680
+ context.write(')');
1681
+ } else {
1682
+ context.visit(node.expression);
1683
+ }
1684
+ },
1685
+
1483
1686
  TSTypeParameterInstantiation(node, context) {
1484
1687
  context.write('<');
1485
1688
  for (let i = 0; i < node.params.length; i++) {
@@ -1499,8 +1702,9 @@ export default (options = {}) => {
1499
1702
  },
1500
1703
 
1501
1704
  TSTypeParameter(node, context) {
1705
+ if (node.name && node.name.type) context.visit(node.name);
1502
1706
  // @ts-expect-error type mismatch TSESTree and acorn-typescript?
1503
- context.write(node.name, node);
1707
+ else context.write(node.name, node);
1504
1708
 
1505
1709
  if (node.constraint) {
1506
1710
  context.write(' extends ');
@@ -1508,11 +1712,35 @@ export default (options = {}) => {
1508
1712
  }
1509
1713
  },
1510
1714
 
1715
+ TSTypePredicate(node, context) {
1716
+ if (node.parameterName) {
1717
+ context.visit(node.parameterName);
1718
+ } else if (node.typeAnnotation) {
1719
+ context.visit(node.typeAnnotation);
1720
+ }
1721
+
1722
+ if (node.asserts) {
1723
+ context.write(' asserts ');
1724
+ } else {
1725
+ context.write(' is ');
1726
+ }
1727
+
1728
+ if (node.typeAnnotation) {
1729
+ context.visit(node.typeAnnotation.typeAnnotation);
1730
+ }
1731
+ },
1732
+
1511
1733
  TSTypeQuery(node, context) {
1512
1734
  context.write('typeof ');
1513
1735
  context.visit(node.exprName);
1514
1736
  },
1515
1737
 
1738
+ TSClassImplements(node, context) {
1739
+ if (node.expression) {
1740
+ context.visit(node.expression);
1741
+ }
1742
+ },
1743
+
1516
1744
  TSEnumMember(node, context) {
1517
1745
  context.visit(node.id);
1518
1746
  if (node.initializer) {
@@ -1521,25 +1749,7 @@ export default (options = {}) => {
1521
1749
  }
1522
1750
  },
1523
1751
 
1524
- TSFunctionType(node, context) {
1525
- if (node.typeParameters) context.visit(node.typeParameters);
1526
-
1527
- context.write('(');
1528
-
1529
- sequence(
1530
- context,
1531
- // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1532
- node.parameters,
1533
- // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1534
- node.typeAnnotation.typeAnnotation.loc?.start ?? null,
1535
- false
1536
- );
1537
-
1538
- context.write(') => ');
1539
-
1540
- // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1541
- context.visit(node.typeAnnotation.typeAnnotation);
1542
- },
1752
+ TSFunctionType: shared['TSFunctionType|TSConstructorType'],
1543
1753
 
1544
1754
  TSIndexSignature(node, context) {
1545
1755
  context.write('[');
@@ -1552,17 +1762,45 @@ export default (options = {}) => {
1552
1762
  context.visit(node.typeAnnotation);
1553
1763
  },
1554
1764
 
1765
+ TSMappedType(node, context) {
1766
+ context.write('{[');
1767
+
1768
+ if (node.typeParameter) {
1769
+ context.visit(node.typeParameter);
1770
+ } else {
1771
+ context.visit(node.key);
1772
+ context.write(' in ');
1773
+ context.visit(node.constraint);
1774
+ }
1775
+
1776
+ context.write(']');
1777
+ if (node.typeAnnotation) {
1778
+ context.write(': ');
1779
+ context.visit(node.typeAnnotation);
1780
+ }
1781
+ context.write('}');
1782
+ },
1783
+
1555
1784
  TSMethodSignature(node, context) {
1556
1785
  context.visit(node.key);
1557
1786
 
1558
1787
  context.write('(');
1559
1788
 
1560
- // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1561
- sequence(context, node.parameters, node.typeAnnotation.loc?.start ?? null, false);
1789
+ sequence(
1790
+ context,
1791
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1792
+ node.parameters ?? node.params,
1793
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1794
+ (node.typeAnnotation ?? node.returnType)?.loc?.start ?? null,
1795
+ false
1796
+ );
1562
1797
  context.write(')');
1563
1798
 
1564
1799
  // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1565
- context.visit(node.typeAnnotation);
1800
+ if (node.typeAnnotation || node.returnType) {
1801
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1802
+ context.visit(node.typeAnnotation ?? node.returnType);
1803
+ }
1566
1804
  },
1567
1805
 
1568
1806
  TSTupleType(node, context) {
@@ -1585,10 +1823,18 @@ export default (options = {}) => {
1585
1823
  sequence(context, node.types, node.loc?.end ?? null, false, ' &');
1586
1824
  },
1587
1825
 
1826
+ TSInferType(node, context) {
1827
+ context.write('infer ');
1828
+ context.visit(node.typeParameter);
1829
+ },
1830
+
1588
1831
  TSLiteralType(node, context) {
1589
1832
  context.visit(node.literal);
1590
1833
  },
1591
1834
 
1835
+ TSCallSignatureDeclaration:
1836
+ shared['TSConstructSignatureDeclaration|TSCallSignatureDeclaration'],
1837
+
1592
1838
  TSConditionalType(node, context) {
1593
1839
  context.visit(node.checkType);
1594
1840
  context.write(' extends ');
@@ -1599,6 +1845,17 @@ export default (options = {}) => {
1599
1845
  context.visit(node.falseType);
1600
1846
  },
1601
1847
 
1848
+ TSConstructSignatureDeclaration:
1849
+ shared['TSConstructSignatureDeclaration|TSCallSignatureDeclaration'],
1850
+
1851
+ TSConstructorType: shared['TSFunctionType|TSConstructorType'],
1852
+
1853
+ TSExternalModuleReference(node, context) {
1854
+ context.write('require(');
1855
+ context.visit(node.expression);
1856
+ context.write(');');
1857
+ },
1858
+
1602
1859
  TSIndexedAccessType(node, context) {
1603
1860
  context.visit(node.objectType);
1604
1861
  context.write('[');
@@ -1606,6 +1863,13 @@ export default (options = {}) => {
1606
1863
  context.write(']');
1607
1864
  },
1608
1865
 
1866
+ TSImportEqualsDeclaration(node, context) {
1867
+ context.write('import ');
1868
+ context.visit(node.id);
1869
+ context.write(' = ');
1870
+ context.visit(node.moduleReference);
1871
+ },
1872
+
1609
1873
  TSImportType(node, context) {
1610
1874
  context.write('import(');
1611
1875
  context.visit(node.argument);
@@ -1617,6 +1881,20 @@ export default (options = {}) => {
1617
1881
  }
1618
1882
  },
1619
1883
 
1884
+ TSOptionalType(node, context) {
1885
+ context.visit(node.typeAnnotation);
1886
+ context.write('?');
1887
+ },
1888
+
1889
+ TSRestType(node, context) {
1890
+ context.write('...');
1891
+ context.visit(node.typeAnnotation);
1892
+ },
1893
+
1894
+ TSThisType(node, context) {
1895
+ context.write('this', node);
1896
+ },
1897
+
1620
1898
  TSAsExpression(node, context) {
1621
1899
  if (node.expression) {
1622
1900
  const needs_parens =
@@ -1640,7 +1918,7 @@ export default (options = {}) => {
1640
1918
  context.write(' {');
1641
1919
  context.indent();
1642
1920
  context.newline();
1643
- sequence(context, node.members, node.loc?.end ?? null, false);
1921
+ sequence(context, node.members ?? node.body.members, node.loc?.end ?? null, false);
1644
1922
  context.dedent();
1645
1923
  context.newline();
1646
1924
  context.write('}');
@@ -1679,7 +1957,7 @@ export default (options = {}) => {
1679
1957
  context.write('interface ');
1680
1958
  context.visit(node.id);
1681
1959
  if (node.typeParameters) context.visit(node.typeParameters);
1682
- if (node.extends) {
1960
+ if (node.extends && node.extends.length > 0) {
1683
1961
  context.write(' extends ');
1684
1962
  sequence(context, node.extends, node.body.loc?.start ?? null, false);
1685
1963
  }
@@ -1688,6 +1966,24 @@ export default (options = {}) => {
1688
1966
  context.write('}');
1689
1967
  },
1690
1968
 
1969
+ TSInstantiationExpression(node, context) {
1970
+ context.visit(node.expression);
1971
+ context.visit(node.typeArguments);
1972
+ },
1973
+
1974
+ TSInterfaceHeritage(node, context) {
1975
+ if (node.expression) {
1976
+ context.visit(node.expression);
1977
+ }
1978
+ },
1979
+
1980
+ //@ts-expect-error I don't know why, but this is relied upon in the tests, but doesn't exist in the TSESTree types
1981
+ TSParenthesizedType(node, context) {
1982
+ context.write('(');
1983
+ context.visit(node.typeAnnotation);
1984
+ context.write(')');
1985
+ },
1986
+
1691
1987
  TSSatisfiesExpression(node, context) {
1692
1988
  if (node.expression) {
1693
1989
  const needs_parens =
package/types/index.d.ts CHANGED
@@ -48,6 +48,7 @@ declare module 'esrap' {
48
48
  dedent(): void;
49
49
  margin(): void;
50
50
  newline(): void;
51
+ space(): void;
51
52
 
52
53
  append(context: Context): void;
53
54
 
@@ -34,6 +34,6 @@
34
34
  null,
35
35
  null
36
36
  ],
37
- "mappings": ";MAGYA,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;aAEPC,QAAQA;;;;WAwBHC,QAAQA;;;;;;MAWbC,OAAOA;;kBAEFC,YAAYA;;;;;;;;;iBCHbC,KAAKA;;;;cC7CRC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCAPC,sBAAsBA;;aAHZC,IAAIA;MCLfC,SAASA;;;;;WAKXC,QAAQA;;;;;;;kBAODC,OAAOA;;;;;;;;;;MJTZd,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;MAEPC,QAAQA;;;;;;;;;;;aKdGO,IAAIA;MDLfC,SAASA;;;;;WAKXC,QAAQA;;;;;;;kBAODC,OAAOA;;;;;;;;;;MJTZd,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;MAEPC,QAAQA",
37
+ "mappings": ";MAGYA,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;aAEPC,QAAQA;;;;WAwBHC,QAAQA;;;;;;MAWbC,OAAOA;;kBAEFC,YAAYA;;;;;;;;;iBCHbC,KAAKA;;;;cC5CRC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCDPC,sBAAsBA;;aAHZC,IAAIA;MCLfC,SAASA;;;;;WAKXC,QAAQA;;;;;;;kBAODC,OAAOA;;;;;;;;;;MJTZd,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;MAEPC,QAAQA;;;;;;;;;;;aKdGO,IAAIA;MDLfC,SAASA;;;;;WAKXC,QAAQA;;;;;;;kBAODC,OAAOA;;;;;;;;;;MJTZd,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;MAEPC,QAAQA",
38
38
  "ignoreList": []
39
39
  }