esrap 1.4.9 → 2.0.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.
@@ -0,0 +1,1876 @@
1
+ /** @import { TSESTree } from '@typescript-eslint/types' */
2
+ /** @import { Visitors } from '../../types.js' */
3
+ /** @import { TSOptions, Comment } from '../types.js' */
4
+ import { Context } from 'esrap';
5
+
6
+ /** @typedef {TSESTree.Node} Node */
7
+
8
+ /** @type {Record<TSESTree.Expression['type'] | 'Super' | 'RestElement', number>} */
9
+ export const EXPRESSIONS_PRECEDENCE = {
10
+ JSXFragment: 20,
11
+ JSXElement: 20,
12
+ ArrayPattern: 20,
13
+ ObjectPattern: 20,
14
+ ArrayExpression: 20,
15
+ TaggedTemplateExpression: 20,
16
+ ThisExpression: 20,
17
+ Identifier: 20,
18
+ TemplateLiteral: 20,
19
+ Super: 20,
20
+ SequenceExpression: 20,
21
+ MemberExpression: 19,
22
+ MetaProperty: 19,
23
+ CallExpression: 19,
24
+ ChainExpression: 19,
25
+ ImportExpression: 19,
26
+ NewExpression: 19,
27
+ Literal: 18,
28
+ TSSatisfiesExpression: 18,
29
+ TSInstantiationExpression: 18,
30
+ TSNonNullExpression: 18,
31
+ TSTypeAssertion: 18,
32
+ AwaitExpression: 17,
33
+ ClassExpression: 17,
34
+ FunctionExpression: 17,
35
+ ObjectExpression: 17,
36
+ TSAsExpression: 16,
37
+ UpdateExpression: 16,
38
+ UnaryExpression: 15,
39
+ BinaryExpression: 14,
40
+ LogicalExpression: 13,
41
+ ConditionalExpression: 4,
42
+ ArrowFunctionExpression: 3,
43
+ AssignmentExpression: 3,
44
+ YieldExpression: 2,
45
+ RestElement: 1
46
+ };
47
+
48
+ const OPERATOR_PRECEDENCE = {
49
+ '||': 2,
50
+ '&&': 3,
51
+ '??': 4,
52
+ '|': 5,
53
+ '^': 6,
54
+ '&': 7,
55
+ '==': 8,
56
+ '!=': 8,
57
+ '===': 8,
58
+ '!==': 8,
59
+ '<': 9,
60
+ '>': 9,
61
+ '<=': 9,
62
+ '>=': 9,
63
+ in: 9,
64
+ instanceof: 9,
65
+ '<<': 10,
66
+ '>>': 10,
67
+ '>>>': 10,
68
+ '+': 11,
69
+ '-': 11,
70
+ '*': 12,
71
+ '%': 12,
72
+ '/': 12,
73
+ '**': 13
74
+ };
75
+
76
+ /**
77
+ * @param {Comment} comment
78
+ * @param {Context} context
79
+ */
80
+ function write_comment(comment, context) {
81
+ if (comment.type === 'Line') {
82
+ context.write(`//${comment.value}`);
83
+ } else {
84
+ context.write('/*');
85
+ const lines = comment.value.split('\n');
86
+
87
+ for (let i = 0; i < lines.length; i += 1) {
88
+ if (i > 0) context.newline();
89
+ context.write(lines[i]);
90
+ }
91
+
92
+ context.write('*/');
93
+ }
94
+ }
95
+
96
+ /**
97
+ * @param {TSOptions} [options]
98
+ * @returns {Visitors<TSESTree.Node>}
99
+ */
100
+ export default (options = {}) => {
101
+ const quote_char = options.quotes === 'double' ? '"' : "'";
102
+
103
+ const comments = options.comments ?? [];
104
+
105
+ let comment_index = 0;
106
+
107
+ /**
108
+ * Set `comment_index` to be the first comment after `start`.
109
+ * Most of the time this is already correct, but if nodes
110
+ * have been moved around we may need to search for it
111
+ * @param {TSESTree.Node} node
112
+ */
113
+ function reset_comment_index(node) {
114
+ if (!node.loc) {
115
+ comment_index = comments.length;
116
+ return;
117
+ }
118
+
119
+ let previous = comments[comment_index - 1];
120
+ let comment = comments[comment_index];
121
+
122
+ if (
123
+ comment &&
124
+ comment.loc &&
125
+ !before(comment.loc.start, node.loc.start) &&
126
+ (!previous || (previous.loc && before(previous.loc.start, node.loc.start)))
127
+ ) {
128
+ return;
129
+ }
130
+
131
+ // TODO use a binary search here, account for synthetic nodes (without `loc`)
132
+ comment_index = comments.findIndex(
133
+ (comment) => comment.loc && node.loc && !before(comment.loc.start, node.loc.start)
134
+ );
135
+ if (comment_index === -1) comment_index = comments.length;
136
+ }
137
+
138
+ /**
139
+ * @param {Context} context
140
+ * @param {{ line: number, column: number } | null} prev
141
+ * @param {{ line: number, column: number } | null} next
142
+ * @returns {boolean} true if final comment is a line comment
143
+ */
144
+ function flush_trailing_comments(context, prev, next) {
145
+ while (comment_index < comments.length) {
146
+ const comment = comments[comment_index];
147
+
148
+ if (
149
+ comment &&
150
+ prev &&
151
+ comment.loc.start.line === prev.line &&
152
+ (next === null || before(comment.loc.end, next))
153
+ ) {
154
+ context.write(' ');
155
+ write_comment(comment, context);
156
+
157
+ comment_index += 1;
158
+
159
+ if (comment.type === 'Line') {
160
+ return true;
161
+ }
162
+ } else {
163
+ break;
164
+ }
165
+ }
166
+
167
+ return false;
168
+ }
169
+
170
+ /**
171
+ * @param {Context} context
172
+ * @param {{ line: number, column: number } | null} from
173
+ * @param {{ line: number, column: number }} to
174
+ * @param {boolean} pad
175
+ */
176
+ function flush_comments_until(context, from, to, pad) {
177
+ let first = true;
178
+
179
+ while (comment_index < comments.length) {
180
+ const comment = comments[comment_index];
181
+
182
+ if (comment && comment.loc && to && before(comment.loc.start, to)) {
183
+ if (first && from !== null && comment.loc.start.line > from.line) {
184
+ context.margin();
185
+ context.newline();
186
+ }
187
+
188
+ first = false;
189
+
190
+ write_comment(comment, context);
191
+
192
+ if (comment.loc.end.line < to.line) {
193
+ context.newline();
194
+ } else if (pad) {
195
+ context.write(' ');
196
+ }
197
+
198
+ comment_index += 1;
199
+ } else {
200
+ break;
201
+ }
202
+ }
203
+ }
204
+
205
+ /**
206
+ * @param {Context} context
207
+ * @param {TSESTree.Node[]} nodes
208
+ * @param {{ line: number, column: number }} until
209
+ * @param {boolean} pad
210
+ */
211
+ function sequence(context, nodes, until, pad, separator = ',') {
212
+ let multiline = false;
213
+ let length = -1;
214
+
215
+ /** @type {boolean[]} */
216
+ const multiline_nodes = [];
217
+
218
+ const children = nodes.map((child, i) => {
219
+ const child_context = context.new();
220
+ if (child) child_context.visit(child);
221
+
222
+ multiline_nodes[i] = child_context.multiline;
223
+
224
+ if (i < nodes.length - 1 || !child) {
225
+ child_context.write(separator);
226
+ }
227
+
228
+ 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
+ }
232
+
233
+ length += child_context.measure() + 1;
234
+ multiline ||= child_context.multiline;
235
+
236
+ return child_context;
237
+ });
238
+
239
+ multiline ||= length > 60;
240
+
241
+ if (multiline) {
242
+ context.indent();
243
+ context.newline();
244
+ } else if (pad && length > 0) {
245
+ context.write(' ');
246
+ }
247
+
248
+ /** @type {Context | null} */
249
+ let prev = null;
250
+
251
+ for (let i = 0; i < nodes.length; i += 1) {
252
+ const child = children[i];
253
+
254
+ if (prev !== null) {
255
+ if (multiline_nodes[i - 1] || multiline_nodes[i]) {
256
+ context.margin();
257
+ }
258
+
259
+ if (nodes[i]) {
260
+ if (multiline) {
261
+ context.newline();
262
+ } else {
263
+ context.write(' ');
264
+ }
265
+ }
266
+ }
267
+
268
+ context.append(child);
269
+
270
+ prev = child;
271
+ }
272
+
273
+ flush_comments_until(context, nodes[nodes.length - 1]?.loc?.end ?? null, until, false);
274
+
275
+ if (multiline) {
276
+ context.dedent();
277
+ context.newline();
278
+ } else if (pad && length > 0) {
279
+ context.write(' ');
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Push a sequence of nodes onto separate lines, separating them with
285
+ * an extra newline where appropriate
286
+ * @param {Context} context
287
+ * @param {TSESTree.Node & { body: TSESTree.Node[] }} node
288
+ */
289
+ function body(context, node) {
290
+ reset_comment_index(node);
291
+
292
+ /** @type {string | null} */
293
+ let prev_type = null;
294
+ let prev_multiline = false;
295
+
296
+ for (const child of node.body) {
297
+ if (child.type === 'EmptyStatement') continue;
298
+
299
+ const child_context = context.new();
300
+ child_context.visit(child);
301
+
302
+ if (prev_type !== null) {
303
+ if (child_context.multiline || prev_multiline || child.type !== prev_type) {
304
+ context.margin();
305
+ }
306
+
307
+ context.newline();
308
+ }
309
+
310
+ context.append(child_context);
311
+
312
+ prev_type = child.type;
313
+ prev_multiline = child_context.multiline;
314
+ }
315
+
316
+ if (node.loc) {
317
+ context.newline();
318
+ flush_comments_until(
319
+ context,
320
+ node.body[node.body.length - 1]?.loc?.end ?? null,
321
+ node.loc.end,
322
+ false
323
+ );
324
+ }
325
+ }
326
+
327
+ const shared = {
328
+ /**
329
+ * @param {TSESTree.ArrayExpression | TSESTree.ArrayPattern} node
330
+ * @param {Context} context
331
+ */
332
+ 'ArrayExpression|ArrayPattern': (node, context) => {
333
+ context.write('[');
334
+ sequence(
335
+ context,
336
+ /** @type {TSESTree.Node[]} */ (node.elements),
337
+ node.loc?.end ?? null,
338
+ false
339
+ );
340
+ context.write(']');
341
+ },
342
+
343
+ /**
344
+ * @param {TSESTree.BinaryExpression | TSESTree.LogicalExpression} node
345
+ * @param {Context} context
346
+ */
347
+ 'BinaryExpression|LogicalExpression': (node, context) => {
348
+ // TODO
349
+ // const is_in = node.operator === 'in';
350
+ // if (is_in) {
351
+ // // Avoids confusion in `for` loops initializers
352
+ // chunks.write('(');
353
+ // }
354
+ if (needs_parens(node.left, node, false)) {
355
+ context.write('(');
356
+ context.visit(node.left);
357
+ context.write(')');
358
+ } else {
359
+ context.visit(node.left);
360
+ }
361
+
362
+ context.write(` ${node.operator} `);
363
+
364
+ if (needs_parens(node.right, node, true)) {
365
+ context.write('(');
366
+ context.visit(node.right);
367
+ context.write(')');
368
+ } else {
369
+ context.visit(node.right);
370
+ }
371
+ },
372
+
373
+ /**
374
+ * @param {TSESTree.BlockStatement | TSESTree.ClassBody} node
375
+ * @param {Context} context
376
+ */
377
+ 'BlockStatement|ClassBody': (node, context) => {
378
+ if (node.loc) {
379
+ const { line, column } = node.loc.start;
380
+ context.location(line, column);
381
+ context.write('{');
382
+ context.location(line, column + 1);
383
+ } else {
384
+ context.write('{');
385
+ }
386
+
387
+ const child_context = context.new();
388
+ body(child_context, node);
389
+
390
+ if (!child_context.empty()) {
391
+ context.indent();
392
+ context.newline();
393
+ context.append(child_context);
394
+ context.dedent();
395
+ context.newline();
396
+ }
397
+
398
+ if (node.loc) {
399
+ const { line, column } = node.loc.end;
400
+
401
+ context.location(line, column - 1);
402
+ context.write('}');
403
+ context.location(line, column);
404
+ } else {
405
+ context.write('}');
406
+ }
407
+ },
408
+
409
+ /**
410
+ * @param {TSESTree.CallExpression | TSESTree.NewExpression} node
411
+ * @param {Context} context
412
+ */
413
+ 'CallExpression|NewExpression': (node, context) => {
414
+ if (node.type === 'NewExpression') {
415
+ context.write('new ');
416
+ }
417
+
418
+ const needs_parens =
419
+ EXPRESSIONS_PRECEDENCE[node.callee.type] < EXPRESSIONS_PRECEDENCE.CallExpression ||
420
+ (node.type === 'NewExpression' && has_call_expression(node.callee));
421
+
422
+ if (needs_parens) {
423
+ context.write('(');
424
+ context.visit(node.callee);
425
+ context.write(')');
426
+ } else {
427
+ context.visit(node.callee);
428
+ }
429
+
430
+ if (/** @type {TSESTree.CallExpression} */ (node).optional) {
431
+ context.write('?.');
432
+ }
433
+
434
+ if (node.typeArguments) context.visit(node.typeArguments);
435
+
436
+ const open = context.new();
437
+ const join = context.new();
438
+
439
+ context.write('(');
440
+ context.append(open);
441
+
442
+ // if the final argument is multiline, it doesn't need to force all the
443
+ // other arguments to also be multiline
444
+ const child_context = context.new();
445
+ const final_context = context.new();
446
+
447
+ context.append(child_context);
448
+ context.append(final_context);
449
+
450
+ for (let i = 0; i < node.arguments.length; i += 1) {
451
+ const is_last = i === node.arguments.length - 1;
452
+ const context = is_last ? final_context : child_context;
453
+ const arg = node.arguments[i];
454
+
455
+ // special case — if final argument has a comment above it,
456
+ // we make the whole sequence multiline
457
+ if (
458
+ is_last &&
459
+ arg.loc &&
460
+ comments[comment_index] &&
461
+ comments[comment_index].loc &&
462
+ comments[comment_index].loc.start.line < arg.loc.start.line
463
+ ) {
464
+ child_context.multiline = true;
465
+ }
466
+
467
+ context.visit(arg);
468
+
469
+ if (!is_last) context.write(',');
470
+
471
+ const next = is_last
472
+ ? (node.loc?.end ?? null)
473
+ : (node.arguments[i + 1]?.loc?.start ?? null);
474
+
475
+ if (flush_trailing_comments(context, arg.loc?.end ?? null, next)) {
476
+ child_context.multiline = true;
477
+ }
478
+
479
+ if (!is_last) context.append(join);
480
+ }
481
+
482
+ context.multiline ||= child_context.multiline || final_context.multiline;
483
+
484
+ if (child_context.multiline) {
485
+ open.indent();
486
+ open.newline();
487
+ join.newline();
488
+ context.dedent();
489
+ context.newline();
490
+ } else {
491
+ join.write(' ');
492
+ }
493
+
494
+ context.write(')');
495
+ },
496
+
497
+ /**
498
+ * @param {TSESTree.ClassDeclaration | TSESTree.ClassExpression} node
499
+ * @param {Context} context
500
+ */
501
+ 'ClassDeclaration|ClassExpression': (node, context) => {
502
+ if (node.declare) {
503
+ context.write('declare ');
504
+ }
505
+
506
+ context.write('class ');
507
+
508
+ if (node.id) {
509
+ context.visit(node.id);
510
+ context.write(' ');
511
+ }
512
+
513
+ if (node.superClass) {
514
+ context.write('extends ');
515
+ context.visit(node.superClass);
516
+ context.write(' ');
517
+ }
518
+
519
+ if (node.implements && node.implements.length > 0) {
520
+ context.write('implements');
521
+ sequence(context, node.implements, node.body.loc?.start ?? null, true);
522
+ }
523
+
524
+ context.visit(node.body);
525
+ },
526
+
527
+ /**
528
+ * @param {TSESTree.ForInStatement | TSESTree.ForOfStatement} node
529
+ * @param {Context} context
530
+ */
531
+ 'ForInStatement|ForOfStatement': (node, context) => {
532
+ context.write('for ');
533
+ if (node.type === 'ForOfStatement' && node.await) context.write('await ');
534
+ context.write('(');
535
+
536
+ if (node.left.type === 'VariableDeclaration') {
537
+ handle_var_declaration(node.left, context);
538
+ } else {
539
+ context.visit(node.left);
540
+ }
541
+
542
+ context.write(node.type === 'ForInStatement' ? ' in ' : ' of ');
543
+ context.visit(node.right);
544
+ context.write(') ');
545
+ context.visit(node.body);
546
+ },
547
+
548
+ /**
549
+ * @param {TSESTree.FunctionDeclaration | TSESTree.FunctionExpression} node
550
+ * @param {Context} context
551
+ */
552
+ 'FunctionDeclaration|FunctionExpression': (node, context) => {
553
+ if (node.async) context.write('async ');
554
+ context.write(node.generator ? 'function* ' : 'function ');
555
+ if (node.id) context.visit(node.id);
556
+
557
+ if (node.typeParameters) {
558
+ context.visit(node.typeParameters);
559
+ }
560
+
561
+ context.write('(');
562
+ sequence(context, node.params, (node.returnType ?? node.body).loc?.start ?? null, false);
563
+ context.write(')');
564
+
565
+ if (node.returnType) context.visit(node.returnType);
566
+
567
+ context.write(' ');
568
+
569
+ context.visit(node.body);
570
+ },
571
+
572
+ /**
573
+ * @param {TSESTree.RestElement | TSESTree.SpreadElement} node
574
+ * @param {Context} context
575
+ */
576
+ 'RestElement|SpreadElement': (node, context) => {
577
+ context.write('...');
578
+ context.visit(node.argument);
579
+
580
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
581
+ if (node.typeAnnotation) context.visit(node.typeAnnotation);
582
+ }
583
+ };
584
+
585
+ return {
586
+ _(node, context, visit) {
587
+ const is_statement = /(Statement|Declaration)$/.test(node.type);
588
+
589
+ if (node.loc) {
590
+ flush_comments_until(context, null, node.loc.start, true);
591
+ }
592
+
593
+ visit(node);
594
+
595
+ if (is_statement && node.loc) {
596
+ flush_trailing_comments(context, node.loc.end, null);
597
+ }
598
+ },
599
+
600
+ ArrayExpression: shared['ArrayExpression|ArrayPattern'],
601
+
602
+ ArrayPattern: shared['ArrayExpression|ArrayPattern'],
603
+
604
+ ArrowFunctionExpression: (node, context) => {
605
+ if (node.async) context.write('async ');
606
+
607
+ context.write('(');
608
+ sequence(context, node.params, node.body.loc?.start ?? null, false);
609
+ context.write(') => ');
610
+
611
+ if (
612
+ node.body.type === 'ObjectExpression' ||
613
+ (node.body.type === 'AssignmentExpression' && node.body.left.type === 'ObjectPattern') ||
614
+ (node.body.type === 'LogicalExpression' && node.body.left.type === 'ObjectExpression') ||
615
+ (node.body.type === 'ConditionalExpression' && node.body.test.type === 'ObjectExpression')
616
+ ) {
617
+ context.write('(');
618
+ context.visit(node.body);
619
+ context.write(')');
620
+ } else {
621
+ context.visit(node.body);
622
+ }
623
+ },
624
+
625
+ AssignmentExpression(node, context) {
626
+ context.visit(node.left);
627
+ context.write(` ${node.operator} `);
628
+ context.visit(node.right);
629
+ },
630
+
631
+ AssignmentPattern(node, context) {
632
+ context.visit(node.left);
633
+ context.write(' = ');
634
+ context.visit(node.right);
635
+ },
636
+
637
+ AwaitExpression(node, context) {
638
+ if (node.argument) {
639
+ const precedence = EXPRESSIONS_PRECEDENCE[node.argument.type];
640
+
641
+ if (precedence && precedence < EXPRESSIONS_PRECEDENCE.AwaitExpression) {
642
+ context.write('await (');
643
+ context.visit(node.argument);
644
+ context.write(')');
645
+ } else {
646
+ context.write('await ');
647
+ context.visit(node.argument);
648
+ }
649
+ } else {
650
+ context.write('await');
651
+ }
652
+ },
653
+
654
+ BinaryExpression: shared['BinaryExpression|LogicalExpression'],
655
+
656
+ BlockStatement: shared['BlockStatement|ClassBody'],
657
+
658
+ BreakStatement(node, context) {
659
+ if (node.label) {
660
+ context.write('break ');
661
+ context.visit(node.label);
662
+ context.write(';');
663
+ } else {
664
+ context.write('break;');
665
+ }
666
+ },
667
+
668
+ CallExpression: shared['CallExpression|NewExpression'],
669
+
670
+ ChainExpression(node, context) {
671
+ context.visit(node.expression);
672
+ },
673
+
674
+ ClassBody: shared['BlockStatement|ClassBody'],
675
+
676
+ ClassDeclaration: shared['ClassDeclaration|ClassExpression'],
677
+
678
+ ClassExpression: shared['ClassDeclaration|ClassExpression'],
679
+
680
+ ConditionalExpression(node, context) {
681
+ if (EXPRESSIONS_PRECEDENCE[node.test.type] > EXPRESSIONS_PRECEDENCE.ConditionalExpression) {
682
+ context.visit(node.test);
683
+ } else {
684
+ context.write('(');
685
+ context.visit(node.test);
686
+ context.write(')');
687
+ }
688
+
689
+ const consequent = context.new();
690
+ const alternate = context.new();
691
+
692
+ // TODO flush comments here, rather than in visitors
693
+
694
+ consequent.visit(node.consequent);
695
+ alternate.visit(node.alternate);
696
+
697
+ if (
698
+ consequent.multiline ||
699
+ alternate.multiline ||
700
+ consequent.measure() + alternate.measure() > 50
701
+ ) {
702
+ context.indent();
703
+ context.newline();
704
+ context.write('? ');
705
+ context.append(consequent);
706
+ context.newline();
707
+ context.write(': ');
708
+ context.append(alternate);
709
+ context.dedent();
710
+ } else {
711
+ context.write(' ? ');
712
+ context.append(consequent);
713
+ context.write(' : ');
714
+ context.append(alternate);
715
+ }
716
+ },
717
+
718
+ ContinueStatement(node, context) {
719
+ if (node.label) {
720
+ context.write('continue ');
721
+ context.visit(node.label);
722
+ context.write(';');
723
+ } else {
724
+ context.write('continue;');
725
+ }
726
+ },
727
+
728
+ DebuggerStatement(node, context) {
729
+ context.write('debugger', node);
730
+ context.write(';');
731
+ },
732
+
733
+ Decorator(node, context) {
734
+ context.write('@');
735
+ context.visit(node.expression);
736
+ context.newline();
737
+ },
738
+
739
+ DoWhileStatement(node, context) {
740
+ context.write('do ');
741
+ context.visit(node.body);
742
+ context.write(' while (');
743
+ context.visit(node.test);
744
+ context.write(');');
745
+ },
746
+
747
+ EmptyStatement(node, context) {
748
+ context.write(';');
749
+ },
750
+
751
+ ExportAllDeclaration(node, context) {
752
+ context.write(node.exportKind === 'type' ? 'export type * ' : 'export * ');
753
+
754
+ if (node.exported) {
755
+ context.write('as ');
756
+ context.visit(node.exported);
757
+ }
758
+
759
+ context.write(' from ');
760
+ context.visit(node.source);
761
+ context.write(';');
762
+ },
763
+
764
+ ExportDefaultDeclaration(node, context) {
765
+ context.write('export default ');
766
+
767
+ context.visit(node.declaration);
768
+
769
+ if (node.declaration.type !== 'FunctionDeclaration') {
770
+ context.write(';');
771
+ }
772
+ },
773
+
774
+ ExportNamedDeclaration(node, context) {
775
+ context.write('export ');
776
+
777
+ if (node.declaration) {
778
+ context.visit(node.declaration);
779
+ return;
780
+ }
781
+
782
+ if (node.exportKind === 'type') {
783
+ context.write('type ');
784
+ }
785
+
786
+ context.write('{');
787
+ sequence(context, node.specifiers, node.source?.loc?.start ?? node.loc?.end ?? null, true);
788
+ context.write('}');
789
+
790
+ if (node.source) {
791
+ context.write(' from ');
792
+ context.visit(node.source);
793
+ }
794
+
795
+ context.write(';');
796
+ },
797
+
798
+ ExportSpecifier(node, context) {
799
+ if (node.exportKind === 'type') {
800
+ context.write('type ');
801
+ }
802
+
803
+ context.visit(node.local);
804
+
805
+ if (node.local.name !== node.exported.name) {
806
+ context.write(' as ');
807
+ context.visit(node.exported);
808
+ }
809
+ },
810
+
811
+ ExpressionStatement(node, context) {
812
+ if (
813
+ node.expression.type === 'ObjectExpression' ||
814
+ (node.expression.type === 'AssignmentExpression' &&
815
+ node.expression.left.type === 'ObjectPattern') ||
816
+ node.expression.type === 'FunctionExpression'
817
+ ) {
818
+ // is an AssignmentExpression to an ObjectPattern
819
+ context.write('(');
820
+ context.visit(node.expression);
821
+ context.write(');');
822
+ return;
823
+ }
824
+
825
+ context.visit(node.expression);
826
+ context.write(';');
827
+ },
828
+
829
+ ForStatement: (node, context) => {
830
+ context.write('for (');
831
+
832
+ if (node.init) {
833
+ if (node.init.type === 'VariableDeclaration') {
834
+ handle_var_declaration(node.init, context);
835
+ } else {
836
+ context.visit(node.init);
837
+ }
838
+ }
839
+
840
+ context.write('; ');
841
+ if (node.test) context.visit(node.test);
842
+ context.write('; ');
843
+ if (node.update) context.visit(node.update);
844
+
845
+ context.write(') ');
846
+ context.visit(node.body);
847
+ },
848
+
849
+ ForInStatement: shared['ForInStatement|ForOfStatement'],
850
+
851
+ ForOfStatement: shared['ForInStatement|ForOfStatement'],
852
+
853
+ FunctionDeclaration: shared['FunctionDeclaration|FunctionExpression'],
854
+
855
+ FunctionExpression: shared['FunctionDeclaration|FunctionExpression'],
856
+
857
+ Identifier(node, context) {
858
+ let name = node.name;
859
+ context.write(name, node);
860
+
861
+ if (node.typeAnnotation) context.visit(node.typeAnnotation);
862
+ },
863
+
864
+ IfStatement(node, context) {
865
+ context.write('if (');
866
+ context.visit(node.test);
867
+ context.write(') ');
868
+ context.visit(node.consequent);
869
+
870
+ if (node.alternate) {
871
+ context.write(' else ');
872
+ context.visit(node.alternate);
873
+ }
874
+ },
875
+
876
+ ImportDeclaration(node, context) {
877
+ if (node.specifiers.length === 0) {
878
+ context.write('import ');
879
+ context.visit(node.source);
880
+ context.write(';');
881
+ return;
882
+ }
883
+
884
+ /** @type {TSESTree.ImportNamespaceSpecifier | null} */
885
+ let namespace_specifier = null;
886
+
887
+ /** @type {TSESTree.ImportDefaultSpecifier | null} */
888
+ let default_specifier = null;
889
+
890
+ /** @type {TSESTree.ImportSpecifier[]} */
891
+ const named_specifiers = [];
892
+
893
+ for (const s of node.specifiers) {
894
+ if (s.type === 'ImportNamespaceSpecifier') {
895
+ namespace_specifier = s;
896
+ } else if (s.type === 'ImportDefaultSpecifier') {
897
+ default_specifier = s;
898
+ } else {
899
+ named_specifiers.push(s);
900
+ }
901
+ }
902
+
903
+ context.write('import ');
904
+ if (node.importKind == 'type') context.write('type ');
905
+
906
+ if (default_specifier) {
907
+ context.write(default_specifier.local.name, default_specifier);
908
+ if (namespace_specifier || named_specifiers.length > 0) context.write(', ');
909
+ }
910
+
911
+ if (namespace_specifier) {
912
+ context.write('* as ' + namespace_specifier.local.name, namespace_specifier);
913
+ }
914
+
915
+ if (named_specifiers.length > 0) {
916
+ context.write('{');
917
+ sequence(context, named_specifiers, node.source.loc?.start ?? null, true);
918
+ context.write('}');
919
+ }
920
+
921
+ context.write(' from ');
922
+ context.visit(node.source);
923
+ if (node.attributes && node.attributes.length > 0) {
924
+ context.write(' with { ');
925
+ for (let index = 0; index < node.attributes.length; index++) {
926
+ const { key, value } = node.attributes[index];
927
+ context.visit(key);
928
+ context.write(': ');
929
+ context.visit(value);
930
+ if (index + 1 !== node.attributes.length) {
931
+ context.write(', ');
932
+ }
933
+ }
934
+ context.write(' }');
935
+ }
936
+ context.write(';');
937
+ },
938
+
939
+ ImportExpression(node, context) {
940
+ context.write('import(');
941
+ context.visit(node.source);
942
+ //@ts-expect-error for some reason the types haven't been updated
943
+ if (node.arguments) {
944
+ //@ts-expect-error
945
+ for (let index = 0; index < node.arguments.length; index++) {
946
+ context.write(', ');
947
+ //@ts-expect-error
948
+ context.visit(node.arguments[index]);
949
+ }
950
+ }
951
+ context.write(')');
952
+ },
953
+
954
+ ImportSpecifier(node, context) {
955
+ if (node.local.name !== node.imported.name) {
956
+ context.visit(node.imported);
957
+ context.write(' as ');
958
+ }
959
+
960
+ if (node.importKind == 'type') context.write('type ');
961
+ context.visit(node.local);
962
+ },
963
+
964
+ LabeledStatement(node, context) {
965
+ context.visit(node.label);
966
+ context.write(': ');
967
+ context.visit(node.body);
968
+ },
969
+
970
+ Literal(node, context) {
971
+ // TODO do we need to handle weird unicode characters somehow?
972
+ // str.replace(/\\u(\d{4})/g, (m, n) => String.fromCharCode(+n))
973
+
974
+ const value =
975
+ node.raw ||
976
+ (typeof node.value === 'string' ? quote(node.value, quote_char) : String(node.value));
977
+
978
+ context.write(value, node);
979
+ },
980
+
981
+ LogicalExpression: shared['BinaryExpression|LogicalExpression'],
982
+
983
+ MemberExpression(node, context) {
984
+ if (EXPRESSIONS_PRECEDENCE[node.object.type] < EXPRESSIONS_PRECEDENCE.MemberExpression) {
985
+ context.write('(');
986
+ context.visit(node.object);
987
+ context.write(')');
988
+ } else {
989
+ context.visit(node.object);
990
+ }
991
+
992
+ if (node.computed) {
993
+ if (node.optional) {
994
+ context.write('?.');
995
+ }
996
+ context.write('[');
997
+ context.visit(node.property);
998
+ context.write(']');
999
+ } else {
1000
+ context.write(node.optional ? '?.' : '.');
1001
+ context.visit(node.property);
1002
+ }
1003
+ },
1004
+
1005
+ MetaProperty(node, context) {
1006
+ context.visit(node.meta);
1007
+ context.write('.');
1008
+ context.visit(node.property);
1009
+ },
1010
+
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
+ },
1054
+
1055
+ NewExpression: shared['CallExpression|NewExpression'],
1056
+
1057
+ ObjectExpression(node, context) {
1058
+ context.write('{');
1059
+ sequence(context, node.properties, node.loc?.end ?? null, true);
1060
+ context.write('}');
1061
+ },
1062
+
1063
+ ObjectPattern(node, context) {
1064
+ context.write('{');
1065
+ sequence(context, node.properties, node.loc?.end ?? null, true);
1066
+ context.write('}');
1067
+
1068
+ if (node.typeAnnotation) context.visit(node.typeAnnotation);
1069
+ },
1070
+
1071
+ // @ts-expect-error this isn't a real node type, but Acorn produces it
1072
+ ParenthesizedExpression(node, context) {
1073
+ return context.visit(node.expression);
1074
+ },
1075
+
1076
+ PrivateIdentifier(node, context) {
1077
+ context.write('#');
1078
+ context.write(node.name, node);
1079
+ },
1080
+
1081
+ Program(node, context) {
1082
+ body(context, node);
1083
+ },
1084
+
1085
+ Property(node, context) {
1086
+ const value = node.value.type === 'AssignmentPattern' ? node.value.left : node.value;
1087
+
1088
+ const shorthand =
1089
+ !node.computed &&
1090
+ node.kind === 'init' &&
1091
+ node.key.type === 'Identifier' &&
1092
+ value.type === 'Identifier' &&
1093
+ node.key.name === value.name;
1094
+
1095
+ if (shorthand) {
1096
+ context.visit(node.value);
1097
+ return;
1098
+ }
1099
+
1100
+ // shorthand methods
1101
+ if (node.value.type === 'FunctionExpression') {
1102
+ if (node.kind !== 'init') context.write(node.kind + ' ');
1103
+ if (node.value.async) context.write('async ');
1104
+ if (node.value.generator) context.write('*');
1105
+ if (node.computed) context.write('[');
1106
+ context.visit(node.key);
1107
+ if (node.computed) context.write(']');
1108
+ context.write('(');
1109
+ sequence(
1110
+ context,
1111
+ node.value.params,
1112
+ (node.value.returnType ?? node.value.body).loc?.start ?? null,
1113
+ false
1114
+ );
1115
+ context.write(')');
1116
+
1117
+ if (node.value.returnType) context.visit(node.value.returnType);
1118
+
1119
+ context.write(' ');
1120
+ context.visit(node.value.body);
1121
+ } else {
1122
+ if (node.computed) context.write('[');
1123
+ if (node.kind === 'get' || node.kind === 'set') context.write(node.kind + ' ');
1124
+ context.visit(node.key);
1125
+ context.write(node.computed ? ']: ' : ': ');
1126
+ context.visit(node.value);
1127
+ }
1128
+ },
1129
+
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
+ },
1171
+
1172
+ RestElement: shared['RestElement|SpreadElement'],
1173
+
1174
+ ReturnStatement(node, context) {
1175
+ if (node.argument) {
1176
+ const contains_comment =
1177
+ comments[comment_index] &&
1178
+ comments[comment_index].loc &&
1179
+ node.argument.loc &&
1180
+ before(comments[comment_index].loc.start, node.argument.loc.start);
1181
+
1182
+ context.write(contains_comment ? 'return (' : 'return ');
1183
+ context.visit(node.argument);
1184
+ context.write(contains_comment ? ');' : ';');
1185
+ } else {
1186
+ context.write('return;');
1187
+ }
1188
+ },
1189
+
1190
+ SequenceExpression(node, context) {
1191
+ context.write('(');
1192
+ sequence(context, node.expressions, node.loc?.end ?? null, false);
1193
+ context.write(')');
1194
+ },
1195
+
1196
+ SpreadElement: shared['RestElement|SpreadElement'],
1197
+
1198
+ StaticBlock(node, context) {
1199
+ context.write('static {');
1200
+ context.indent();
1201
+ context.newline();
1202
+
1203
+ body(context, node);
1204
+
1205
+ context.dedent();
1206
+ context.newline();
1207
+ context.write('}');
1208
+ },
1209
+
1210
+ Super(node, context) {
1211
+ context.write('super', node);
1212
+ },
1213
+
1214
+ SwitchStatement(node, context) {
1215
+ context.write('switch (');
1216
+ context.visit(node.discriminant);
1217
+ context.write(') {');
1218
+ context.indent();
1219
+
1220
+ let first = true;
1221
+
1222
+ for (const block of node.cases) {
1223
+ if (!first) {
1224
+ context.margin();
1225
+ }
1226
+
1227
+ first = false;
1228
+
1229
+ if (block.test) {
1230
+ context.newline();
1231
+ context.write('case ');
1232
+ context.visit(block.test);
1233
+ context.write(':');
1234
+ } else {
1235
+ context.newline();
1236
+ context.write('default:');
1237
+ }
1238
+
1239
+ context.indent();
1240
+
1241
+ for (const statement of block.consequent) {
1242
+ context.newline();
1243
+ context.visit(statement);
1244
+ }
1245
+
1246
+ context.dedent();
1247
+ }
1248
+
1249
+ context.dedent();
1250
+ context.newline();
1251
+ context.write('}');
1252
+ },
1253
+
1254
+ TaggedTemplateExpression(node, context) {
1255
+ context.visit(node.tag);
1256
+ context.visit(node.quasi);
1257
+ },
1258
+
1259
+ TemplateLiteral(node, context) {
1260
+ context.write('`');
1261
+
1262
+ const { quasis, expressions } = node;
1263
+
1264
+ for (let i = 0; i < expressions.length; i++) {
1265
+ const raw = quasis[i].value.raw;
1266
+
1267
+ context.write(raw + '${');
1268
+ context.visit(expressions[i]);
1269
+ context.write('}');
1270
+
1271
+ if (/\n/.test(raw)) context.multiline = true;
1272
+ }
1273
+
1274
+ const raw = quasis[quasis.length - 1].value.raw;
1275
+
1276
+ context.write(raw + '`');
1277
+ if (/\n/.test(raw)) context.multiline = true;
1278
+ },
1279
+
1280
+ ThisExpression(node, context) {
1281
+ context.write('this', node);
1282
+ },
1283
+
1284
+ ThrowStatement(node, context) {
1285
+ context.write('throw ');
1286
+ if (node.argument) context.visit(node.argument);
1287
+ context.write(';');
1288
+ },
1289
+
1290
+ TryStatement(node, context) {
1291
+ context.write('try ');
1292
+ context.visit(node.block);
1293
+
1294
+ if (node.handler) {
1295
+ if (node.handler.param) {
1296
+ context.write(' catch(');
1297
+ context.visit(node.handler.param);
1298
+ context.write(') ');
1299
+ } else {
1300
+ context.write(' catch ');
1301
+ }
1302
+
1303
+ context.visit(node.handler.body);
1304
+ }
1305
+
1306
+ if (node.finalizer) {
1307
+ context.write(' finally ');
1308
+ context.visit(node.finalizer);
1309
+ }
1310
+ },
1311
+
1312
+ UnaryExpression(node, context) {
1313
+ context.write(node.operator);
1314
+
1315
+ if (node.operator.length > 1) {
1316
+ context.write(' ');
1317
+ }
1318
+
1319
+ if (EXPRESSIONS_PRECEDENCE[node.argument.type] < EXPRESSIONS_PRECEDENCE.UnaryExpression) {
1320
+ context.write('(');
1321
+ context.visit(node.argument);
1322
+ context.write(')');
1323
+ } else {
1324
+ context.visit(node.argument);
1325
+ }
1326
+ },
1327
+
1328
+ UpdateExpression(node, context) {
1329
+ if (node.prefix) {
1330
+ context.write(node.operator);
1331
+ context.visit(node.argument);
1332
+ } else {
1333
+ context.visit(node.argument);
1334
+ context.write(node.operator);
1335
+ }
1336
+ },
1337
+
1338
+ VariableDeclaration(node, context) {
1339
+ handle_var_declaration(node, context);
1340
+ context.write(';');
1341
+ },
1342
+
1343
+ VariableDeclarator(node, context) {
1344
+ context.visit(node.id);
1345
+
1346
+ if (node.init) {
1347
+ context.write(' = ');
1348
+ context.visit(node.init);
1349
+ }
1350
+ },
1351
+
1352
+ WhileStatement(node, context) {
1353
+ context.write('while (');
1354
+ context.visit(node.test);
1355
+ context.write(') ');
1356
+ context.visit(node.body);
1357
+ },
1358
+
1359
+ WithStatement(node, context) {
1360
+ context.write('with (');
1361
+ context.visit(node.object);
1362
+ context.write(') ');
1363
+ context.visit(node.body);
1364
+ },
1365
+
1366
+ YieldExpression(node, context) {
1367
+ if (node.argument) {
1368
+ context.write(node.delegate ? `yield* ` : `yield `);
1369
+ context.visit(node.argument);
1370
+ } else {
1371
+ context.write(node.delegate ? `yield*` : `yield`);
1372
+ }
1373
+ },
1374
+
1375
+ TSDeclareFunction(node, context) {
1376
+ context.write('declare ');
1377
+
1378
+ if (node.async) {
1379
+ context.write('async ');
1380
+ }
1381
+
1382
+ context.write('function');
1383
+
1384
+ if (node.generator) {
1385
+ context.write('*');
1386
+ }
1387
+
1388
+ if (node.id) {
1389
+ context.write(' ');
1390
+ context.visit(node.id);
1391
+ }
1392
+
1393
+ if (node.typeParameters) {
1394
+ context.visit(node.typeParameters);
1395
+ }
1396
+
1397
+ context.write('(');
1398
+ sequence(context, node.params, node.returnType?.loc?.start ?? node.loc?.end ?? null, false);
1399
+ context.write(')');
1400
+
1401
+ if (node.returnType) {
1402
+ context.visit(node.returnType);
1403
+ }
1404
+
1405
+ context.write(';');
1406
+ },
1407
+
1408
+ TSNumberKeyword(node, context) {
1409
+ context.write('number', node);
1410
+ },
1411
+
1412
+ TSStringKeyword(node, context) {
1413
+ context.write('string', node);
1414
+ },
1415
+
1416
+ TSBooleanKeyword(node, context) {
1417
+ context.write('boolean', node);
1418
+ },
1419
+
1420
+ TSAnyKeyword(node, context) {
1421
+ context.write('any', node);
1422
+ },
1423
+
1424
+ TSVoidKeyword(node, context) {
1425
+ context.write('void', node);
1426
+ },
1427
+
1428
+ TSUnknownKeyword(node, context) {
1429
+ context.write('unknown', node);
1430
+ },
1431
+
1432
+ TSNeverKeyword(node, context) {
1433
+ context.write('never', node);
1434
+ },
1435
+
1436
+ TSSymbolKeyword(node, context) {
1437
+ context.write('symbol', node);
1438
+ },
1439
+
1440
+ TSNullKeyword(node, context) {
1441
+ context.write('null', node);
1442
+ },
1443
+
1444
+ TSUndefinedKeyword(node, context) {
1445
+ context.write('undefined', node);
1446
+ },
1447
+
1448
+ TSArrayType(node, context) {
1449
+ context.visit(node.elementType);
1450
+ context.write('[]');
1451
+ },
1452
+
1453
+ TSTypeAnnotation(node, context) {
1454
+ context.write(': ');
1455
+ context.visit(node.typeAnnotation);
1456
+ },
1457
+
1458
+ TSTypeLiteral(node, context) {
1459
+ context.write('{ ');
1460
+ sequence(context, node.members, node.loc?.end ?? null, false, ';');
1461
+ context.write(' }');
1462
+ },
1463
+
1464
+ TSPropertySignature(node, context) {
1465
+ context.visit(node.key);
1466
+ if (node.optional) context.write('?');
1467
+ if (node.typeAnnotation) context.visit(node.typeAnnotation);
1468
+ },
1469
+
1470
+ TSTypeReference(node, context) {
1471
+ context.visit(node.typeName);
1472
+
1473
+ if (node.typeArguments) {
1474
+ context.visit(node.typeArguments);
1475
+ }
1476
+ },
1477
+
1478
+ //@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
+ TSExpressionWithTypeArguments(node, context) {
1480
+ context.visit(node.expression);
1481
+ },
1482
+
1483
+ TSTypeParameterInstantiation(node, context) {
1484
+ context.write('<');
1485
+ for (let i = 0; i < node.params.length; i++) {
1486
+ context.visit(node.params[i]);
1487
+ if (i != node.params.length - 1) context.write(', ');
1488
+ }
1489
+ context.write('>');
1490
+ },
1491
+
1492
+ TSTypeParameterDeclaration(node, context) {
1493
+ context.write('<');
1494
+ for (let i = 0; i < node.params.length; i++) {
1495
+ context.visit(node.params[i]);
1496
+ if (i != node.params.length - 1) context.write(', ');
1497
+ }
1498
+ context.write('>');
1499
+ },
1500
+
1501
+ TSTypeParameter(node, context) {
1502
+ // @ts-expect-error type mismatch TSESTree and acorn-typescript?
1503
+ context.write(node.name, node);
1504
+
1505
+ if (node.constraint) {
1506
+ context.write(' extends ');
1507
+ context.visit(node.constraint);
1508
+ }
1509
+ },
1510
+
1511
+ TSTypeQuery(node, context) {
1512
+ context.write('typeof ');
1513
+ context.visit(node.exprName);
1514
+ },
1515
+
1516
+ TSEnumMember(node, context) {
1517
+ context.visit(node.id);
1518
+ if (node.initializer) {
1519
+ context.write(' = ');
1520
+ context.visit(node.initializer);
1521
+ }
1522
+ },
1523
+
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
+ },
1543
+
1544
+ TSIndexSignature(node, context) {
1545
+ context.write('[');
1546
+
1547
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1548
+ sequence(context, node.parameters, node.typeAnnotation?.loc?.start ?? null, false);
1549
+ context.write(']');
1550
+
1551
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1552
+ context.visit(node.typeAnnotation);
1553
+ },
1554
+
1555
+ TSMethodSignature(node, context) {
1556
+ context.visit(node.key);
1557
+
1558
+ context.write('(');
1559
+
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);
1562
+ context.write(')');
1563
+
1564
+ // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1565
+ context.visit(node.typeAnnotation);
1566
+ },
1567
+
1568
+ TSTupleType(node, context) {
1569
+ context.write('[');
1570
+ sequence(context, node.elementTypes, node.loc?.end ?? null, false);
1571
+ context.write(']');
1572
+ },
1573
+
1574
+ TSNamedTupleMember(node, context) {
1575
+ context.visit(node.label);
1576
+ context.write(': ');
1577
+ context.visit(node.elementType);
1578
+ },
1579
+
1580
+ TSUnionType(node, context) {
1581
+ sequence(context, node.types, node.loc?.end ?? null, false, ' |');
1582
+ },
1583
+
1584
+ TSIntersectionType(node, context) {
1585
+ sequence(context, node.types, node.loc?.end ?? null, false, ' &');
1586
+ },
1587
+
1588
+ TSLiteralType(node, context) {
1589
+ context.visit(node.literal);
1590
+ },
1591
+
1592
+ TSConditionalType(node, context) {
1593
+ context.visit(node.checkType);
1594
+ context.write(' extends ');
1595
+ context.visit(node.extendsType);
1596
+ context.write(' ? ');
1597
+ context.visit(node.trueType);
1598
+ context.write(' : ');
1599
+ context.visit(node.falseType);
1600
+ },
1601
+
1602
+ TSIndexedAccessType(node, context) {
1603
+ context.visit(node.objectType);
1604
+ context.write('[');
1605
+ context.visit(node.indexType);
1606
+ context.write(']');
1607
+ },
1608
+
1609
+ TSImportType(node, context) {
1610
+ context.write('import(');
1611
+ context.visit(node.argument);
1612
+ context.write(')');
1613
+
1614
+ if (node.qualifier) {
1615
+ context.write('.');
1616
+ context.visit(node.qualifier);
1617
+ }
1618
+ },
1619
+
1620
+ TSAsExpression(node, context) {
1621
+ if (node.expression) {
1622
+ const needs_parens =
1623
+ EXPRESSIONS_PRECEDENCE[node.expression.type] < EXPRESSIONS_PRECEDENCE.TSAsExpression;
1624
+
1625
+ if (needs_parens) {
1626
+ context.write('(');
1627
+ context.visit(node.expression);
1628
+ context.write(')');
1629
+ } else {
1630
+ context.visit(node.expression);
1631
+ }
1632
+ }
1633
+ context.write(' as ');
1634
+ context.visit(node.typeAnnotation);
1635
+ },
1636
+
1637
+ TSEnumDeclaration(node, context) {
1638
+ context.write('enum ');
1639
+ context.visit(node.id);
1640
+ context.write(' {');
1641
+ context.indent();
1642
+ context.newline();
1643
+ sequence(context, node.members, node.loc?.end ?? null, false);
1644
+ context.dedent();
1645
+ context.newline();
1646
+ context.write('}');
1647
+ },
1648
+
1649
+ TSModuleBlock(node, context) {
1650
+ context.write(' {');
1651
+ context.indent();
1652
+ context.newline();
1653
+ body(context, node);
1654
+ context.dedent();
1655
+ context.newline();
1656
+ context.write('}');
1657
+ },
1658
+
1659
+ TSModuleDeclaration(node, context) {
1660
+ if (node.declare) context.write('declare ');
1661
+ else context.write('namespace ');
1662
+
1663
+ context.visit(node.id);
1664
+
1665
+ if (!node.body) return;
1666
+ context.visit(node.body);
1667
+ },
1668
+
1669
+ TSNonNullExpression(node, context) {
1670
+ context.visit(node.expression);
1671
+ context.write('!');
1672
+ },
1673
+
1674
+ TSInterfaceBody(node, context) {
1675
+ sequence(context, node.body, node.loc?.end ?? null, true, ';');
1676
+ },
1677
+
1678
+ TSInterfaceDeclaration(node, context) {
1679
+ context.write('interface ');
1680
+ context.visit(node.id);
1681
+ if (node.typeParameters) context.visit(node.typeParameters);
1682
+ if (node.extends) {
1683
+ context.write(' extends ');
1684
+ sequence(context, node.extends, node.body.loc?.start ?? null, false);
1685
+ }
1686
+ context.write(' {');
1687
+ context.visit(node.body);
1688
+ context.write('}');
1689
+ },
1690
+
1691
+ TSSatisfiesExpression(node, context) {
1692
+ if (node.expression) {
1693
+ const needs_parens =
1694
+ EXPRESSIONS_PRECEDENCE[node.expression.type] <
1695
+ EXPRESSIONS_PRECEDENCE.TSSatisfiesExpression;
1696
+
1697
+ if (needs_parens) {
1698
+ context.write('(');
1699
+ context.visit(node.expression);
1700
+ context.write(')');
1701
+ } else {
1702
+ context.visit(node.expression);
1703
+ }
1704
+ }
1705
+ context.write(' satisfies ');
1706
+ context.visit(node.typeAnnotation);
1707
+ },
1708
+
1709
+ TSTypeAliasDeclaration(node, context) {
1710
+ context.write('type ');
1711
+ context.visit(node.id);
1712
+ if (node.typeParameters) context.visit(node.typeParameters);
1713
+ context.write(' = ');
1714
+ context.visit(node.typeAnnotation);
1715
+ context.write(';');
1716
+ },
1717
+
1718
+ TSQualifiedName(node, context) {
1719
+ context.visit(node.left);
1720
+ context.write('.');
1721
+ context.visit(node.right);
1722
+ }
1723
+ };
1724
+ };
1725
+
1726
+ /** @satisfies {Visitors} */
1727
+
1728
+ /**
1729
+ *
1730
+ * @param {TSESTree.Expression | TSESTree.PrivateIdentifier} node
1731
+ * @param {TSESTree.BinaryExpression | TSESTree.LogicalExpression} parent
1732
+ * @param {boolean} is_right
1733
+ * @returns
1734
+ */
1735
+ function needs_parens(node, parent, is_right) {
1736
+ if (node.type === 'PrivateIdentifier') return false;
1737
+
1738
+ // special case where logical expressions and coalesce expressions cannot be mixed,
1739
+ // either of them need to be wrapped with parentheses
1740
+ if (
1741
+ node.type === 'LogicalExpression' &&
1742
+ parent.type === 'LogicalExpression' &&
1743
+ ((parent.operator === '??' && node.operator !== '??') ||
1744
+ (parent.operator !== '??' && node.operator === '??'))
1745
+ ) {
1746
+ return true;
1747
+ }
1748
+
1749
+ const precedence = EXPRESSIONS_PRECEDENCE[node.type];
1750
+ const parent_precedence = EXPRESSIONS_PRECEDENCE[parent.type];
1751
+
1752
+ if (precedence !== parent_precedence) {
1753
+ // Different node types
1754
+ return (
1755
+ (!is_right && precedence === 15 && parent_precedence === 14 && parent.operator === '**') ||
1756
+ precedence < parent_precedence
1757
+ );
1758
+ }
1759
+
1760
+ if (precedence !== 13 && precedence !== 14) {
1761
+ // Not a `LogicalExpression` or `BinaryExpression`
1762
+ return false;
1763
+ }
1764
+
1765
+ if (
1766
+ /** @type {TSESTree.BinaryExpression} */ (node).operator === '**' &&
1767
+ parent.operator === '**'
1768
+ ) {
1769
+ // Exponentiation operator has right-to-left associativity
1770
+ return !is_right;
1771
+ }
1772
+
1773
+ if (is_right) {
1774
+ // Parenthesis are used if both operators have the same precedence
1775
+ return (
1776
+ OPERATOR_PRECEDENCE[/** @type {TSESTree.BinaryExpression} */ (node).operator] <=
1777
+ OPERATOR_PRECEDENCE[parent.operator]
1778
+ );
1779
+ }
1780
+
1781
+ return (
1782
+ OPERATOR_PRECEDENCE[/** @type {TSESTree.BinaryExpression} */ (node).operator] <
1783
+ OPERATOR_PRECEDENCE[parent.operator]
1784
+ );
1785
+ }
1786
+
1787
+ /** @param {TSESTree.Node} node */
1788
+ function has_call_expression(node) {
1789
+ while (node) {
1790
+ if (node.type === 'CallExpression') {
1791
+ return true;
1792
+ } else if (node.type === 'MemberExpression') {
1793
+ node = node.object;
1794
+ } else {
1795
+ return false;
1796
+ }
1797
+ }
1798
+ }
1799
+
1800
+ /**
1801
+ * @param {TSESTree.VariableDeclaration} node
1802
+ * @param {Context} context
1803
+ */
1804
+ function handle_var_declaration(node, context) {
1805
+ const open = context.new();
1806
+ const join = context.new();
1807
+ const child_context = context.new();
1808
+
1809
+ context.append(child_context);
1810
+
1811
+ if (node.declare) {
1812
+ child_context.write('declare ');
1813
+ }
1814
+
1815
+ child_context.write(`${node.kind} `);
1816
+ child_context.append(open);
1817
+
1818
+ let first = true;
1819
+
1820
+ for (const d of node.declarations) {
1821
+ if (!first) child_context.append(join);
1822
+ first = false;
1823
+
1824
+ child_context.visit(d);
1825
+ }
1826
+
1827
+ const length = child_context.measure() + 2 * (node.declarations.length - 1);
1828
+
1829
+ const multiline = child_context.multiline || (node.declarations.length > 1 && length > 50);
1830
+
1831
+ if (multiline) {
1832
+ context.multiline = true;
1833
+
1834
+ if (node.declarations.length > 1) open.indent();
1835
+ join.write(',');
1836
+ join.newline();
1837
+ if (node.declarations.length > 1) context.dedent();
1838
+ } else {
1839
+ join.write(', ');
1840
+ }
1841
+ }
1842
+
1843
+ /**
1844
+ * @param {string} string
1845
+ * @param {string} char
1846
+ */
1847
+ function quote(string, char) {
1848
+ let out = char;
1849
+
1850
+ for (const c of string) {
1851
+ if (c === '\\') {
1852
+ out += '\\\\';
1853
+ } else if (c === char) {
1854
+ out += '\\' + c;
1855
+ } else if (c === '\n') {
1856
+ out += '\\n';
1857
+ } else if (c === '\r') {
1858
+ out += '\\r';
1859
+ } else {
1860
+ out += c;
1861
+ }
1862
+ }
1863
+
1864
+ return out + char;
1865
+ }
1866
+
1867
+ /**
1868
+ *
1869
+ * @param {{ line: number, column: number }} a
1870
+ * @param {{ line: number, column: number }} b
1871
+ */
1872
+ function before(a, b) {
1873
+ if (a.line < b.line) return true;
1874
+ if (a.line > b.line) return false;
1875
+ return a.column < b.column;
1876
+ }