esrap 2.2.6 → 2.2.7

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.7",
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) {
@@ -1339,8 +1517,10 @@ export default (options = {}) => {
1339
1517
 
1340
1518
  // shorthand methods
1341
1519
  if (node.value.type === 'FunctionExpression') {
1342
- if (node.kind !== 'init') context.write(node.kind + ' ');
1343
- if (node.value.async) context.write('async ');
1520
+ const kw = create_keyword_write(context, node);
1521
+
1522
+ if (node.kind !== 'init') kw(node.kind + ' ');
1523
+ if (node.value.async) kw('async ');
1344
1524
  if (node.value.generator) context.write('*');
1345
1525
  if (node.computed) context.write('[');
1346
1526
  context.visit(node.key);
@@ -1360,7 +1540,9 @@ export default (options = {}) => {
1360
1540
  context.visit(node.value.body);
1361
1541
  } else {
1362
1542
  if (node.computed) context.write('[');
1363
- if (node.kind === 'get' || node.kind === 'set') context.write(node.kind + ' ');
1543
+ if (node.kind === 'get' || node.kind === 'set') {
1544
+ write_keyword(context, node, node.kind, ' ');
1545
+ }
1364
1546
  context.visit(node.key);
1365
1547
  context.write(node.computed ? ']: ' : ': ');
1366
1548
  context.visit(node.value);
@@ -1382,11 +1564,11 @@ export default (options = {}) => {
1382
1564
  node.argument.loc &&
1383
1565
  before(comments[comment_index].loc.start, node.argument.loc.start);
1384
1566
 
1385
- context.write(contains_comment ? 'return (' : 'return ');
1567
+ write_keyword(context, node, 'return', contains_comment ? ' (' : ' ');
1386
1568
  context.visit(node.argument);
1387
1569
  context.write(contains_comment ? ');' : ';');
1388
1570
  } else {
1389
- context.write('return;');
1571
+ write_keyword(context, node, 'return', ';');
1390
1572
  }
1391
1573
  },
1392
1574
 
@@ -1399,7 +1581,7 @@ export default (options = {}) => {
1399
1581
  SpreadElement: shared['RestElement|SpreadElement'],
1400
1582
 
1401
1583
  StaticBlock(node, context) {
1402
- context.write('static {');
1584
+ write_keyword(context, node, 'static', ' {');
1403
1585
  context.indent();
1404
1586
  context.newline();
1405
1587
 
@@ -1415,7 +1597,7 @@ export default (options = {}) => {
1415
1597
  },
1416
1598
 
1417
1599
  SwitchStatement(node, context) {
1418
- context.write('switch (');
1600
+ write_keyword(context, node, 'switch', ' (');
1419
1601
  context.visit(node.discriminant);
1420
1602
  context.write(') {');
1421
1603
  context.indent();
@@ -1431,12 +1613,12 @@ export default (options = {}) => {
1431
1613
 
1432
1614
  if (block.test) {
1433
1615
  context.newline();
1434
- context.write('case ');
1616
+ write_keyword(context, block, 'case', ' ');
1435
1617
  context.visit(block.test);
1436
1618
  context.write(':');
1437
1619
  } else {
1438
1620
  context.newline();
1439
- context.write('default:');
1621
+ write_keyword(context, block, 'default', ':');
1440
1622
  }
1441
1623
 
1442
1624
  context.indent();
@@ -1485,29 +1667,40 @@ export default (options = {}) => {
1485
1667
  },
1486
1668
 
1487
1669
  ThrowStatement(node, context) {
1488
- context.write('throw ');
1670
+ write_keyword(context, node, 'throw', ' ');
1489
1671
  if (node.argument) context.visit(node.argument);
1490
1672
  context.write(';');
1491
1673
  },
1492
1674
 
1493
1675
  TryStatement(node, context) {
1494
- context.write('try ');
1676
+ write_keyword(context, node, 'try', ' ');
1495
1677
  context.visit(node.block);
1496
1678
 
1497
1679
  if (node.handler) {
1680
+ context.write(' ');
1681
+
1498
1682
  if (node.handler.param) {
1499
- context.write(' catch(');
1683
+ write_keyword(context, node.handler, 'catch', '(');
1500
1684
  context.visit(node.handler.param);
1501
1685
  context.write(') ');
1502
1686
  } else {
1503
- context.write(' catch ');
1687
+ write_keyword(context, node.handler, 'catch', ' ');
1504
1688
  }
1505
1689
 
1506
1690
  context.visit(node.handler.body);
1507
1691
  }
1508
1692
 
1509
1693
  if (node.finalizer) {
1510
- context.write(' finally ');
1694
+ const fin_loc = node.finalizer.loc?.start;
1695
+ const prev_end = node.handler ? node.handler.loc?.end : node.block.loc?.end;
1696
+ if (fin_loc && prev_end && prev_end.line === fin_loc.line && fin_loc.column >= 7) {
1697
+ context.write(' ');
1698
+ write_source_keyword(context, prev_end.line, prev_end.column + 1, 'finally');
1699
+ context.write(' ');
1700
+ } else {
1701
+ context.write(' finally ');
1702
+ }
1703
+
1511
1704
  context.visit(node.finalizer);
1512
1705
  }
1513
1706
  },
@@ -1553,25 +1746,27 @@ export default (options = {}) => {
1553
1746
  },
1554
1747
 
1555
1748
  WhileStatement(node, context) {
1556
- context.write('while (');
1749
+ write_keyword(context, node, 'while', ' (');
1557
1750
  context.visit(node.test);
1558
1751
  context.write(') ');
1559
1752
  context.visit(node.body);
1560
1753
  },
1561
1754
 
1562
1755
  WithStatement(node, context) {
1563
- context.write('with (');
1756
+ write_keyword(context, node, 'with', ' (');
1564
1757
  context.visit(node.object);
1565
1758
  context.write(') ');
1566
1759
  context.visit(node.body);
1567
1760
  },
1568
1761
 
1569
1762
  YieldExpression(node, context) {
1763
+ const word = node.delegate ? 'yield*' : 'yield';
1764
+
1570
1765
  if (node.argument) {
1571
- context.write(node.delegate ? `yield* ` : `yield `);
1766
+ write_keyword(context, node, word, ' ');
1572
1767
  context.visit(node.argument);
1573
1768
  } else {
1574
- context.write(node.delegate ? `yield*` : `yield`);
1769
+ write_keyword(context, node, word, '');
1575
1770
  }
1576
1771
  },
1577
1772
 
@@ -1588,13 +1783,15 @@ export default (options = {}) => {
1588
1783
  ],
1589
1784
 
1590
1785
  TSDeclareFunction(node, context) {
1591
- context.write('declare ');
1786
+ const kw = create_keyword_write(context, node);
1787
+
1788
+ kw('declare ');
1592
1789
 
1593
1790
  if (node.async) {
1594
- context.write('async ');
1791
+ kw('async ');
1595
1792
  }
1596
1793
 
1597
- context.write('function');
1794
+ kw('function');
1598
1795
 
1599
1796
  if (node.generator) {
1600
1797
  context.write('*');
@@ -1793,6 +1990,11 @@ export default (options = {}) => {
1793
1990
  context.write(' extends ');
1794
1991
  context.visit(node.constraint);
1795
1992
  }
1993
+
1994
+ if (node.default) {
1995
+ context.write(' = ');
1996
+ context.visit(node.default);
1997
+ }
1796
1998
  },
1797
1999
 
1798
2000
  TSTypePredicate(node, context) {
@@ -2202,11 +2404,11 @@ function handle_var_declaration(node, context) {
2202
2404
 
2203
2405
  context.append(child_context);
2204
2406
 
2205
- if (node.declare) {
2206
- child_context.write('declare ');
2207
- }
2407
+ const kw = create_keyword_write(child_context, node);
2408
+
2409
+ if (node.declare) kw('declare ');
2410
+ kw(node.kind + ' ');
2208
2411
 
2209
- child_context.write(`${node.kind} `);
2210
2412
  child_context.append(open);
2211
2413
 
2212
2414
  let first = true;