esrap 2.2.13 → 2.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esrap",
3
- "version": "2.2.13",
3
+ "version": "2.3.0",
4
4
  "description": "Parse in reverse",
5
5
  "repository": {
6
6
  "type": "git",
@@ -484,20 +484,50 @@ export default (options = {}) => {
484
484
  }
485
485
  }
486
486
 
487
+ const boundary_tokens = options.boundaryTokens === true;
488
+
489
+ /**
490
+ * A one-character synthetic token node so structural tokens (`(`, `[`, `{`,
491
+ * unary operators, …) written where a node's SOURCE span begins or ends get a
492
+ * sourcemap anchor. Without it, everything up to the next mapped token is
493
+ * attributed to the PREVIOUS token's source position — `write(content, node)`
494
+ * only maps tokens written with a node, and these boundary characters belong
495
+ * to no written token. Opt-in (`boundaryTokens`): denser maps, byte-identical
496
+ * output.
497
+ * @param {{ line: number, column: number } | undefined} pos
498
+ * @param {number} [length]
499
+ */
500
+ function token_at(pos, length = 1) {
501
+ if (!boundary_tokens) return undefined;
502
+ if (!pos) return undefined;
503
+ return /** @type {any} */ ({
504
+ loc: { start: pos, end: { line: pos.line, column: pos.column + length } }
505
+ });
506
+ }
507
+
508
+ /** @param {{ line: number, column: number } | undefined} pos */
509
+ function token_before(pos) {
510
+ if (!boundary_tokens) return undefined;
511
+ if (!pos || pos.column === 0) return undefined;
512
+ return /** @type {any} */ ({
513
+ loc: { start: { line: pos.line, column: pos.column - 1 }, end: pos }
514
+ });
515
+ }
516
+
487
517
  const shared = {
488
518
  /**
489
519
  * @param {TSESTree.ArrayExpression | TSESTree.ArrayPattern} node
490
520
  * @param {Context} context
491
521
  */
492
522
  'ArrayExpression|ArrayPattern': (node, context) => {
493
- context.write('[');
523
+ context.write('[', token_at(node.loc?.start));
494
524
  sequence(
495
525
  context,
496
526
  /** @type {TSESTree.Node[]} */ (node.elements),
497
527
  node.loc?.end ?? null,
498
528
  false
499
529
  );
500
- context.write(']');
530
+ context.write(']', token_before(node.loc?.end));
501
531
  },
502
532
 
503
533
  /**
@@ -631,7 +661,7 @@ export default (options = {}) => {
631
661
  join.write(' ');
632
662
  }
633
663
 
634
- context.write(')');
664
+ context.write(')', token_before(node.loc?.end));
635
665
  },
636
666
 
637
667
  /**
@@ -804,9 +834,9 @@ export default (options = {}) => {
804
834
 
805
835
  if (node.value.generator) context.write('*');
806
836
 
807
- if (node.computed) context.write('[');
837
+ if (node.computed) context.write('[', token_before(node.key.loc?.start));
808
838
  context.visit(node.key);
809
- if (node.computed) context.write(']');
839
+ if (node.computed) context.write(']', token_at(node.key.loc?.end));
810
840
 
811
841
  // optional method (`m?()`)
812
842
  if (node.optional) context.write('?');
@@ -879,9 +909,9 @@ export default (options = {}) => {
879
909
  }
880
910
 
881
911
  if (node.computed) {
882
- context.write('[');
912
+ context.write('[', token_before(node.key.loc?.start));
883
913
  context.visit(node.key);
884
- context.write(']');
914
+ context.write(']', token_at(node.key.loc?.end));
885
915
  } else {
886
916
  context.visit(node.key);
887
917
  }
@@ -1446,7 +1476,7 @@ export default (options = {}) => {
1446
1476
  }
1447
1477
  context.write('[');
1448
1478
  context.visit(node.property);
1449
- context.write(']');
1479
+ context.write(']', token_before(node.loc?.end));
1450
1480
  } else {
1451
1481
  context.write(node.optional ? '?.' : '.');
1452
1482
  context.visit(node.property);
@@ -1464,22 +1494,28 @@ export default (options = {}) => {
1464
1494
  NewExpression: shared['CallExpression|NewExpression'],
1465
1495
 
1466
1496
  ObjectExpression(node, context) {
1467
- context.write('{');
1497
+ context.write('{', token_at(node.loc?.start));
1468
1498
  sequence(context, node.properties, node.loc?.end ?? null, true);
1469
- context.write('}');
1499
+ context.write('}', token_before(node.loc?.end));
1470
1500
  },
1471
1501
 
1472
1502
  ObjectPattern(node, context) {
1473
- context.write('{');
1503
+ context.write('{', token_at(node.loc?.start));
1474
1504
  sequence(context, node.properties, node.loc?.end ?? null, true);
1475
- context.write('}');
1505
+ context.write('}', token_before(node.loc?.end));
1476
1506
 
1477
1507
  if (node.typeAnnotation) context.visit(node.typeAnnotation);
1478
1508
  },
1479
1509
 
1480
1510
  // @ts-expect-error this isn't a real node type, but Acorn produces it
1481
1511
  ParenthesizedExpression(node, context) {
1482
- maybe_wrap(context, node.expression, true);
1512
+ if (node.loc) {
1513
+ context.write('(', token_at(node.loc.start));
1514
+ context.visit(node.expression);
1515
+ context.write(')', token_before(node.loc.end));
1516
+ } else {
1517
+ maybe_wrap(context, node.expression, true);
1518
+ }
1483
1519
  },
1484
1520
 
1485
1521
  PrivateIdentifier(node, context) {
@@ -1513,9 +1549,9 @@ export default (options = {}) => {
1513
1549
  if (node.kind !== 'init') kw(node.kind + ' ');
1514
1550
  if (node.value.async) kw('async ');
1515
1551
  if (node.value.generator) context.write('*');
1516
- if (node.computed) context.write('[');
1552
+ if (node.computed) context.write('[', token_before(node.key.loc?.start));
1517
1553
  context.visit(node.key);
1518
- if (node.computed) context.write(']');
1554
+ if (node.computed) context.write(']', token_at(node.key.loc?.end));
1519
1555
  context.write('(');
1520
1556
  sequence(
1521
1557
  context,
@@ -1530,12 +1566,17 @@ export default (options = {}) => {
1530
1566
  context.write(' ');
1531
1567
  context.visit(node.value.body);
1532
1568
  } else {
1533
- if (node.computed) context.write('[');
1569
+ if (node.computed) context.write('[', token_before(node.key.loc?.start));
1534
1570
  if (node.kind === 'get' || node.kind === 'set') {
1535
1571
  write_keyword(context, node, node.kind, ' ');
1536
1572
  }
1537
1573
  context.visit(node.key);
1538
- context.write(node.computed ? ']: ' : ': ');
1574
+ if (node.computed) {
1575
+ context.write(']', token_at(node.key.loc?.end));
1576
+ context.write(': ');
1577
+ } else {
1578
+ context.write(': ');
1579
+ }
1539
1580
  context.visit(node.value);
1540
1581
  }
1541
1582
  },
@@ -1702,7 +1743,7 @@ export default (options = {}) => {
1702
1743
  },
1703
1744
 
1704
1745
  UnaryExpression(node, context) {
1705
- context.write(node.operator);
1746
+ context.write(node.operator, token_at(node.loc?.start, node.operator.length));
1706
1747
 
1707
1748
  if (node.operator.length > 1) {
1708
1749
  context.write(' ');
@@ -1886,9 +1927,9 @@ export default (options = {}) => {
1886
1927
  },
1887
1928
 
1888
1929
  TSPropertySignature(node, context) {
1889
- if (node.computed) context.write('[');
1930
+ if (node.computed) context.write('[', token_before(node.key.loc?.start));
1890
1931
  context.visit(node.key);
1891
- if (node.computed) context.write(']');
1932
+ if (node.computed) context.write(']', token_at(node.key.loc?.end));
1892
1933
  if (node.optional) context.write('?');
1893
1934
  if (node.typeAnnotation) context.visit(node.typeAnnotation);
1894
1935
  },
@@ -2094,9 +2135,9 @@ export default (options = {}) => {
2094
2135
  },
2095
2136
 
2096
2137
  TSMethodSignature(node, context) {
2097
- if (node.computed) context.write('[');
2138
+ if (node.computed) context.write('[', token_before(node.key.loc?.start));
2098
2139
  context.visit(node.key);
2099
- if (node.computed) context.write(']');
2140
+ if (node.computed) context.write(']', token_at(node.key.loc?.end));
2100
2141
 
2101
2142
  context.write('(');
2102
2143
  sequence(
@@ -2,6 +2,14 @@ import type { BaseNode } from '../types';
2
2
 
3
3
  export type TSOptions = {
4
4
  quotes?: 'double' | 'single';
5
+ /**
6
+ * Anchor structural tokens (array/object brackets and braces, preserved
7
+ * parentheses, unary operators, and the closing tokens of calls and
8
+ * computed member access) with one-character source locations, so
9
+ * node-boundary positions always resolve through the source map instead of
10
+ * being attributed to the previous token. Denser maps; identical output.
11
+ */
12
+ boundaryTokens?: boolean;
5
13
  comments?: Comment[];
6
14
  getLeadingComments?: (node: BaseNode) => BaseComment[] | undefined;
7
15
  getTrailingComments?: (node: BaseNode) => BaseComment[] | undefined;
package/types/index.d.ts CHANGED
@@ -95,6 +95,14 @@ declare module 'esrap/languages/ts' {
95
95
  : SpecialisedVisitors<T> & { _?: (node: T, context: Context, visit: (node: T) => void) => void };
96
96
  type TSOptions = {
97
97
  quotes?: 'double' | 'single';
98
+ /**
99
+ * Anchor structural tokens (array/object brackets and braces, preserved
100
+ * parentheses, unary operators, and the closing tokens of calls and
101
+ * computed member access) with one-character source locations, so
102
+ * node-boundary positions always resolve through the source map instead of
103
+ * being attributed to the previous token. Denser maps; identical output.
104
+ */
105
+ boundaryTokens?: boolean;
98
106
  comments?: Comment[];
99
107
  getLeadingComments?: (node: BaseNode) => BaseComment[] | undefined;
100
108
  getTrailingComments?: (node: BaseNode) => BaseComment[] | undefined;
@@ -149,6 +157,14 @@ declare module 'esrap/languages/tsx' {
149
157
  : SpecialisedVisitors<T> & { _?: (node: T, context: Context, visit: (node: T) => void) => void };
150
158
  type TSOptions = {
151
159
  quotes?: 'double' | 'single';
160
+ /**
161
+ * Anchor structural tokens (array/object brackets and braces, preserved
162
+ * parentheses, unary operators, and the closing tokens of calls and
163
+ * computed member access) with one-character source locations, so
164
+ * node-boundary positions always resolve through the source map instead of
165
+ * being attributed to the previous token. Denser maps; identical output.
166
+ */
167
+ boundaryTokens?: boolean;
152
168
  comments?: Comment[];
153
169
  getLeadingComments?: (node: BaseNode) => BaseComment[] | undefined;
154
170
  getTrailingComments?: (node: BaseNode) => BaseComment[] | undefined;
@@ -37,6 +37,6 @@
37
37
  null,
38
38
  null
39
39
  ],
40
- "mappings": ";MAEYA,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;aAEPC,QAAQA;;;;WAWHC,QAAQA;;;;;;MAWbC,OAAOA;;kBAEFC,YAAYA;;;;;;;;;iBCWbC,KAAKA;;;;cC7CRC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aCLRC,IAAIA;yBACQC,EAAEA;;cAELC,sBAAsBA;MHJ/BZ,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;MAEPC,QAAQA;;;MIhBRS,SAASA;;;;;;;WAOXC,QAAQA;;;;;;;kBAODC,WAAWA;;;;;;;kBAOXC,OAAOA;;;;;;;;;;;;aCpBZN,IAAIA;yBACQO,GAAGA;MLFfjB,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;MAEPC,QAAQA;;;MIhBRS,SAASA;;;;;;;WAOXC,QAAQA;;;;;;;kBAODC,WAAWA;;;;;;;kBAOXC,OAAOA",
40
+ "mappings": ";MAEYA,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;aAEPC,QAAQA;;;;WAWHC,QAAQA;;;;;;MAWbC,OAAOA;;kBAEFC,YAAYA;;;;;;;;;iBCWbC,KAAKA;;;;cC7CRC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aCLRC,IAAIA;yBACQC,EAAEA;;cAELC,sBAAsBA;MHJ/BZ,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;MAEPC,QAAQA;;;MIhBRS,SAASA;;;;;;;;;;;;;;;WAeXC,QAAQA;;;;;;;kBAODC,WAAWA;;;;;;;kBAOXC,OAAOA;;;;;;;;;;;;aC5BZN,IAAIA;yBACQO,GAAGA;MLFfjB,QAAQA;;;;;;;;MAQfC,MAAMA;;MAENC,mBAAmBA;;;;MAIZC,OAAOA;;MAEPC,QAAQA;;;MIhBRS,SAASA;;;;;;;;;;;;;;;WAeXC,QAAQA;;;;;;;kBAODC,WAAWA;;;;;;;kBAOXC,OAAOA",
41
41
  "ignoreList": []
42
42
  }