esrap 2.2.8 → 2.2.10

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.8",
3
+ "version": "2.2.10",
4
4
  "description": "Parse in reverse",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,6 @@ export const EXPRESSIONS_PRECEDENCE = {
25
25
  ImportExpression: 19,
26
26
  NewExpression: 19,
27
27
  Literal: 18,
28
- TSSatisfiesExpression: 18,
29
28
  TSInstantiationExpression: 18,
30
29
  TSNonNullExpression: 18,
31
30
  TSTypeAssertion: 18,
@@ -33,11 +32,13 @@ export const EXPRESSIONS_PRECEDENCE = {
33
32
  ClassExpression: 17,
34
33
  FunctionExpression: 17,
35
34
  ObjectExpression: 17,
36
- TSAsExpression: 16,
37
35
  UpdateExpression: 16,
38
36
  UnaryExpression: 15,
39
37
  BinaryExpression: 14,
40
- LogicalExpression: 13,
38
+ // `as`/`satisfies` sit between binary and logical operators
39
+ TSAsExpression: 13,
40
+ TSSatisfiesExpression: 13,
41
+ LogicalExpression: 12,
41
42
  ConditionalExpression: 4,
42
43
  ArrowFunctionExpression: 3,
43
44
  AssignmentExpression: 3,
@@ -360,7 +361,7 @@ export default (options = {}) => {
360
361
  * @param {{ line: number, column: number }} until
361
362
  * @param {boolean} pad
362
363
  */
363
- function sequence(context, nodes, until, pad, separator = ',') {
364
+ function sequence(context, nodes, until, pad, separator = ',', trailing_newline = true) {
364
365
  let multiline = false;
365
366
  let length = -1;
366
367
 
@@ -426,7 +427,7 @@ export default (options = {}) => {
426
427
 
427
428
  if (multiline) {
428
429
  context.dedent();
429
- context.newline();
430
+ if (trailing_newline) context.newline();
430
431
  } else if (pad && length > 0) {
431
432
  context.write(' ');
432
433
  }
@@ -1010,12 +1011,7 @@ export default (options = {}) => {
1010
1011
 
1011
1012
  context.write(' => ');
1012
1013
 
1013
- if (
1014
- node.body.type === 'ObjectExpression' ||
1015
- (node.body.type === 'AssignmentExpression' && node.body.left.type === 'ObjectPattern') ||
1016
- (node.body.type === 'LogicalExpression' && node.body.left.type === 'ObjectExpression') ||
1017
- (node.body.type === 'ConditionalExpression' && node.body.test.type === 'ObjectExpression')
1018
- ) {
1014
+ if (arrow_concise_body_needs_wrap(node.body)) {
1019
1015
  context.write('(');
1020
1016
  context.visit(node.body);
1021
1017
  context.write(')');
@@ -2111,11 +2107,13 @@ export default (options = {}) => {
2111
2107
  },
2112
2108
 
2113
2109
  TSUnionType(node, context) {
2114
- sequence(context, node.types, node.loc?.end ?? null, false, ' |');
2110
+ // no trailing newline, so a following `=>` stays on the same line
2111
+ sequence(context, node.types, node.loc?.end ?? null, false, ' |', false);
2115
2112
  },
2116
2113
 
2117
2114
  TSIntersectionType(node, context) {
2118
- sequence(context, node.types, node.loc?.end ?? null, false, ' &');
2115
+ // no trailing newline, so a following `=>` stays on the same line
2116
+ sequence(context, node.types, node.loc?.end ?? null, false, ' &', false);
2119
2117
  },
2120
2118
 
2121
2119
  TSInferType(node, context) {
@@ -2246,7 +2244,16 @@ export default (options = {}) => {
2246
2244
  },
2247
2245
 
2248
2246
  TSNonNullExpression(node, context) {
2249
- context.visit(node.expression);
2247
+ // operator expressions can't take a postfix `!` directly: `(0 as number)!`, `(await x)!`
2248
+ if (
2249
+ EXPRESSIONS_PRECEDENCE[node.expression.type] < EXPRESSIONS_PRECEDENCE.TSNonNullExpression
2250
+ ) {
2251
+ context.write('(');
2252
+ context.visit(node.expression);
2253
+ context.write(')');
2254
+ } else {
2255
+ context.visit(node.expression);
2256
+ }
2250
2257
  context.write('!');
2251
2258
  },
2252
2259
 
@@ -2322,6 +2329,34 @@ export default (options = {}) => {
2322
2329
 
2323
2330
  /** @satisfies {Visitors} */
2324
2331
 
2332
+ /**
2333
+ * Arrow functions with a concise body must wrap certain expressions in parentheses,
2334
+ * otherwise `{` can start a block statement instead of an object literal (`as` /
2335
+ * `satisfies` / `!` do not change that (e.g. `() => { x } as const`).
2336
+ * @param {TSESTree.BlockStatement | TSESTree.Expression} body
2337
+ * @returns {boolean}
2338
+ */
2339
+ function arrow_concise_body_needs_wrap(body) {
2340
+ if (body.type === 'BlockStatement') return false;
2341
+
2342
+ switch (body.type) {
2343
+ case 'ObjectExpression':
2344
+ return true;
2345
+ case 'AssignmentExpression':
2346
+ return body.left.type === 'ObjectPattern';
2347
+ case 'LogicalExpression':
2348
+ return body.left.type === 'ObjectExpression';
2349
+ case 'ConditionalExpression':
2350
+ return body.test.type === 'ObjectExpression';
2351
+ case 'TSAsExpression':
2352
+ case 'TSSatisfiesExpression':
2353
+ case 'TSNonNullExpression':
2354
+ return body.expression ? arrow_concise_body_needs_wrap(body.expression) : false;
2355
+ default:
2356
+ return false;
2357
+ }
2358
+ }
2359
+
2325
2360
  /**
2326
2361
  *
2327
2362
  * @param {TSESTree.Expression | TSESTree.PrivateIdentifier} node
@@ -2332,6 +2367,11 @@ export default (options = {}) => {
2332
2367
  function needs_parens(node, parent, is_right) {
2333
2368
  if (node.type === 'PrivateIdentifier') return false;
2334
2369
 
2370
+ if (!is_right && (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression')) {
2371
+ // `**` would be invalid, `&`/`|` would be swallowed into the trailing type
2372
+ return parent.operator === '**' || parent.operator === '&' || parent.operator === '|';
2373
+ }
2374
+
2335
2375
  // special case where logical expressions and coalesce expressions cannot be mixed,
2336
2376
  // either of them need to be wrapped with parentheses
2337
2377
  if (