esrap 2.2.6 → 2.2.8

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.6",
3
+ "version": "2.2.8",
4
4
  "description": "Parse in reverse",
5
5
  "repository": {
6
6
  "type": "git",
@@ -73,6 +73,118 @@ const OPERATOR_PRECEDENCE = {
73
73
  '**': 13
74
74
  };
75
75
 
76
+ /**
77
+ * Writes `keyword` bounded by source map locations for the exact character span,
78
+ * so breakpoints line up on keywords (not only identifiers and braces).
79
+ *
80
+ * @param {import('esrap').Context} context
81
+ * @param {number} line ESTree / acorn 1-based line
82
+ * @param {number} column 0-based ESTree column (UTF-16 indices, same as emitted JS)
83
+ * @param {string} keyword
84
+ */
85
+ function write_source_keyword(context, line, column, keyword) {
86
+ context.location(line, column);
87
+ context.write(keyword);
88
+ context.location(line, column + keyword.length);
89
+ }
90
+
91
+ /**
92
+ * Sequential fragments from one ESTree `loc.start`, advancing columns for source mappings.
93
+ * Pass each printed fragment exactly (usually including trailing spaces), e.g. `declare `, `class `.
94
+ * Usage: `const kw = create_keyword_write(context, node, predicate);`
95
+ *
96
+ * @param {import('esrap').Context} context
97
+ * @param {TSESTree.Node} node
98
+ * @param {(n: any) => boolean} [map_ok] When false or missing `loc`, writes go through `context.write` only.
99
+ * @returns {(fragment: string) => void}
100
+ */
101
+ function create_keyword_write(context, node, map_ok) {
102
+ let cursor =
103
+ node.loc && (!map_ok || map_ok(node))
104
+ ? { line: node.loc.start.line, col: node.loc.start.column }
105
+ : null;
106
+
107
+ return (fragment) => {
108
+ if (cursor) {
109
+ write_source_keyword(context, cursor.line, cursor.col, fragment);
110
+ cursor.col += fragment.length;
111
+ } else {
112
+ context.write(fragment);
113
+ }
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Map one keyword at `node.loc.start`, then append unmapped `suffix` (spaces, punctuation, etc.).
119
+ *
120
+ * @param {import('esrap').Context} context
121
+ * @param {TSESTree.Node} node
122
+ * @param {string} keyword
123
+ * @param {string} [suffix='']
124
+ */
125
+ function write_keyword(context, node, keyword, suffix = '') {
126
+ if (node.loc) {
127
+ write_source_keyword(context, node.loc.start.line, node.loc.start.column, keyword);
128
+ context.write(suffix);
129
+ } else {
130
+ context.write(keyword + suffix);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * `async function` offset heuristics assume the `function` token is on the same line as `async`
136
+ * and anchored with the function `id`/`body` ESTree locations.
137
+ *
138
+ * @param {TSESTree.FunctionDeclaration | TSESTree.FunctionExpression} node
139
+ */
140
+ function function_async_function_offset_ok(node) {
141
+ const line = /** @type {{ loc?: { start: { line: number } } }} */ (node).loc?.start.line;
142
+ if (line === undefined) return false;
143
+
144
+ return node.id?.loc?.start.line === line || node.body?.loc?.start.line === line;
145
+ }
146
+
147
+ /** @param {{ loc?: { start: { line: number }, end: { line: number } } }} node */
148
+ function single_line_node(node) {
149
+ return !!(node.loc && node.loc.start.line === node.loc.end.line);
150
+ }
151
+
152
+ /**
153
+ * @param {TSESTree.ClassDeclaration | TSESTree.ClassExpression} node
154
+ * @returns {boolean}
155
+ */
156
+ function class_modifier_keywords_map_ok(node) {
157
+ return (
158
+ !!node.loc &&
159
+ !node.decorators?.length &&
160
+ (node.id
161
+ ? node.id.loc?.start.line === node.loc.start.line
162
+ : node.body.loc.start.line === node.loc.start.line)
163
+ );
164
+ }
165
+
166
+ /**
167
+ * @param {TSESTree.MethodDefinition | TSESTree.TSAbstractMethodDefinition} node
168
+ * @returns {boolean}
169
+ */
170
+ function method_modifiers_keywords_map_ok(node) {
171
+ return !!(
172
+ node.loc &&
173
+ !node.decorators?.length &&
174
+ node.loc.start.line === node.value.loc?.start.line
175
+ );
176
+ }
177
+
178
+ /**
179
+ * @param {TSESTree.PropertyDefinition | TSESTree.TSAbstractPropertyDefinition | TSESTree.AccessorProperty | TSESTree.TSAbstractAccessorProperty} node
180
+ * @returns {boolean}
181
+ */
182
+ function field_modifiers_keywords_map_ok(node) {
183
+ if (!node.loc || node.decorators?.length) return false;
184
+ if (!node.value?.loc) return true;
185
+ return node.loc.start.line === node.value.loc.start.line;
186
+ }
187
+
76
188
  /**
77
189
  * @param {BaseComment} comment
78
190
  * @param {Context} context
@@ -459,7 +571,7 @@ export default (options = {}) => {
459
571
  */
460
572
  'CallExpression|NewExpression': (node, context) => {
461
573
  if (node.type === 'NewExpression') {
462
- context.write('new ');
574
+ write_keyword(context, node, 'new', ' ');
463
575
  }
464
576
 
465
577
  const needs_parens =
@@ -550,13 +662,11 @@ export default (options = {}) => {
550
662
  }
551
663
  }
552
664
 
553
- if (node.declare) {
554
- context.write('declare ');
555
- }
556
-
557
- if (node.abstract) context.write('abstract ');
665
+ const kw = create_keyword_write(context, node, class_modifier_keywords_map_ok);
558
666
 
559
- context.write('class ');
667
+ if (node.declare) kw('declare ');
668
+ if (node.abstract) kw('abstract ');
669
+ kw('class ');
560
670
 
561
671
  if (node.id) {
562
672
  context.visit(node.id);
@@ -589,8 +699,14 @@ export default (options = {}) => {
589
699
  * @param {Context} context
590
700
  */
591
701
  'ForInStatement|ForOfStatement': (node, context) => {
592
- context.write('for ');
593
- if (node.type === 'ForOfStatement' && node.await) context.write('await ');
702
+ const kw = create_keyword_write(context, node);
703
+
704
+ kw('for ');
705
+ if (node.type === 'ForOfStatement' && node.await) {
706
+ if (single_line_node(node)) kw('await ');
707
+ else context.write('await ');
708
+ }
709
+
594
710
  context.write('(');
595
711
 
596
712
  if (node.left.type === 'VariableDeclaration') {
@@ -610,8 +726,31 @@ export default (options = {}) => {
610
726
  * @param {Context} context
611
727
  */
612
728
  'FunctionDeclaration|FunctionExpression': (node, context) => {
613
- if (node.async) context.write('async ');
614
- context.write(node.generator ? 'function* ' : 'function ');
729
+ if (!node.loc?.start) {
730
+ if (node.async) context.write('async ');
731
+ context.write(node.generator ? 'function* ' : 'function ');
732
+ } else {
733
+ const { line, column } = node.loc.start;
734
+
735
+ if (node.async) {
736
+ if (function_async_function_offset_ok(node)) {
737
+ write_source_keyword(context, line, column, 'async ');
738
+ const col2 = column + 'async '.length;
739
+ write_source_keyword(context, line, col2, 'function');
740
+ context.write(node.generator ? '* ' : ' ');
741
+ } else {
742
+ context.write('async ');
743
+ context.write(node.generator ? 'function* ' : 'function ');
744
+ }
745
+ } else if (node.generator) {
746
+ write_source_keyword(context, line, column, 'function');
747
+ context.write('* ');
748
+ } else {
749
+ write_source_keyword(context, line, column, 'function');
750
+ context.write(' ');
751
+ }
752
+ }
753
+
615
754
  if (node.id) context.visit(node.id);
616
755
 
617
756
  if (node.typeParameters) {
@@ -640,35 +779,34 @@ export default (options = {}) => {
640
779
  }
641
780
  }
642
781
 
782
+ const kw = create_keyword_write(context, node, method_modifiers_keywords_map_ok);
783
+
643
784
  // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
644
785
  if (node.abstract || node.type === 'TSAbstractMethodDefinition') {
645
- context.write('abstract ');
786
+ kw('abstract ');
646
787
  }
647
788
 
648
789
  if (node.accessibility) {
649
- context.write(node.accessibility + ' ');
790
+ kw(node.accessibility + ' ');
650
791
  }
651
792
 
652
793
  if (node.override) {
653
- context.write('override ');
794
+ kw('override ');
654
795
  }
655
796
 
656
797
  if (node.static) {
657
- context.write('static ');
798
+ kw('static ');
658
799
  }
659
800
 
660
801
  if (node.kind === 'get' || node.kind === 'set') {
661
- // Getter or setter
662
- context.write(node.kind + ' ');
802
+ kw(node.kind + ' ');
663
803
  }
664
804
 
665
805
  if (node.value.async) {
666
- context.write('async ');
806
+ kw('async ');
667
807
  }
668
808
 
669
- if (node.value.generator) {
670
- context.write('*');
671
- }
809
+ if (node.value.generator) context.write('*');
672
810
 
673
811
  if (node.computed) context.write('[');
674
812
  context.visit(node.key);
@@ -704,8 +842,10 @@ export default (options = {}) => {
704
842
  }
705
843
  }
706
844
 
845
+ const kw = create_keyword_write(context, node, field_modifiers_keywords_map_ok);
846
+
707
847
  if (node.accessibility) {
708
- context.write(node.accessibility + ' ');
848
+ kw(node.accessibility + ' ');
709
849
  }
710
850
 
711
851
  if (
@@ -714,11 +854,11 @@ export default (options = {}) => {
714
854
  node.type === 'TSAbstractPropertyDefinition' ||
715
855
  node.type === 'TSAbstractAccessorProperty'
716
856
  ) {
717
- context.write('abstract ');
857
+ kw('abstract ');
718
858
  }
719
859
 
720
860
  if (node.static) {
721
- context.write('static ');
861
+ kw('static ');
722
862
  }
723
863
 
724
864
  if (
@@ -727,7 +867,7 @@ export default (options = {}) => {
727
867
  node.type === 'AccessorProperty' ||
728
868
  node.type === 'TSAbstractAccessorProperty'
729
869
  ) {
730
- context.write('accessor ');
870
+ kw('accessor ');
731
871
  }
732
872
 
733
873
  if (node.computed) {
@@ -854,11 +994,21 @@ export default (options = {}) => {
854
994
  ArrayPattern: shared['ArrayExpression|ArrayPattern'],
855
995
 
856
996
  ArrowFunctionExpression: (node, context) => {
857
- if (node.async) context.write('async ');
997
+ if (node.async) {
998
+ write_keyword(context, node, 'async', ' ');
999
+ }
1000
+
1001
+ if (node.typeParameters) {
1002
+ context.visit(node.typeParameters);
1003
+ }
858
1004
 
859
1005
  context.write('(');
860
- sequence(context, node.params, node.body.loc?.start ?? null, false);
861
- context.write(') => ');
1006
+ sequence(context, node.params, (node.returnType ?? node.body).loc?.start ?? null, false);
1007
+ context.write(')');
1008
+
1009
+ if (node.returnType) context.visit(node.returnType);
1010
+
1011
+ context.write(' => ');
862
1012
 
863
1013
  if (
864
1014
  node.body.type === 'ObjectExpression' ||
@@ -891,15 +1041,15 @@ export default (options = {}) => {
891
1041
  const precedence = EXPRESSIONS_PRECEDENCE[node.argument.type];
892
1042
 
893
1043
  if (precedence && precedence < EXPRESSIONS_PRECEDENCE.AwaitExpression) {
894
- context.write('await (');
1044
+ write_keyword(context, node, 'await', ' (');
895
1045
  context.visit(node.argument);
896
1046
  context.write(')');
897
1047
  } else {
898
- context.write('await ');
1048
+ write_keyword(context, node, 'await', ' ');
899
1049
  context.visit(node.argument);
900
1050
  }
901
1051
  } else {
902
- context.write('await');
1052
+ write_keyword(context, node, 'await', '');
903
1053
  }
904
1054
  },
905
1055
 
@@ -909,11 +1059,11 @@ export default (options = {}) => {
909
1059
 
910
1060
  BreakStatement(node, context) {
911
1061
  if (node.label) {
912
- context.write('break ');
1062
+ write_keyword(context, node, 'break', ' ');
913
1063
  context.visit(node.label);
914
1064
  context.write(';');
915
1065
  } else {
916
- context.write('break;');
1066
+ write_keyword(context, node, 'break', ';');
917
1067
  }
918
1068
  },
919
1069
 
@@ -969,11 +1119,11 @@ export default (options = {}) => {
969
1119
 
970
1120
  ContinueStatement(node, context) {
971
1121
  if (node.label) {
972
- context.write('continue ');
1122
+ write_keyword(context, node, 'continue', ' ');
973
1123
  context.visit(node.label);
974
1124
  context.write(';');
975
1125
  } else {
976
- context.write('continue;');
1126
+ write_keyword(context, node, 'continue', ';');
977
1127
  }
978
1128
  },
979
1129
 
@@ -989,9 +1139,19 @@ export default (options = {}) => {
989
1139
  },
990
1140
 
991
1141
  DoWhileStatement(node, context) {
992
- context.write('do ');
1142
+ write_keyword(context, node, 'do', ' ');
993
1143
  context.visit(node.body);
994
- context.write(' while (');
1144
+
1145
+ const test_loc = node.test.loc?.start;
1146
+ const body_end = node.body.loc?.end;
1147
+ if (test_loc && body_end && body_end.line === test_loc.line && test_loc.column >= 6) {
1148
+ context.write(' ');
1149
+ write_source_keyword(context, body_end.line, body_end.column + 1, 'while');
1150
+ context.write(' (');
1151
+ } else {
1152
+ context.write(' while (');
1153
+ }
1154
+
995
1155
  context.visit(node.test);
996
1156
  context.write(');');
997
1157
  },
@@ -1014,7 +1174,9 @@ export default (options = {}) => {
1014
1174
  },
1015
1175
 
1016
1176
  ExportDefaultDeclaration(node, context) {
1017
- context.write('export default ');
1177
+ const kw = create_keyword_write(context, node, single_line_node);
1178
+ kw('export ');
1179
+ kw('default ');
1018
1180
 
1019
1181
  context.visit(node.declaration);
1020
1182
 
@@ -1031,23 +1193,25 @@ export default (options = {}) => {
1031
1193
  for (const decorator of decl.decorators) {
1032
1194
  context.visit(decorator);
1033
1195
  }
1034
- context.write('export ');
1196
+ write_keyword(context, node, 'export', ' ');
1035
1197
  // Temporarily remove decorators so ClassDeclaration doesn't print them again
1036
1198
  const savedDecorators = decl.decorators;
1037
1199
  decl.decorators = [];
1038
1200
  context.visit(node.declaration);
1039
1201
  decl.decorators = savedDecorators;
1040
1202
  } else {
1041
- context.write('export ');
1203
+ write_keyword(context, node, 'export', ' ');
1042
1204
  context.visit(node.declaration);
1043
1205
  }
1044
1206
  return;
1045
1207
  }
1046
1208
 
1047
- context.write('export ');
1209
+ const kw = create_keyword_write(context, node);
1048
1210
 
1211
+ kw('export ');
1049
1212
  if (node.exportKind === 'type') {
1050
- context.write('type ');
1213
+ if (single_line_node(node)) kw('type ');
1214
+ else context.write('type ');
1051
1215
  }
1052
1216
 
1053
1217
  context.write('{');
@@ -1098,7 +1262,7 @@ export default (options = {}) => {
1098
1262
  },
1099
1263
 
1100
1264
  ForStatement: (node, context) => {
1101
- context.write('for (');
1265
+ write_keyword(context, node, 'for', ' (');
1102
1266
 
1103
1267
  if (node.init) {
1104
1268
  if (node.init.type === 'VariableDeclaration') {
@@ -1133,21 +1297,30 @@ export default (options = {}) => {
1133
1297
  },
1134
1298
 
1135
1299
  IfStatement(node, context) {
1136
- context.write('if (');
1300
+ write_keyword(context, node, 'if', ' (');
1137
1301
  context.visit(node.test);
1138
1302
  context.write(') ');
1139
1303
  context.visit(node.consequent);
1140
1304
 
1141
1305
  if (node.alternate) {
1142
1306
  context.space();
1143
- context.write('else ');
1307
+
1308
+ const alt_loc = node.alternate.loc?.start;
1309
+ const con_end = node.consequent.loc?.end;
1310
+ if (alt_loc && con_end && con_end.line === alt_loc.line && alt_loc.column >= 4) {
1311
+ write_source_keyword(context, con_end.line, con_end.column + 1, 'else');
1312
+ context.write(' ');
1313
+ } else {
1314
+ context.write('else ');
1315
+ }
1316
+
1144
1317
  context.visit(node.alternate);
1145
1318
  }
1146
1319
  },
1147
1320
 
1148
1321
  ImportDeclaration(node, context) {
1149
1322
  if (node.specifiers.length === 0) {
1150
- context.write('import ');
1323
+ write_keyword(context, node, 'import', ' ');
1151
1324
  context.visit(node.source);
1152
1325
  context.write(';');
1153
1326
  return;
@@ -1172,8 +1345,13 @@ export default (options = {}) => {
1172
1345
  }
1173
1346
  }
1174
1347
 
1175
- context.write('import ');
1176
- if (node.importKind == 'type') context.write('type ');
1348
+ const kw = create_keyword_write(context, node);
1349
+
1350
+ kw('import ');
1351
+ if (node.importKind == 'type') {
1352
+ if (single_line_node(node)) kw('type ');
1353
+ else context.write('type ');
1354
+ }
1177
1355
 
1178
1356
  if (default_specifier) {
1179
1357
  context.write(default_specifier.local.name, default_specifier);
@@ -1209,7 +1387,7 @@ export default (options = {}) => {
1209
1387
  },
1210
1388
 
1211
1389
  ImportExpression(node, context) {
1212
- context.write('import(');
1390
+ write_keyword(context, node, 'import', '(');
1213
1391
  context.visit(node.source);
1214
1392
  //@ts-expect-error for some reason the types haven't been updated
1215
1393
  if (node.arguments) {
@@ -1228,6 +1406,8 @@ export default (options = {}) => {
1228
1406
  },
1229
1407
 
1230
1408
  ImportSpecifier(node, context) {
1409
+ if (node.importKind == 'type') context.write('type ');
1410
+
1231
1411
  if (
1232
1412
  node.local.type === 'Identifier' &&
1233
1413
  node.imported.type === 'Identifier' &&
@@ -1237,7 +1417,6 @@ export default (options = {}) => {
1237
1417
  context.write(' as ');
1238
1418
  }
1239
1419
 
1240
- if (node.importKind == 'type') context.write('type ');
1241
1420
  context.visit(node.local);
1242
1421
  },
1243
1422
 
@@ -1339,8 +1518,10 @@ export default (options = {}) => {
1339
1518
 
1340
1519
  // shorthand methods
1341
1520
  if (node.value.type === 'FunctionExpression') {
1342
- if (node.kind !== 'init') context.write(node.kind + ' ');
1343
- if (node.value.async) context.write('async ');
1521
+ const kw = create_keyword_write(context, node);
1522
+
1523
+ if (node.kind !== 'init') kw(node.kind + ' ');
1524
+ if (node.value.async) kw('async ');
1344
1525
  if (node.value.generator) context.write('*');
1345
1526
  if (node.computed) context.write('[');
1346
1527
  context.visit(node.key);
@@ -1360,7 +1541,9 @@ export default (options = {}) => {
1360
1541
  context.visit(node.value.body);
1361
1542
  } else {
1362
1543
  if (node.computed) context.write('[');
1363
- if (node.kind === 'get' || node.kind === 'set') context.write(node.kind + ' ');
1544
+ if (node.kind === 'get' || node.kind === 'set') {
1545
+ write_keyword(context, node, node.kind, ' ');
1546
+ }
1364
1547
  context.visit(node.key);
1365
1548
  context.write(node.computed ? ']: ' : ': ');
1366
1549
  context.visit(node.value);
@@ -1382,11 +1565,11 @@ export default (options = {}) => {
1382
1565
  node.argument.loc &&
1383
1566
  before(comments[comment_index].loc.start, node.argument.loc.start);
1384
1567
 
1385
- context.write(contains_comment ? 'return (' : 'return ');
1568
+ write_keyword(context, node, 'return', contains_comment ? ' (' : ' ');
1386
1569
  context.visit(node.argument);
1387
1570
  context.write(contains_comment ? ');' : ';');
1388
1571
  } else {
1389
- context.write('return;');
1572
+ write_keyword(context, node, 'return', ';');
1390
1573
  }
1391
1574
  },
1392
1575
 
@@ -1399,7 +1582,7 @@ export default (options = {}) => {
1399
1582
  SpreadElement: shared['RestElement|SpreadElement'],
1400
1583
 
1401
1584
  StaticBlock(node, context) {
1402
- context.write('static {');
1585
+ write_keyword(context, node, 'static', ' {');
1403
1586
  context.indent();
1404
1587
  context.newline();
1405
1588
 
@@ -1415,7 +1598,7 @@ export default (options = {}) => {
1415
1598
  },
1416
1599
 
1417
1600
  SwitchStatement(node, context) {
1418
- context.write('switch (');
1601
+ write_keyword(context, node, 'switch', ' (');
1419
1602
  context.visit(node.discriminant);
1420
1603
  context.write(') {');
1421
1604
  context.indent();
@@ -1431,12 +1614,12 @@ export default (options = {}) => {
1431
1614
 
1432
1615
  if (block.test) {
1433
1616
  context.newline();
1434
- context.write('case ');
1617
+ write_keyword(context, block, 'case', ' ');
1435
1618
  context.visit(block.test);
1436
1619
  context.write(':');
1437
1620
  } else {
1438
1621
  context.newline();
1439
- context.write('default:');
1622
+ write_keyword(context, block, 'default', ':');
1440
1623
  }
1441
1624
 
1442
1625
  context.indent();
@@ -1485,29 +1668,40 @@ export default (options = {}) => {
1485
1668
  },
1486
1669
 
1487
1670
  ThrowStatement(node, context) {
1488
- context.write('throw ');
1671
+ write_keyword(context, node, 'throw', ' ');
1489
1672
  if (node.argument) context.visit(node.argument);
1490
1673
  context.write(';');
1491
1674
  },
1492
1675
 
1493
1676
  TryStatement(node, context) {
1494
- context.write('try ');
1677
+ write_keyword(context, node, 'try', ' ');
1495
1678
  context.visit(node.block);
1496
1679
 
1497
1680
  if (node.handler) {
1681
+ context.write(' ');
1682
+
1498
1683
  if (node.handler.param) {
1499
- context.write(' catch(');
1684
+ write_keyword(context, node.handler, 'catch', '(');
1500
1685
  context.visit(node.handler.param);
1501
1686
  context.write(') ');
1502
1687
  } else {
1503
- context.write(' catch ');
1688
+ write_keyword(context, node.handler, 'catch', ' ');
1504
1689
  }
1505
1690
 
1506
1691
  context.visit(node.handler.body);
1507
1692
  }
1508
1693
 
1509
1694
  if (node.finalizer) {
1510
- context.write(' finally ');
1695
+ const fin_loc = node.finalizer.loc?.start;
1696
+ const prev_end = node.handler ? node.handler.loc?.end : node.block.loc?.end;
1697
+ if (fin_loc && prev_end && prev_end.line === fin_loc.line && fin_loc.column >= 7) {
1698
+ context.write(' ');
1699
+ write_source_keyword(context, prev_end.line, prev_end.column + 1, 'finally');
1700
+ context.write(' ');
1701
+ } else {
1702
+ context.write(' finally ');
1703
+ }
1704
+
1511
1705
  context.visit(node.finalizer);
1512
1706
  }
1513
1707
  },
@@ -1553,25 +1747,27 @@ export default (options = {}) => {
1553
1747
  },
1554
1748
 
1555
1749
  WhileStatement(node, context) {
1556
- context.write('while (');
1750
+ write_keyword(context, node, 'while', ' (');
1557
1751
  context.visit(node.test);
1558
1752
  context.write(') ');
1559
1753
  context.visit(node.body);
1560
1754
  },
1561
1755
 
1562
1756
  WithStatement(node, context) {
1563
- context.write('with (');
1757
+ write_keyword(context, node, 'with', ' (');
1564
1758
  context.visit(node.object);
1565
1759
  context.write(') ');
1566
1760
  context.visit(node.body);
1567
1761
  },
1568
1762
 
1569
1763
  YieldExpression(node, context) {
1764
+ const word = node.delegate ? 'yield*' : 'yield';
1765
+
1570
1766
  if (node.argument) {
1571
- context.write(node.delegate ? `yield* ` : `yield `);
1767
+ write_keyword(context, node, word, ' ');
1572
1768
  context.visit(node.argument);
1573
1769
  } else {
1574
- context.write(node.delegate ? `yield*` : `yield`);
1770
+ write_keyword(context, node, word, '');
1575
1771
  }
1576
1772
  },
1577
1773
 
@@ -1588,13 +1784,15 @@ export default (options = {}) => {
1588
1784
  ],
1589
1785
 
1590
1786
  TSDeclareFunction(node, context) {
1591
- context.write('declare ');
1787
+ const kw = create_keyword_write(context, node);
1788
+
1789
+ kw('declare ');
1592
1790
 
1593
1791
  if (node.async) {
1594
- context.write('async ');
1792
+ kw('async ');
1595
1793
  }
1596
1794
 
1597
- context.write('function');
1795
+ kw('function');
1598
1796
 
1599
1797
  if (node.generator) {
1600
1798
  context.write('*');
@@ -1793,6 +1991,11 @@ export default (options = {}) => {
1793
1991
  context.write(' extends ');
1794
1992
  context.visit(node.constraint);
1795
1993
  }
1994
+
1995
+ if (node.default) {
1996
+ context.write(' = ');
1997
+ context.visit(node.default);
1998
+ }
1796
1999
  },
1797
2000
 
1798
2001
  TSTypePredicate(node, context) {
@@ -2202,11 +2405,11 @@ function handle_var_declaration(node, context) {
2202
2405
 
2203
2406
  context.append(child_context);
2204
2407
 
2205
- if (node.declare) {
2206
- child_context.write('declare ');
2207
- }
2408
+ const kw = create_keyword_write(child_context, node);
2409
+
2410
+ if (node.declare) kw('declare ');
2411
+ kw(node.kind + ' ');
2208
2412
 
2209
- child_context.write(`${node.kind} `);
2210
2413
  child_context.append(open);
2211
2414
 
2212
2415
  let first = true;