espolar 0.3.1 → 0.5.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
@@ -85,16 +85,29 @@ interface MappingDataOptions<Data> {
85
85
  }
86
86
  type MappingDataUndefinedOptions = { [K in keyof MappingDataOptions<0>]?: undefined };
87
87
  type PrintOptions<Data> = PrintOptionsBase<Data> & ([Data] extends [undefined] ? MappingDataUndefinedOptions : MappingDataOptions<Data>);
88
+ interface WriteNodeOptions {
89
+ noLeadingComment?: boolean;
90
+ noTrailingComment?: boolean;
91
+ }
88
92
  interface PrinterContext<Data = any> {
89
93
  readonly options: PrintOptions<Data>;
90
94
  readonly source: string;
91
95
  readonly generatedOffset: number;
92
96
  write(text: string): void;
93
97
  writeMapped(text: string, sourceStart: number, sourceEnd: number, data?: Data): void;
94
- writeNode(node: AST.Node | null | undefined): void;
98
+ writeNode(node: AST.Node | null | undefined, options?: WriteNodeOptions): void;
95
99
  writeNodeList(nodes: readonly (AST.Node | null)[], separator: string): void;
96
100
  writeExpressionListWithCommaSep(nodes: readonly (AST.Expression | AST.SpreadElement | null)[]): void;
97
- writeNodeListWithNewLineSep(nodes: readonly (AST.ProgramStatement | AST.ClassElement)[]): void;
101
+ /**
102
+ * Write `nodes` with "newline" separator, but if for node either:
103
+ * - it is the first or the last node, ranged, and `listRange` is also provided,
104
+ * - two adjacent nodes are both ranged,
105
+ * Then the source text ranging between the two adjacent nodes/boundaries will be preserved
106
+ * instead of printing a newline character.
107
+ * @param nodes
108
+ * @param lastRangeEnd
109
+ */
110
+ writeNodeListWithNewLineSep(nodes: readonly (AST.ProgramStatement | AST.ClassElement)[], listRange?: SourceRange): void;
98
111
  writeSource(start: number, end: number, data?: Data): void;
99
112
  writePreservedNode(node: AST.Node): void;
100
113
  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,
@@ -274,7 +278,12 @@ const defaultPrinters = {
274
278
  TSIntrinsicKeyword: printKeywordType
275
279
  };
276
280
  function printProgram(program, context) {
277
- context.writeNodeListWithNewLineSep(program.body);
281
+ let listRange;
282
+ if (program.range) listRange = {
283
+ start: program.range[0],
284
+ end: program.range[1]
285
+ };
286
+ context.writeNodeListWithNewLineSep(program.body, listRange);
278
287
  }
279
288
  function canStartExpressionStatement(node) {
280
289
  let lhs;
@@ -335,8 +344,11 @@ function printVariableDeclaration(declaration, context) {
335
344
  context.write(";");
336
345
  }
337
346
  function printVariableDeclarator(declarator, context) {
338
- context.writeNode(declarator.id);
339
- if (declarator.definite === true) context.write("!");
347
+ if (declarator.definite === true) {
348
+ context.write(String(declarator.id.name));
349
+ context.write("!");
350
+ writeOptionalTypeAnnotation(declarator.id, context);
351
+ } else context.writeNode(declarator.id);
340
352
  if (declarator.init) {
341
353
  context.write(" = ");
342
354
  if (expectAssignmentExprNeedsParen(declarator.init)) {
@@ -350,9 +362,12 @@ function printBlockStatement(block, context) {
350
362
  const body = block.body;
351
363
  context.write("{");
352
364
  if (body.length > 0) {
353
- context.write("\n");
354
- context.writeNodeListWithNewLineSep(body);
355
- context.write("\n");
365
+ let listRange;
366
+ if (block.range) listRange = {
367
+ start: block.range[0] + 1,
368
+ end: block.range[1] - 1
369
+ };
370
+ context.writeNodeListWithNewLineSep(body, listRange);
356
371
  }
357
372
  context.write("}");
358
373
  }
@@ -870,9 +885,12 @@ function printClassBody(node, context) {
870
885
  context.write("{");
871
886
  const body = node.body;
872
887
  if (body.length > 0) {
873
- context.write("\n");
874
- context.writeNodeListWithNewLineSep(body);
875
- context.write("\n");
888
+ let listRange;
889
+ if (node.range) listRange = {
890
+ start: node.range[0] + 1,
891
+ end: node.range[1] - 1
892
+ };
893
+ context.writeNodeListWithNewLineSep(body, listRange);
876
894
  }
877
895
  context.write("}");
878
896
  }
@@ -880,9 +898,12 @@ function printStaticBlock(node, context) {
880
898
  context.write("static {");
881
899
  const body = node.body;
882
900
  if (body.length > 0) {
883
- context.write("\n");
884
- context.writeNodeListWithNewLineSep(body);
885
- context.write("\n");
901
+ let listRange;
902
+ if (node.range) listRange = {
903
+ start: node.range[0] + 1,
904
+ end: node.range[1] - 1
905
+ };
906
+ context.writeNodeListWithNewLineSep(body, listRange);
886
907
  }
887
908
  context.write("}");
888
909
  }
@@ -1425,9 +1446,14 @@ function printTSModuleDeclaration(node, context) {
1425
1446
  }
1426
1447
  }
1427
1448
  function printTSModuleBlock(node, context) {
1428
- context.write("{\n");
1429
- context.writeNodeListWithNewLineSep(node.body);
1430
- context.write("\n}");
1449
+ context.write("{");
1450
+ let listRange;
1451
+ if (node.range) listRange = {
1452
+ start: node.range[0] + 1,
1453
+ end: node.range[1] - 1
1454
+ };
1455
+ context.writeNodeListWithNewLineSep(node.body, listRange);
1456
+ context.write("}");
1431
1457
  }
1432
1458
  function printTSDeclareFunction(node, context) {
1433
1459
  context.write("declare ");
@@ -1571,6 +1597,7 @@ function createPrinterContext(options) {
1571
1597
  };
1572
1598
  const context = {
1573
1599
  options,
1600
+ _skipLeadingComment: false,
1574
1601
  source: options.source,
1575
1602
  get generatedOffset() {
1576
1603
  return generatedOffset;
@@ -1589,7 +1616,7 @@ function createPrinterContext(options) {
1589
1616
  end: sourceEnd
1590
1617
  }, generatedStart, generatedOffset, data ?? getMappingData(null));
1591
1618
  },
1592
- writeNode(node) {
1619
+ writeNode(node, writeOpt = {}) {
1593
1620
  if (!node) return;
1594
1621
  const range = getNodeRange(node);
1595
1622
  const untouchedRet = isUntouched(node);
@@ -1601,13 +1628,17 @@ function createPrinterContext(options) {
1601
1628
  }
1602
1629
  const printer = printers[node.type];
1603
1630
  if (!printer) throw new Error(`No printer registered for node type ${node.type}`);
1604
- const leadingComments = options.getLeadingComments?.(node);
1605
- if (leadingComments) for (const comment of leadingComments) writeComment(comment, context);
1631
+ if (!writeOpt.noLeadingComment) {
1632
+ const leadingComments = options.getLeadingComments?.(node);
1633
+ if (leadingComments && !context._skipLeadingComment) for (const comment of leadingComments) writeComment(comment, context);
1634
+ }
1606
1635
  const generatedStart = generatedOffset;
1607
1636
  printer(node, context);
1608
1637
  const generatedEnd = generatedOffset;
1609
- const trailingComments = options.getTrailingComments?.(node);
1610
- if (trailingComments) for (const comment of trailingComments) writeComment(comment, context);
1638
+ if (!writeOpt.noTrailingComment) {
1639
+ const trailingComments = options.getTrailingComments?.(node);
1640
+ if (trailingComments) for (const comment of trailingComments) writeComment(comment, context);
1641
+ }
1611
1642
  const lastMappingGeneratedEnd = mappings.at(-1)?.generatedEnd ?? 0;
1612
1643
  if (range && lastMappingGeneratedEnd <= generatedStart) appendMapping(range, generatedStart, generatedEnd, getMappingData(node));
1613
1644
  },
@@ -1639,19 +1670,30 @@ function createPrinterContext(options) {
1639
1670
  needsSeparator = true;
1640
1671
  }
1641
1672
  },
1642
- writeNodeListWithNewLineSep(nodes) {
1673
+ writeNodeListWithNewLineSep(nodes, parentRange) {
1643
1674
  let lastRangeEnd;
1644
- let wroteNode = false;
1645
- for (const node of nodes) {
1646
- if (!node) continue;
1675
+ let trailingSepEnd;
1676
+ if (parentRange) {
1677
+ lastRangeEnd = parentRange.start;
1678
+ trailingSepEnd = parentRange.end;
1679
+ }
1680
+ for (let i = 0; i < nodes.length; i++) {
1681
+ const node = nodes[i];
1647
1682
  const range = getNodeRange(node);
1648
- if (wroteNode) if (lastRangeEnd !== void 0 && range && range.start >= lastRangeEnd) context.writeSource(lastRangeEnd, range.start, getMappingData(null));
1649
- else context.write("\n");
1650
- context.writeNode(node);
1651
- wroteNode = true;
1683
+ let noLeadingComment = false;
1684
+ let noTrailingComment = i < nodes.length - 1 ? true : range !== void 0 && parentRange !== void 0;
1685
+ if (lastRangeEnd !== void 0 && range && range.start >= lastRangeEnd) {
1686
+ context.writeSource(lastRangeEnd, range.start, getMappingData(null));
1687
+ noLeadingComment = true;
1688
+ } else if (i > 0) context.write("\n");
1689
+ context.writeNode(node, {
1690
+ noLeadingComment,
1691
+ noTrailingComment
1692
+ });
1652
1693
  if (range) lastRangeEnd = range.end;
1653
1694
  else lastRangeEnd = void 0;
1654
1695
  }
1696
+ if (trailingSepEnd !== void 0 && lastRangeEnd !== void 0) context.writeSource(lastRangeEnd, trailingSepEnd, getMappingData(null));
1655
1697
  },
1656
1698
  writeSource(start, end, data) {
1657
1699
  if (end < start) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "espolar",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
package/src/api.ts CHANGED
@@ -41,6 +41,11 @@ export type PrintOptions<Data> = PrintOptionsBase<Data> &
41
41
  ? MappingDataUndefinedOptions
42
42
  : MappingDataOptions<Data>);
43
43
 
44
+ export interface WriteNodeOptions {
45
+ noLeadingComment?: boolean;
46
+ noTrailingComment?: boolean;
47
+ }
48
+
44
49
  export interface PrinterContext<Data = any> {
45
50
  readonly options: PrintOptions<Data>;
46
51
  readonly source: string;
@@ -52,13 +57,23 @@ export interface PrinterContext<Data = any> {
52
57
  sourceEnd: number,
53
58
  data?: Data,
54
59
  ): void;
55
- writeNode(node: AST.Node | null | undefined): void;
60
+ writeNode(node: AST.Node | null | undefined, options?: WriteNodeOptions): void;
56
61
  writeNodeList(nodes: readonly (AST.Node | null)[], separator: string): void;
57
62
  writeExpressionListWithCommaSep(
58
63
  nodes: readonly (AST.Expression | AST.SpreadElement | null)[],
59
64
  ): void;
65
+ /**
66
+ * Write `nodes` with "newline" separator, but if for node either:
67
+ * - it is the first or the last node, ranged, and `listRange` is also provided,
68
+ * - two adjacent nodes are both ranged,
69
+ * Then the source text ranging between the two adjacent nodes/boundaries will be preserved
70
+ * instead of printing a newline character.
71
+ * @param nodes
72
+ * @param lastRangeEnd
73
+ */
60
74
  writeNodeListWithNewLineSep(
61
75
  nodes: readonly (AST.ProgramStatement | AST.ClassElement)[],
76
+ listRange?: SourceRange,
62
77
  ): void;
63
78
  writeSource(start: number, end: number, data?: Data): void;
64
79
  writePreservedNode(node: AST.Node): void;
package/src/printer.ts CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  interface InternalPrinterContext extends PrinterContext<any> {
23
23
  // make typescript happy about complex types
24
24
  options: any;
25
+ _skipLeadingComment: boolean;
25
26
  result(): PrintResult<any>;
26
27
  }
27
28
 
@@ -97,6 +98,7 @@ function createPrinterContext<Data>(
97
98
 
98
99
  const context: InternalPrinterContext = {
99
100
  options: options,
101
+ _skipLeadingComment: false,
100
102
  source: options.source,
101
103
  get generatedOffset() {
102
104
  return generatedOffset;
@@ -121,7 +123,7 @@ function createPrinterContext<Data>(
121
123
  data ?? getMappingData(null),
122
124
  );
123
125
  },
124
- writeNode(node) {
126
+ writeNode(node, writeOpt = {}) {
125
127
  if (!node) {
126
128
  return;
127
129
  }
@@ -150,10 +152,12 @@ function createPrinterContext<Data>(
150
152
  throw new Error(`No printer registered for node type ${node.type}`);
151
153
  }
152
154
 
153
- const leadingComments = options.getLeadingComments?.(node);
154
- if (leadingComments) {
155
- for (const comment of leadingComments) {
156
- writeComment(comment, context);
155
+ if (!writeOpt.noLeadingComment) {
156
+ const leadingComments = options.getLeadingComments?.(node);
157
+ if (leadingComments && !context._skipLeadingComment) {
158
+ for (const comment of leadingComments) {
159
+ writeComment(comment, context);
160
+ }
157
161
  }
158
162
  }
159
163
 
@@ -161,12 +165,15 @@ function createPrinterContext<Data>(
161
165
  printer(node, context);
162
166
  const generatedEnd = generatedOffset;
163
167
 
164
- const trailingComments = options.getTrailingComments?.(node);
165
- if (trailingComments) {
166
- for (const comment of trailingComments) {
167
- writeComment(comment, context);
168
+ if (!writeOpt.noTrailingComment) {
169
+ const trailingComments = options.getTrailingComments?.(node);
170
+ if (trailingComments) {
171
+ for (const comment of trailingComments) {
172
+ writeComment(comment, context);
173
+ }
168
174
  }
169
175
  }
176
+
170
177
  // If children nodes don't emit any mapping but the parent node itself
171
178
  // can produce mapping, add that mapping
172
179
  const lastMappingGeneratedEnd = mappings.at(-1)?.generatedEnd ?? 0;
@@ -220,41 +227,44 @@ function createPrinterContext<Data>(
220
227
  needsSeparator = true;
221
228
  }
222
229
  },
223
- writeNodeListWithNewLineSep(nodes) {
230
+ writeNodeListWithNewLineSep(nodes, parentRange) {
224
231
  let lastRangeEnd: number | undefined;
225
- let wroteNode = false;
226
-
227
- for (const node of nodes) {
228
- if (!node) {
229
- continue;
230
- }
231
-
232
+ let trailingSepEnd: number | undefined;
233
+ if (parentRange) {
234
+ lastRangeEnd = parentRange.start;
235
+ trailingSepEnd = parentRange.end;
236
+ }
237
+ for (let i = 0; i < nodes.length; i++) {
238
+ const node = nodes[i];
232
239
  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
- }
240
+ let noLeadingComment = false;
241
+ let noTrailingComment =
242
+ i < nodes.length - 1
243
+ ? true
244
+ : range !== undefined && parentRange !== undefined;
245
+ if (
246
+ lastRangeEnd !== undefined &&
247
+ range &&
248
+ range.start >= lastRangeEnd
249
+ ) {
250
+ context.writeSource(lastRangeEnd, range.start, getMappingData(null));
251
+ noLeadingComment = true;
252
+ } else if (i > 0) {
253
+ context.write("\n");
247
254
  }
248
-
249
- context.writeNode(node);
250
- wroteNode = true;
251
-
255
+ context.writeNode(node, {
256
+ noLeadingComment,
257
+ noTrailingComment,
258
+ });
252
259
  if (range) {
253
260
  lastRangeEnd = range.end;
254
261
  } else {
255
262
  lastRangeEnd = undefined;
256
263
  }
257
264
  }
265
+ if (trailingSepEnd !== undefined && lastRangeEnd !== undefined) {
266
+ context.writeSource(lastRangeEnd, trailingSepEnd, getMappingData(null));
267
+ }
258
268
  },
259
269
  writeSource(start, end, data) {
260
270
  if (end < start) {
package/src/printers.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import type { PrinterContext, Printers } from "./api.ts";
2
+ import type { SourceRange } from "./mappings.ts";
2
3
  import type { AST, AST_NODE_TYPES, Comment } from "./types.ts";
3
4
 
5
+ /**
6
+ * Precedence table for expressions.
7
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table
8
+ */
4
9
  const EXPRESSIONS_PRECEDENCE = {
5
10
  // LHS of =, must NOT parenthesized
6
11
  ArrayPattern: 20,
@@ -48,7 +53,9 @@ const EXPRESSIONS_PRECEDENCE = {
48
53
 
49
54
  // ranges from 13-5 depending on operator
50
55
  BinaryExpression: 13,
51
- // as/satisfies have same precedence as relational operators
56
+ // as/satisfies have same precedence as relational operators, e.g.
57
+ // `1 < 2 as number` is `(1 < 2) as number`, but
58
+ // `1 === 2 as number` is `1 === (2 as number)`
52
59
  TSAsExpression: 9,
53
60
  TSSatisfiesExpression: 9,
54
61
  // ranges from 4-3 depending on operator
@@ -426,7 +433,14 @@ export const defaultPrinters = {
426
433
  // JS – Statements
427
434
 
428
435
  function printProgram(program: AST.Program, context: PrinterContext): void {
429
- context.writeNodeListWithNewLineSep(program.body);
436
+ let listRange: SourceRange | undefined;
437
+ if (program.range) {
438
+ listRange = {
439
+ start: program.range[0],
440
+ end: program.range[1],
441
+ };
442
+ }
443
+ context.writeNodeListWithNewLineSep(program.body, listRange);
430
444
  }
431
445
 
432
446
  function canStartExpressionStatement(
@@ -518,9 +532,18 @@ function printVariableDeclarator(
518
532
  declarator: AST.VariableDeclarator,
519
533
  context: PrinterContext,
520
534
  ): void {
521
- context.writeNode(declarator.id);
522
535
  if (declarator.definite === true) {
536
+ context.write(String((declarator.id as AST.Identifier).name));
523
537
  context.write("!");
538
+ writeOptionalTypeAnnotation(
539
+ declarator.id as unknown as {
540
+ optional?: boolean;
541
+ typeAnnotation?: AST.Node | null;
542
+ },
543
+ context,
544
+ );
545
+ } else {
546
+ context.writeNode(declarator.id);
524
547
  }
525
548
  if (declarator.init) {
526
549
  context.write(" = ");
@@ -542,9 +565,14 @@ function printBlockStatement(
542
565
  const body = block.body;
543
566
  context.write("{");
544
567
  if (body.length > 0) {
545
- context.write("\n");
546
- context.writeNodeListWithNewLineSep(body);
547
- context.write("\n");
568
+ let listRange: SourceRange | undefined;
569
+ if (block.range) {
570
+ listRange = {
571
+ start: block.range[0] + 1,
572
+ end: block.range[1] - 1,
573
+ };
574
+ }
575
+ context.writeNodeListWithNewLineSep(body, listRange);
548
576
  }
549
577
  context.write("}");
550
578
  }
@@ -1457,9 +1485,14 @@ function printClassBody(node: AST.ClassBody, context: PrinterContext): void {
1457
1485
  context.write("{");
1458
1486
  const body = node.body;
1459
1487
  if (body.length > 0) {
1460
- context.write("\n");
1461
- context.writeNodeListWithNewLineSep(body);
1462
- context.write("\n");
1488
+ let listRange: SourceRange | undefined;
1489
+ if (node.range) {
1490
+ listRange = {
1491
+ start: node.range[0] + 1,
1492
+ end: node.range[1] - 1,
1493
+ };
1494
+ }
1495
+ context.writeNodeListWithNewLineSep(body, listRange);
1463
1496
  }
1464
1497
  context.write("}");
1465
1498
  }
@@ -1471,9 +1504,14 @@ function printStaticBlock(
1471
1504
  context.write("static {");
1472
1505
  const body = node.body;
1473
1506
  if (body.length > 0) {
1474
- context.write("\n");
1475
- context.writeNodeListWithNewLineSep(body);
1476
- context.write("\n");
1507
+ let listRange: SourceRange | undefined;
1508
+ if (node.range) {
1509
+ listRange = {
1510
+ start: node.range[0] + 1,
1511
+ end: node.range[1] - 1,
1512
+ };
1513
+ }
1514
+ context.writeNodeListWithNewLineSep(body, listRange);
1477
1515
  }
1478
1516
  context.write("}");
1479
1517
  }
@@ -2512,9 +2550,16 @@ function printTSModuleBlock(
2512
2550
  node: AST.TSModuleBlock,
2513
2551
  context: PrinterContext,
2514
2552
  ): void {
2515
- context.write("{\n");
2516
- context.writeNodeListWithNewLineSep(node.body);
2517
- context.write("\n}");
2553
+ context.write("{");
2554
+ let listRange: SourceRange | undefined;
2555
+ if (node.range) {
2556
+ listRange = {
2557
+ start: node.range[0] + 1,
2558
+ end: node.range[1] - 1,
2559
+ };
2560
+ }
2561
+ context.writeNodeListWithNewLineSep(node.body, listRange);
2562
+ context.write("}");
2518
2563
  }
2519
2564
 
2520
2565
  function printTSDeclareFunction(