espolar 0.3.0 → 0.4.0

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/dist/index.d.ts CHANGED
@@ -94,7 +94,15 @@ interface PrinterContext<Data = any> {
94
94
  writeNode(node: AST.Node | null | undefined): void;
95
95
  writeNodeList(nodes: readonly (AST.Node | null)[], separator: string): void;
96
96
  writeExpressionListWithCommaSep(nodes: readonly (AST.Expression | AST.SpreadElement | null)[]): void;
97
- writeNodeListWithNewLineSep(nodes: readonly (AST.ProgramStatement | AST.ClassElement)[]): void;
97
+ /**
98
+ * Write `nodes` with "newline" separator, but if the node is ranged and either:
99
+ * - it is the first node with `lastRangeEnd` provided, or
100
+ * - the previous adjacent node is ranged too,
101
+ * Then the source text ranging between the two adjacent nodes will be preserved instead of printing a newline character.
102
+ * @param nodes
103
+ * @param lastRangeEnd
104
+ */
105
+ writeNodeListWithNewLineSep(nodes: readonly (AST.ProgramStatement | AST.ClassElement)[], lastRangeEnd?: number): void;
98
106
  writeSource(start: number, end: number, data?: Data): void;
99
107
  writePreservedNode(node: AST.Node): void;
100
108
  appendMapping(sourceRange: SourceRange, generatedStart: number, generatedEnd: number, data?: Data): void;
package/dist/index.js CHANGED
@@ -1,4 +1,8 @@
1
1
  //#region src/printers.ts
2
+ /**
3
+ * Precedence table for expressions.
4
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table
5
+ */
2
6
  const EXPRESSIONS_PRECEDENCE = {
3
7
  ArrayPattern: 20,
4
8
  ObjectPattern: 20,
@@ -109,13 +113,13 @@ function operandOfBinaryExprNeedsParens(node, parent, where) {
109
113
  * which not contains a CallExpression
110
114
  */
111
115
  function validUnparenthesizedNewOperand(node) {
116
+ if (node.type === "ChainExpression" || node.type === "ImportExpression") return false;
112
117
  let cur = node;
113
118
  while (true) if (cur.type === "CallExpression") return false;
114
119
  else if (cur.type === "TSNonNullExpression") cur = cur.expression;
115
120
  else if (cur.type === "MemberExpression") cur = cur.object;
116
121
  else if (cur.type === "TaggedTemplateExpression") cur = cur.tag;
117
- else if (cur.type === "MetaProperty" || cur.type === "NewExpression" || cur.type === "ImportExpression") return true;
118
- else return cur !== node;
122
+ else return true;
119
123
  }
120
124
  function operandOfUnaryExprNeedsParens(node) {
121
125
  return EXPRESSIONS_PRECEDENCE[node.type] < EXPRESSIONS_PRECEDENCE.UnaryExpression;
@@ -130,19 +134,6 @@ function commentNeedsNewline(comment) {
130
134
  if (comment.type === "Line") return true;
131
135
  return comment.value.includes("\n");
132
136
  }
133
- function arrowConciseBodyNeedsWrap(body) {
134
- if (body.type === "BlockStatement") return false;
135
- switch (body.type) {
136
- case "ObjectExpression": return true;
137
- case "AssignmentExpression": return body.left.type === "ObjectPattern";
138
- case "LogicalExpression": return body.left.type === "ObjectExpression";
139
- case "ConditionalExpression": return body.test.type === "ObjectExpression";
140
- case "TSAsExpression":
141
- case "TSSatisfiesExpression":
142
- case "TSNonNullExpression": return body.expression ? arrowConciseBodyNeedsWrap(body.expression) : false;
143
- default: return false;
144
- }
145
- }
146
137
  const defaultPrinters = {
147
138
  Program: printProgram,
148
139
  Identifier: printIdentifier,
@@ -287,7 +278,7 @@ const defaultPrinters = {
287
278
  TSIntrinsicKeyword: printKeywordType
288
279
  };
289
280
  function printProgram(program, context) {
290
- context.writeNodeListWithNewLineSep(program.body);
281
+ context.writeNodeListWithNewLineSep(program.body, program.range?.[0]);
291
282
  }
292
283
  function canStartExpressionStatement(node) {
293
284
  let lhs;
@@ -312,6 +303,7 @@ function canStartExpressionStatement(node) {
312
303
  break;
313
304
  case "MemberExpression":
314
305
  lhs = node.object;
306
+ if (node.computed && lhs.type === "Identifier" && lhs.name === "let") return false;
315
307
  break;
316
308
  case "TaggedTemplateExpression":
317
309
  lhs = node.tag;
@@ -347,8 +339,11 @@ function printVariableDeclaration(declaration, context) {
347
339
  context.write(";");
348
340
  }
349
341
  function printVariableDeclarator(declarator, context) {
350
- context.writeNode(declarator.id);
351
- if (declarator.definite === true) context.write("!");
342
+ if (declarator.definite === true) {
343
+ context.write(String(declarator.id.name));
344
+ context.write("!");
345
+ writeOptionalTypeAnnotation(declarator.id, context);
346
+ } else context.writeNode(declarator.id);
352
347
  if (declarator.init) {
353
348
  context.write(" = ");
354
349
  if (expectAssignmentExprNeedsParen(declarator.init)) {
@@ -362,8 +357,10 @@ function printBlockStatement(block, context) {
362
357
  const body = block.body;
363
358
  context.write("{");
364
359
  if (body.length > 0) {
365
- context.write("\n");
366
- context.writeNodeListWithNewLineSep(body);
360
+ let lastRangeEnd;
361
+ if (block.range) lastRangeEnd = block.range[0] + 1;
362
+ else context.write("\n");
363
+ context.writeNodeListWithNewLineSep(body, lastRangeEnd);
367
364
  context.write("\n");
368
365
  }
369
366
  context.write("}");
@@ -686,7 +683,7 @@ function printProperty(property, context) {
686
683
  context.writeNode(value);
687
684
  return;
688
685
  }
689
- if (value.type === "FunctionExpression") {
686
+ if (value.type === "FunctionExpression" && (property.method || property.kind !== "init")) {
690
687
  if (property.kind !== "init") context.write(property.kind + " ");
691
688
  if (value.async === true) context.write("async ");
692
689
  if (value.generator === true) context.write("*");
@@ -708,7 +705,6 @@ function printProperty(property, context) {
708
705
  context.writeNode(property.key);
709
706
  context.write("]: ");
710
707
  } else {
711
- if (property.kind === "get" || property.kind === "set") context.write(property.kind + " ");
712
708
  context.writeNode(property.key);
713
709
  context.write(": ");
714
710
  }
@@ -794,6 +790,44 @@ function printFunction(fn, context) {
794
790
  context.writeNode(fn.body);
795
791
  }
796
792
  }
793
+ function canStartConciseBody(body) {
794
+ if (body.type === "BlockStatement" || body.type === "PrivateIdentifier") return true;
795
+ if (expectAssignmentExprNeedsParen(body)) return false;
796
+ let lhs;
797
+ switch (body.type) {
798
+ default: return true;
799
+ case "ObjectExpression":
800
+ case "FunctionExpression":
801
+ case "ClassExpression":
802
+ case "ObjectPattern": return false;
803
+ case "AssignmentExpression":
804
+ case "LogicalExpression":
805
+ case "BinaryExpression":
806
+ lhs = body.left;
807
+ break;
808
+ case "TSAsExpression":
809
+ case "TSSatisfiesExpression":
810
+ case "TSNonNullExpression":
811
+ lhs = body.expression;
812
+ break;
813
+ case "CallExpression":
814
+ lhs = body.callee;
815
+ break;
816
+ case "MemberExpression":
817
+ lhs = body.object;
818
+ if (body.computed && lhs.type === "Identifier" && lhs.name === "let") return false;
819
+ break;
820
+ case "TaggedTemplateExpression":
821
+ lhs = body.tag;
822
+ break;
823
+ case "ConditionalExpression":
824
+ lhs = body.test;
825
+ break;
826
+ case "UpdateExpression": return canStartConciseBody(body.argument);
827
+ case "ChainExpression": return canStartConciseBody(body.expression);
828
+ }
829
+ return operandOfBinaryExprNeedsParens(lhs, body, "left") || canStartConciseBody(lhs);
830
+ }
797
831
  function printArrowFunctionExpression(fn, context) {
798
832
  if (fn.async === true) context.write("async ");
799
833
  if (fn.typeParameters) context.writeNode(fn.typeParameters);
@@ -803,7 +837,7 @@ function printArrowFunctionExpression(fn, context) {
803
837
  writeReturnType(fn, context);
804
838
  context.write(" => ");
805
839
  const body = fn.body;
806
- if (arrowConciseBodyNeedsWrap(body)) {
840
+ if (!canStartConciseBody(body)) {
807
841
  context.write("(");
808
842
  context.writeNode(body);
809
843
  context.write(")");
@@ -845,8 +879,10 @@ function printClassBody(node, context) {
845
879
  context.write("{");
846
880
  const body = node.body;
847
881
  if (body.length > 0) {
848
- context.write("\n");
849
- context.writeNodeListWithNewLineSep(body);
882
+ let lastRangeEnd;
883
+ if (node.range) lastRangeEnd = node.range[0] + 1;
884
+ else context.write("\n");
885
+ context.writeNodeListWithNewLineSep(body, lastRangeEnd);
850
886
  context.write("\n");
851
887
  }
852
888
  context.write("}");
@@ -855,8 +891,10 @@ function printStaticBlock(node, context) {
855
891
  context.write("static {");
856
892
  const body = node.body;
857
893
  if (body.length > 0) {
858
- context.write("\n");
859
- context.writeNodeListWithNewLineSep(body);
894
+ let lastRangeEnd;
895
+ if (node.range) lastRangeEnd = node.range[0] + 1;
896
+ else context.write("\n");
897
+ context.writeNodeListWithNewLineSep(body, lastRangeEnd);
860
898
  context.write("\n");
861
899
  }
862
900
  context.write("}");
@@ -1400,8 +1438,11 @@ function printTSModuleDeclaration(node, context) {
1400
1438
  }
1401
1439
  }
1402
1440
  function printTSModuleBlock(node, context) {
1403
- context.write("{\n");
1404
- context.writeNodeListWithNewLineSep(node.body);
1441
+ context.write("{");
1442
+ let lastRangeEnd;
1443
+ if (node.range) lastRangeEnd = node.range[0] + 1;
1444
+ else context.write("\n");
1445
+ context.writeNodeListWithNewLineSep(node.body, lastRangeEnd);
1405
1446
  context.write("\n}");
1406
1447
  }
1407
1448
  function printTSDeclareFunction(node, context) {
@@ -1614,14 +1655,12 @@ function createPrinterContext(options) {
1614
1655
  needsSeparator = true;
1615
1656
  }
1616
1657
  },
1617
- writeNodeListWithNewLineSep(nodes) {
1618
- let lastRangeEnd;
1658
+ writeNodeListWithNewLineSep(nodes, lastRangeEnd) {
1619
1659
  let wroteNode = false;
1620
1660
  for (const node of nodes) {
1621
- if (!node) continue;
1622
1661
  const range = getNodeRange(node);
1623
- if (wroteNode) if (lastRangeEnd !== void 0 && range && range.start >= lastRangeEnd) context.writeSource(lastRangeEnd, range.start, getMappingData(null));
1624
- else context.write("\n");
1662
+ if (lastRangeEnd !== void 0 && range && range.start >= lastRangeEnd) context.writeSource(lastRangeEnd, range.start, getMappingData(null));
1663
+ else if (wroteNode) context.write("\n");
1625
1664
  context.writeNode(node);
1626
1665
  wroteNode = true;
1627
1666
  if (range) lastRangeEnd = range.end;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "espolar",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
package/src/api.ts CHANGED
@@ -57,8 +57,17 @@ export interface PrinterContext<Data = any> {
57
57
  writeExpressionListWithCommaSep(
58
58
  nodes: readonly (AST.Expression | AST.SpreadElement | null)[],
59
59
  ): void;
60
+ /**
61
+ * Write `nodes` with "newline" separator, but if the node is ranged and either:
62
+ * - it is the first node with `lastRangeEnd` provided, or
63
+ * - the previous adjacent node is ranged too,
64
+ * Then the source text ranging between the two adjacent nodes will be preserved instead of printing a newline character.
65
+ * @param nodes
66
+ * @param lastRangeEnd
67
+ */
60
68
  writeNodeListWithNewLineSep(
61
69
  nodes: readonly (AST.ProgramStatement | AST.ClassElement)[],
70
+ lastRangeEnd?: number,
62
71
  ): void;
63
72
  writeSource(start: number, end: number, data?: Data): void;
64
73
  writePreservedNode(node: AST.Node): void;
package/src/printer.ts CHANGED
@@ -220,35 +220,21 @@ function createPrinterContext<Data>(
220
220
  needsSeparator = true;
221
221
  }
222
222
  },
223
- writeNodeListWithNewLineSep(nodes) {
224
- let lastRangeEnd: number | undefined;
223
+ writeNodeListWithNewLineSep(nodes, lastRangeEnd) {
225
224
  let wroteNode = false;
226
-
227
225
  for (const node of nodes) {
228
- if (!node) {
229
- continue;
230
- }
231
-
232
226
  const range = getNodeRange(node);
233
- if (wroteNode) {
234
- if (
235
- lastRangeEnd !== undefined &&
236
- range &&
237
- range.start >= lastRangeEnd
238
- ) {
239
- context.writeSource(
240
- lastRangeEnd,
241
- range.start,
242
- getMappingData(null),
243
- );
244
- } else {
245
- context.write("\n");
246
- }
227
+ if (
228
+ lastRangeEnd !== undefined &&
229
+ range &&
230
+ range.start >= lastRangeEnd
231
+ ) {
232
+ context.writeSource(lastRangeEnd, range.start, getMappingData(null));
233
+ } else if (wroteNode) {
234
+ context.write("\n");
247
235
  }
248
-
249
236
  context.writeNode(node);
250
237
  wroteNode = true;
251
-
252
238
  if (range) {
253
239
  lastRangeEnd = range.end;
254
240
  } else {
package/src/printers.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import type { PrinterContext, Printers } from "./api.ts";
2
2
  import type { AST, AST_NODE_TYPES, Comment } from "./types.ts";
3
3
 
4
+ /**
5
+ * Precedence table for expressions.
6
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table
7
+ */
4
8
  const EXPRESSIONS_PRECEDENCE = {
5
9
  // LHS of =, must NOT parenthesized
6
10
  ArrayPattern: 20,
@@ -48,7 +52,9 @@ const EXPRESSIONS_PRECEDENCE = {
48
52
 
49
53
  // ranges from 13-5 depending on operator
50
54
  BinaryExpression: 13,
51
- // as/satisfies have same precedence as relational operators
55
+ // as/satisfies have same precedence as relational operators, e.g.
56
+ // `1 < 2 as number` is `(1 < 2) as number`, but
57
+ // `1 === 2 as number` is `1 === (2 as number)`
52
58
  TSAsExpression: 9,
53
59
  TSSatisfiesExpression: 9,
54
60
  // ranges from 4-3 depending on operator
@@ -225,6 +231,9 @@ function operandOfBinaryExprNeedsParens(
225
231
  * which not contains a CallExpression
226
232
  */
227
233
  function validUnparenthesizedNewOperand(node: MemberLikeExpression): boolean {
234
+ if (node.type === "ChainExpression" || node.type === "ImportExpression") {
235
+ return false;
236
+ }
228
237
  let cur: AST.Expression = node;
229
238
  while (true) {
230
239
  if (cur.type === "CallExpression") {
@@ -235,18 +244,8 @@ function validUnparenthesizedNewOperand(node: MemberLikeExpression): boolean {
235
244
  cur = cur.object;
236
245
  } else if (cur.type === "TaggedTemplateExpression") {
237
246
  cur = cur.tag;
238
- } else if (
239
- cur.type === "MetaProperty" ||
240
- cur.type === "NewExpression" ||
241
- cur.type === "ImportExpression"
242
- ) {
243
- return true;
244
247
  } else {
245
- // The operand is either:
246
- // - an expression with higher precedence, no need to check further
247
- // - an expression with lower precedence, will be inner-parenthesized later
248
- // - a ChainExpression, will be inner-parenthesized later
249
- return cur !== node;
248
+ return true;
250
249
  }
251
250
  }
252
251
  }
@@ -279,30 +278,6 @@ function commentNeedsNewline(comment: Comment): boolean {
279
278
  return comment.value.includes("\n");
280
279
  }
281
280
 
282
- function arrowConciseBodyNeedsWrap(
283
- body: AST.BlockStatement | AST.Expression,
284
- ): boolean {
285
- if (body.type === "BlockStatement") return false;
286
- switch (body.type) {
287
- case "ObjectExpression":
288
- return true;
289
- case "AssignmentExpression":
290
- return body.left.type === "ObjectPattern";
291
- case "LogicalExpression":
292
- return body.left.type === "ObjectExpression";
293
- case "ConditionalExpression":
294
- return body.test.type === "ObjectExpression";
295
- case "TSAsExpression":
296
- case "TSSatisfiesExpression":
297
- case "TSNonNullExpression":
298
- return body.expression
299
- ? arrowConciseBodyNeedsWrap(body.expression)
300
- : false;
301
- default:
302
- return false;
303
- }
304
- }
305
-
306
281
  // Printers
307
282
  export const defaultPrinters = {
308
283
  Program: printProgram,
@@ -457,7 +432,7 @@ export const defaultPrinters = {
457
432
  // JS – Statements
458
433
 
459
434
  function printProgram(program: AST.Program, context: PrinterContext): void {
460
- context.writeNodeListWithNewLineSep(program.body);
435
+ context.writeNodeListWithNewLineSep(program.body, program.range?.[0]);
461
436
  }
462
437
 
463
438
  function canStartExpressionStatement(
@@ -487,6 +462,9 @@ function canStartExpressionStatement(
487
462
  break;
488
463
  case "MemberExpression":
489
464
  lhs = node.object;
465
+ if (node.computed && lhs.type === "Identifier" && lhs.name === "let") {
466
+ return false;
467
+ }
490
468
  break;
491
469
  case "TaggedTemplateExpression":
492
470
  lhs = node.tag;
@@ -546,9 +524,18 @@ function printVariableDeclarator(
546
524
  declarator: AST.VariableDeclarator,
547
525
  context: PrinterContext,
548
526
  ): void {
549
- context.writeNode(declarator.id);
550
527
  if (declarator.definite === true) {
528
+ context.write(String((declarator.id as AST.Identifier).name));
551
529
  context.write("!");
530
+ writeOptionalTypeAnnotation(
531
+ declarator.id as unknown as {
532
+ optional?: boolean;
533
+ typeAnnotation?: AST.Node | null;
534
+ },
535
+ context,
536
+ );
537
+ } else {
538
+ context.writeNode(declarator.id);
552
539
  }
553
540
  if (declarator.init) {
554
541
  context.write(" = ");
@@ -570,8 +557,13 @@ function printBlockStatement(
570
557
  const body = block.body;
571
558
  context.write("{");
572
559
  if (body.length > 0) {
573
- context.write("\n");
574
- context.writeNodeListWithNewLineSep(body);
560
+ let lastRangeEnd: number | undefined;
561
+ if (block.range) {
562
+ lastRangeEnd = block.range[0] + 1;
563
+ } else {
564
+ context.write("\n");
565
+ }
566
+ context.writeNodeListWithNewLineSep(body, lastRangeEnd);
575
567
  context.write("\n");
576
568
  }
577
569
  context.write("}");
@@ -1161,7 +1153,10 @@ function printProperty(
1161
1153
  }
1162
1154
 
1163
1155
  // shorthand method
1164
- if (value.type === "FunctionExpression") {
1156
+ if (
1157
+ value.type === "FunctionExpression" &&
1158
+ (property.method || property.kind !== "init")
1159
+ ) {
1165
1160
  if (property.kind !== "init") {
1166
1161
  context.write(property.kind + " ");
1167
1162
  }
@@ -1192,9 +1187,6 @@ function printProperty(
1192
1187
  context.writeNode(property.key);
1193
1188
  context.write("]: ");
1194
1189
  } else {
1195
- if (property.kind === "get" || property.kind === "set") {
1196
- context.write(property.kind + " ");
1197
- }
1198
1190
  context.writeNode(property.key);
1199
1191
  context.write(": ");
1200
1192
  }
@@ -1342,6 +1334,60 @@ function printFunction(
1342
1334
  }
1343
1335
  }
1344
1336
 
1337
+ function canStartConciseBody(
1338
+ body: AST.BlockStatement | AST.Expression | AST.PrivateIdentifier,
1339
+ ): boolean {
1340
+ if (body.type === "BlockStatement" || body.type === "PrivateIdentifier") {
1341
+ return true;
1342
+ }
1343
+ if (expectAssignmentExprNeedsParen(body)) {
1344
+ return false;
1345
+ }
1346
+ let lhs: AST.Expression | AST.PrivateIdentifier;
1347
+ switch (body.type) {
1348
+ default:
1349
+ return true;
1350
+ case "ObjectExpression":
1351
+ case "FunctionExpression":
1352
+ case "ClassExpression":
1353
+ case "ObjectPattern":
1354
+ return false;
1355
+ case "AssignmentExpression":
1356
+ case "LogicalExpression":
1357
+ case "BinaryExpression":
1358
+ lhs = body.left;
1359
+ break;
1360
+ case "TSAsExpression":
1361
+ case "TSSatisfiesExpression":
1362
+ case "TSNonNullExpression":
1363
+ lhs = body.expression;
1364
+ break;
1365
+ case "CallExpression":
1366
+ lhs = body.callee;
1367
+ break;
1368
+ case "MemberExpression":
1369
+ lhs = body.object;
1370
+ if (body.computed && lhs.type === "Identifier" && lhs.name === "let") {
1371
+ return false;
1372
+ }
1373
+ break;
1374
+ case "TaggedTemplateExpression":
1375
+ lhs = body.tag;
1376
+ break;
1377
+ case "ConditionalExpression":
1378
+ lhs = body.test;
1379
+ break;
1380
+ case "UpdateExpression":
1381
+ return canStartConciseBody(body.argument);
1382
+ case "ChainExpression":
1383
+ return canStartConciseBody(body.expression);
1384
+ }
1385
+ return (
1386
+ operandOfBinaryExprNeedsParens(lhs, body, "left") ||
1387
+ canStartConciseBody(lhs)
1388
+ );
1389
+ }
1390
+
1345
1391
  function printArrowFunctionExpression(
1346
1392
  fn: AST.ArrowFunctionExpression,
1347
1393
  context: PrinterContext,
@@ -1358,7 +1404,7 @@ function printArrowFunctionExpression(
1358
1404
  writeReturnType(fn, context);
1359
1405
  context.write(" => ");
1360
1406
  const body = fn.body;
1361
- if (arrowConciseBodyNeedsWrap(body)) {
1407
+ if (!canStartConciseBody(body)) {
1362
1408
  context.write("(");
1363
1409
  context.writeNode(body);
1364
1410
  context.write(")");
@@ -1431,8 +1477,13 @@ function printClassBody(node: AST.ClassBody, context: PrinterContext): void {
1431
1477
  context.write("{");
1432
1478
  const body = node.body;
1433
1479
  if (body.length > 0) {
1434
- context.write("\n");
1435
- context.writeNodeListWithNewLineSep(body);
1480
+ let lastRangeEnd: number | undefined;
1481
+ if (node.range) {
1482
+ lastRangeEnd = node.range[0] + 1;
1483
+ } else {
1484
+ context.write("\n");
1485
+ }
1486
+ context.writeNodeListWithNewLineSep(body, lastRangeEnd);
1436
1487
  context.write("\n");
1437
1488
  }
1438
1489
  context.write("}");
@@ -1445,8 +1496,13 @@ function printStaticBlock(
1445
1496
  context.write("static {");
1446
1497
  const body = node.body;
1447
1498
  if (body.length > 0) {
1448
- context.write("\n");
1449
- context.writeNodeListWithNewLineSep(body);
1499
+ let lastRangeEnd: number | undefined;
1500
+ if (node.range) {
1501
+ lastRangeEnd = node.range[0] + 1;
1502
+ } else {
1503
+ context.write("\n");
1504
+ }
1505
+ context.writeNodeListWithNewLineSep(body, lastRangeEnd);
1450
1506
  context.write("\n");
1451
1507
  }
1452
1508
  context.write("}");
@@ -2486,8 +2542,14 @@ function printTSModuleBlock(
2486
2542
  node: AST.TSModuleBlock,
2487
2543
  context: PrinterContext,
2488
2544
  ): void {
2489
- context.write("{\n");
2490
- context.writeNodeListWithNewLineSep(node.body);
2545
+ context.write("{");
2546
+ let lastRangeEnd: number | undefined;
2547
+ if (node.range) {
2548
+ lastRangeEnd = node.range[0] + 1;
2549
+ } else {
2550
+ context.write("\n");
2551
+ }
2552
+ context.writeNodeListWithNewLineSep(node.body, lastRangeEnd);
2491
2553
  context.write("\n}");
2492
2554
  }
2493
2555