@openrewrite/rewrite 8.91.2 → 8.91.3

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.
Files changed (124) hide show
  1. package/dist/execution.d.ts +8 -0
  2. package/dist/execution.d.ts.map +1 -1
  3. package/dist/execution.js +31 -0
  4. package/dist/execution.js.map +1 -1
  5. package/dist/javascript/add-import.d.ts +64 -9
  6. package/dist/javascript/add-import.d.ts.map +1 -1
  7. package/dist/javascript/add-import.js +398 -71
  8. package/dist/javascript/add-import.js.map +1 -1
  9. package/dist/javascript/amd.d.ts +11 -1
  10. package/dist/javascript/amd.d.ts.map +1 -1
  11. package/dist/javascript/amd.js +43 -9
  12. package/dist/javascript/amd.js.map +1 -1
  13. package/dist/javascript/binding.d.ts +10 -2
  14. package/dist/javascript/binding.d.ts.map +1 -1
  15. package/dist/javascript/binding.js +44 -71
  16. package/dist/javascript/binding.js.map +1 -1
  17. package/dist/javascript/comparator.d.ts +2 -0
  18. package/dist/javascript/comparator.d.ts.map +1 -1
  19. package/dist/javascript/comparator.js +5 -1
  20. package/dist/javascript/comparator.js.map +1 -1
  21. package/dist/javascript/format/format.d.ts +5 -2
  22. package/dist/javascript/format/format.d.ts.map +1 -1
  23. package/dist/javascript/format/format.js +51 -11
  24. package/dist/javascript/format/format.js.map +1 -1
  25. package/dist/javascript/format/minimum-viable-spacing-visitor.d.ts +19 -0
  26. package/dist/javascript/format/minimum-viable-spacing-visitor.d.ts.map +1 -1
  27. package/dist/javascript/format/minimum-viable-spacing-visitor.js +180 -7
  28. package/dist/javascript/format/minimum-viable-spacing-visitor.js.map +1 -1
  29. package/dist/javascript/format/prettier-format.d.ts +2 -1
  30. package/dist/javascript/format/prettier-format.d.ts.map +1 -1
  31. package/dist/javascript/format/prettier-format.js +33 -23
  32. package/dist/javascript/format/prettier-format.js.map +1 -1
  33. package/dist/javascript/index.d.ts +3 -3
  34. package/dist/javascript/index.d.ts.map +1 -1
  35. package/dist/javascript/index.js +21 -1
  36. package/dist/javascript/index.js.map +1 -1
  37. package/dist/javascript/parser-utils.d.ts.map +1 -1
  38. package/dist/javascript/parser-utils.js +5 -9
  39. package/dist/javascript/parser-utils.js.map +1 -1
  40. package/dist/javascript/parser.d.ts.map +1 -1
  41. package/dist/javascript/parser.js +48 -3
  42. package/dist/javascript/parser.js.map +1 -1
  43. package/dist/javascript/print.d.ts +1 -0
  44. package/dist/javascript/print.d.ts.map +1 -1
  45. package/dist/javascript/print.js +9 -0
  46. package/dist/javascript/print.js.map +1 -1
  47. package/dist/javascript/recipes/change-import.d.ts +6 -3
  48. package/dist/javascript/recipes/change-import.d.ts.map +1 -1
  49. package/dist/javascript/recipes/change-import.js +17 -277
  50. package/dist/javascript/recipes/change-import.js.map +1 -1
  51. package/dist/javascript/rpc.js +2 -0
  52. package/dist/javascript/rpc.js.map +1 -1
  53. package/dist/javascript/scope.d.ts +40 -7
  54. package/dist/javascript/scope.d.ts.map +1 -1
  55. package/dist/javascript/scope.js +182 -30
  56. package/dist/javascript/scope.js.map +1 -1
  57. package/dist/javascript/templating/bindings.js +2 -20
  58. package/dist/javascript/templating/bindings.js.map +1 -1
  59. package/dist/javascript/templating/comparator.d.ts +2 -0
  60. package/dist/javascript/templating/comparator.d.ts.map +1 -1
  61. package/dist/javascript/templating/comparator.js +25 -0
  62. package/dist/javascript/templating/comparator.js.map +1 -1
  63. package/dist/javascript/templating/engine.d.ts +5 -0
  64. package/dist/javascript/templating/engine.d.ts.map +1 -1
  65. package/dist/javascript/templating/engine.js +30 -12
  66. package/dist/javascript/templating/engine.js.map +1 -1
  67. package/dist/javascript/templating/template.d.ts.map +1 -1
  68. package/dist/javascript/templating/template.js +6 -0
  69. package/dist/javascript/templating/template.js.map +1 -1
  70. package/dist/javascript/templating/types.d.ts +5 -0
  71. package/dist/javascript/templating/types.d.ts.map +1 -1
  72. package/dist/javascript/tree.d.ts +1 -0
  73. package/dist/javascript/tree.d.ts.map +1 -1
  74. package/dist/javascript/tree.js.map +1 -1
  75. package/dist/javascript/type-mapping.d.ts.map +1 -1
  76. package/dist/javascript/type-mapping.js +14 -0
  77. package/dist/javascript/type-mapping.js.map +1 -1
  78. package/dist/javascript/visitor.d.ts.map +1 -1
  79. package/dist/javascript/visitor.js +1 -0
  80. package/dist/javascript/visitor.js.map +1 -1
  81. package/dist/parser.d.ts +6 -0
  82. package/dist/parser.d.ts.map +1 -1
  83. package/dist/parser.js +23 -1
  84. package/dist/parser.js.map +1 -1
  85. package/dist/print.d.ts.map +1 -1
  86. package/dist/print.js +5 -2
  87. package/dist/print.js.map +1 -1
  88. package/dist/rewrite-javascript-version.txt +1 -1
  89. package/dist/rpc/request/parse-project.d.ts +11 -1
  90. package/dist/rpc/request/parse-project.d.ts.map +1 -1
  91. package/dist/rpc/request/parse-project.js +7 -2
  92. package/dist/rpc/request/parse-project.js.map +1 -1
  93. package/dist/rpc/request/parse.d.ts +13 -1
  94. package/dist/rpc/request/parse.d.ts.map +1 -1
  95. package/dist/rpc/request/parse.js +8 -2
  96. package/dist/rpc/request/parse.js.map +1 -1
  97. package/package.json +1 -1
  98. package/src/execution.ts +23 -0
  99. package/src/javascript/add-import.ts +449 -78
  100. package/src/javascript/amd.ts +49 -10
  101. package/src/javascript/binding.ts +60 -74
  102. package/src/javascript/comparator.ts +6 -1
  103. package/src/javascript/format/format.ts +56 -12
  104. package/src/javascript/format/minimum-viable-spacing-visitor.ts +196 -7
  105. package/src/javascript/format/prettier-format.ts +35 -25
  106. package/src/javascript/index.ts +10 -3
  107. package/src/javascript/parser-utils.ts +5 -11
  108. package/src/javascript/parser.ts +65 -6
  109. package/src/javascript/print.ts +10 -0
  110. package/src/javascript/recipes/change-import.ts +19 -349
  111. package/src/javascript/rpc.ts +2 -0
  112. package/src/javascript/scope.ts +210 -31
  113. package/src/javascript/templating/bindings.ts +2 -25
  114. package/src/javascript/templating/comparator.ts +29 -2
  115. package/src/javascript/templating/engine.ts +41 -14
  116. package/src/javascript/templating/template.ts +6 -0
  117. package/src/javascript/templating/types.ts +6 -0
  118. package/src/javascript/tree.ts +1 -0
  119. package/src/javascript/type-mapping.ts +15 -0
  120. package/src/javascript/visitor.ts +1 -0
  121. package/src/parser.ts +24 -1
  122. package/src/print.ts +5 -2
  123. package/src/rpc/request/parse-project.ts +6 -2
  124. package/src/rpc/request/parse.ts +7 -2
@@ -86,9 +86,10 @@ export class MinimumViableSpacingVisitor<P> extends JavaScriptVisitor<P> {
86
86
  // e.g., "class DataTable<Row>" not "class DataTable <Row>"
87
87
  // Note: body.prefix spacing (space before '{') is handled by SpacesVisitor, not here.
88
88
 
89
- if (c.extends && c.extends.before.whitespace === "") {
89
+ if (c.extends) {
90
90
  c = produce(c, draft => {
91
91
  this.ensureSpace(draft.extends!.before);
92
+ this.ensureSpace(draft.extends!.element.prefix);
92
93
  });
93
94
  }
94
95
 
@@ -156,10 +157,9 @@ export class MinimumViableSpacingVisitor<P> extends JavaScriptVisitor<P> {
156
157
  protected async visitNewClass(newClass: J.NewClass, p: P): Promise<J | undefined> {
157
158
  const ret = await super.visitNewClass(newClass, p) as J.NewClass;
158
159
  return produce(ret, draft => {
159
- if (draft.class) {
160
- if (draft.class.kind == J.Kind.Identifier) {
161
- this.ensureSpace((draft.class as Draft<J.Identifier>).prefix);
162
- }
160
+ // a parenthesized class expression separates itself from `new`
161
+ if (draft.class && draft.class.kind !== J.Kind.Parentheses) {
162
+ this.ensureSpace((draft.class as Draft<J>).prefix);
163
163
  }
164
164
  });
165
165
  }
@@ -232,12 +232,54 @@ export class MinimumViableSpacingVisitor<P> extends JavaScriptVisitor<P> {
232
232
  return ret;
233
233
  }
234
234
 
235
+ protected async visitAlias(alias: JS.Alias, p: P): Promise<J | undefined> {
236
+ const ret = await super.visitAlias(alias, p) as JS.Alias;
237
+ return produce(ret, draft => {
238
+ this.ensureSpace(draft.propertyName.after);
239
+ this.ensureSpace(draft.alias.prefix);
240
+ });
241
+ }
242
+
243
+ protected async visitAs(as_: JS.As, p: P): Promise<J | undefined> {
244
+ const ret = await super.visitAs(as_, p) as JS.As;
245
+ return produce(ret, draft => {
246
+ this.ensureSpace(draft.left.after);
247
+ this.ensureSpace(draft.right.prefix);
248
+ });
249
+ }
250
+
251
+ protected async visitBinaryExtensions(jsBinary: JS.Binary, p: P): Promise<J | undefined> {
252
+ const ret = await super.visitBinaryExtensions(jsBinary, p) as JS.Binary;
253
+ // `in` is the only word operator here; the rest are punctuators that need no separation
254
+ if (ret.operator.element !== JS.Binary.Type.In) {
255
+ return ret;
256
+ }
257
+ return produce(ret, draft => {
258
+ this.ensureSpace(draft.operator.before);
259
+ this.ensureSpace(draft.right.prefix);
260
+ });
261
+ }
262
+
263
+ protected async visitBreak(breakNode: J.Break, p: P): Promise<J | undefined> {
264
+ const ret = await super.visitBreak(breakNode, p) as J.Break;
265
+ return ret.label ? produce(ret, draft => {
266
+ this.ensureSpace(draft.label!.prefix);
267
+ }) : ret;
268
+ }
235
269
 
236
270
  protected async visitCase(caseNode: J.Case, p: P): Promise<J | undefined> {
237
- const c = await super.visitCase(caseNode, p) as J.Case;
271
+ let c = await super.visitCase(caseNode, p) as J.Case;
272
+
273
+ // `default` prints without the `case` keyword, so its label needs no separator
274
+ const first = c.caseLabels.elements[0]?.element;
275
+ if (first && (first.kind !== J.Kind.Identifier || (first as J.Identifier).simpleName !== "default")) {
276
+ c = produce(c, draft => {
277
+ this.ensureSpace(draft.caseLabels.before);
278
+ });
279
+ }
238
280
 
239
281
  if (c.guard && c.caseLabels.elements.length > 0 && c.caseLabels.elements[c.caseLabels.elements.length - 1].after.whitespace === "") {
240
- return produce(c, draft => {
282
+ c = produce(c, draft => {
241
283
  const last = draft.caseLabels.elements.length - 1;
242
284
  draft.caseLabels.elements[last].after.whitespace = " ";
243
285
  });
@@ -246,6 +288,153 @@ export class MinimumViableSpacingVisitor<P> extends JavaScriptVisitor<P> {
246
288
  return c;
247
289
  }
248
290
 
291
+ protected async visitContinue(continueNode: J.Continue, p: P): Promise<J | undefined> {
292
+ const ret = await super.visitContinue(continueNode, p) as J.Continue;
293
+ return ret.label ? produce(ret, draft => {
294
+ this.ensureSpace(draft.label!.prefix);
295
+ }) : ret;
296
+ }
297
+
298
+ protected async visitDelete(delete_: JS.Delete, p: P): Promise<J | undefined> {
299
+ const ret = await super.visitDelete(delete_, p) as JS.Delete;
300
+ return produce(ret, draft => {
301
+ this.ensureSpace(draft.expression.prefix);
302
+ });
303
+ }
304
+
305
+ protected async visitExportAssignment(exportAssignment: JS.ExportAssignment, p: P): Promise<J | undefined> {
306
+ const ret = await super.visitExportAssignment(exportAssignment, p) as JS.ExportAssignment;
307
+ if (ret.exportEquals) {
308
+ return ret;
309
+ }
310
+ return produce(ret, draft => {
311
+ this.ensureSpace(draft.expression.before);
312
+ this.ensureSpace(draft.expression.element.prefix);
313
+ });
314
+ }
315
+
316
+ protected async visitExportDeclaration(exportDeclaration: JS.ExportDeclaration, p: P): Promise<J | undefined> {
317
+ const ret = await super.visitExportDeclaration(exportDeclaration, p) as JS.ExportDeclaration;
318
+ return produce(ret, draft => {
319
+ if (draft.typeOnly.element) {
320
+ this.ensureSpace(draft.typeOnly.before);
321
+ }
322
+ // a namespace re-export ends its clause with the alias, unlike `}` or a bare `*`
323
+ if (draft.moduleSpecifier && draft.exportClause?.kind === JS.Kind.Alias) {
324
+ this.ensureSpace(draft.moduleSpecifier.before);
325
+ }
326
+ });
327
+ }
328
+
329
+ protected async visitForInLoop(forInLoop: JS.ForInLoop, p: P): Promise<J | undefined> {
330
+ const ret = await super.visitForInLoop(forInLoop, p) as JS.ForInLoop;
331
+ return produce(ret, draft => {
332
+ this.ensureSpace(draft.control.variable.after);
333
+ this.ensureSpace(draft.control.iterable.element.prefix);
334
+ });
335
+ }
336
+
337
+ protected async visitForOfLoop(forOfLoop: JS.ForOfLoop, p: P): Promise<J | undefined> {
338
+ const ret = await super.visitForOfLoop(forOfLoop, p) as JS.ForOfLoop;
339
+ return produce(ret, draft => {
340
+ this.ensureSpace(draft.loop.control.variable.after);
341
+ this.ensureSpace(draft.loop.control.iterable.element.prefix);
342
+ });
343
+ }
344
+
345
+ protected async visitImportClause(importClause: JS.ImportClause, p: P): Promise<J | undefined> {
346
+ const ret = await super.visitImportClause(importClause, p) as JS.ImportClause;
347
+ return produce(ret, draft => {
348
+ // named bindings open with a punctuator, which separates itself from `import` and `type`
349
+ if (draft.typeOnly || draft.name) {
350
+ this.ensureSpace(draft.prefix);
351
+ }
352
+ if (draft.typeOnly && draft.name) {
353
+ this.ensureSpace(draft.name.element.prefix);
354
+ }
355
+ });
356
+ }
357
+
358
+ protected async visitImportDeclaration(jsImport: JS.Import, p: P): Promise<J | undefined> {
359
+ const ret = await super.visitImportDeclaration(jsImport, p) as JS.Import;
360
+ // `from` prints only with a clause, and not at all in the `= require(...)` form; `}` separates
361
+ // named imports from it, and the specifier after it is always a string literal
362
+ const bindings = ret.importClause?.namedBindings;
363
+ if (!ret.importClause || !ret.moduleSpecifier || bindings?.kind === JS.Kind.NamedImports) {
364
+ return ret;
365
+ }
366
+ return produce(ret, draft => {
367
+ this.ensureSpace(draft.moduleSpecifier!.before);
368
+ });
369
+ }
370
+
371
+ protected async visitInstanceOf(instanceOf: J.InstanceOf, p: P): Promise<J | undefined> {
372
+ const ret = await super.visitInstanceOf(instanceOf, p) as J.InstanceOf;
373
+ return produce(ret, draft => {
374
+ this.ensureSpace(draft.expression.after);
375
+ this.ensureSpace(draft.class.prefix);
376
+ });
377
+ }
378
+
379
+ protected async visitSatisfiesExpression(satisfies: JS.SatisfiesExpression, p: P): Promise<J | undefined> {
380
+ const ret = await super.visitSatisfiesExpression(satisfies, p) as JS.SatisfiesExpression;
381
+ return produce(ret, draft => {
382
+ this.ensureSpace(draft.satisfiesType.before);
383
+ this.ensureSpace(draft.satisfiesType.element.prefix);
384
+ });
385
+ }
386
+
387
+ protected async visitScopedVariableDeclarations(scoped: JS.ScopedVariableDeclarations, p: P): Promise<J | undefined> {
388
+ const ret = await super.visitScopedVariableDeclarations(scoped, p) as JS.ScopedVariableDeclarations;
389
+ if (ret.modifiers.length === 0 || ret.variables.length === 0) {
390
+ return ret;
391
+ }
392
+ return produce(ret, draft => {
393
+ for (let i = 1; i < draft.modifiers.length; i++) {
394
+ this.ensureSpace(draft.modifiers[i].prefix);
395
+ }
396
+ this.ensureSpace(draft.variables[0].element.prefix);
397
+ });
398
+ }
399
+
400
+ protected async visitTypeOperator(typeOperator: JS.TypeOperator, p: P): Promise<J | undefined> {
401
+ const ret = await super.visitTypeOperator(typeOperator, p) as JS.TypeOperator;
402
+ return produce(ret, draft => {
403
+ this.ensureSpace(draft.expression.before);
404
+ });
405
+ }
406
+
407
+ protected async visitTypePredicate(typePredicate: JS.TypePredicate, p: P): Promise<J | undefined> {
408
+ const ret = await super.visitTypePredicate(typePredicate, p) as JS.TypePredicate;
409
+ return produce(ret, draft => {
410
+ if (draft.asserts.element) {
411
+ this.ensureSpace(draft.parameterName.prefix);
412
+ }
413
+ if (draft.expression) {
414
+ this.ensureSpace(draft.expression.before);
415
+ this.ensureSpace(draft.expression.element.prefix);
416
+ }
417
+ });
418
+ }
419
+
420
+ protected async visitVoid(void_: JS.Void, p: P): Promise<J | undefined> {
421
+ const ret = await super.visitVoid(void_, p) as JS.Void;
422
+ return produce(ret, draft => {
423
+ this.ensureSpace(draft.expression.prefix);
424
+ });
425
+ }
426
+
427
+ protected async visitYield(aYield: J.Yield, p: P): Promise<J | undefined> {
428
+ const ret = await super.visitYield(aYield, p) as J.Yield;
429
+ // `yield*` separates itself from the value
430
+ if (!ret.value || findMarker(ret, JS.Markers.DelegatedYield)) {
431
+ return ret;
432
+ }
433
+ return produce(ret, draft => {
434
+ this.ensureSpace(draft.value!.prefix);
435
+ });
436
+ }
437
+
249
438
  private ensureSpace(spaceDraft: Draft<J.Space>) {
250
439
  if (spaceDraft.whitespace.length === 0 && spaceDraft.comments.length === 0) {
251
440
  spaceDraft.whitespace = " ";
@@ -357,6 +357,15 @@ function pruneNode(node: any, path: PathSegment[], pathIndex: number, target: an
357
357
  return node;
358
358
  }
359
359
 
360
+ // A compilation unit's statements are laid out at column 0, so nothing beside the target bears
361
+ // on how Prettier lays it out and the printed program can be the target alone.
362
+ if (node.kind === JS.Kind.CompilationUnit && segment.property === 'statements' && segment.index !== undefined) {
363
+ const statements = value as J.RightPadded<Statement>[];
364
+ return updateIfChanged(node, {
365
+ statements: [pruneNode(statements[segment.index], path, pathIndex + 1, target)]
366
+ });
367
+ }
368
+
360
369
  // Handle J.Block#statements specially - prune siblings
361
370
  if (node.kind === J.Kind.Block && segment.property === 'statements' && segment.index !== undefined) {
362
371
  const statements = value as J.RightPadded<Statement>[];
@@ -430,10 +439,10 @@ function findByPath(tree: any, path: PathSegment[]): any {
430
439
  if (value == null) return undefined;
431
440
 
432
441
  if (Array.isArray(value) && segment.index !== undefined) {
433
- // For block statements, target is always at the last index
434
- // since following siblings are omitted during pruning
435
- const isBlockStatements = current.kind === J.Kind.Block && segment.property === 'statements';
436
- const index = isBlockStatements ? value.length - 1 : segment.index;
442
+ // In pruned statement lists the target is last, following siblings having been omitted
443
+ const isPrunedStatements = segment.property === 'statements' &&
444
+ (current.kind === J.Kind.Block || current.kind === JS.Kind.CompilationUnit);
445
+ const index = isPrunedStatements ? value.length - 1 : segment.index;
437
446
  const item = value[index];
438
447
  if (item == null) return undefined;
439
448
  current = item;
@@ -474,6 +483,12 @@ export async function prettierFormatSubtree<T extends J>(
474
483
  return undefined;
475
484
  }
476
485
 
486
+ // Whatever stands between a statement and the top of its file is the file's business, so a whole
487
+ // statement keeps the prefix it came with. Prettier sees it, for the directives — a leading
488
+ // `prettier-ignore` — that a statement's own layout answers to.
489
+ const wholeStatement = path.length === 2 && path[0].property === 'statements' &&
490
+ path[0].index !== undefined && path[1].property === 'element';
491
+
477
492
  // Prune the tree for efficient formatting and substitute the (potentially modified) target.
478
493
  // This ensures that if the visitor modified the target before calling autoFormat,
479
494
  // we format the modified content, not the original from the cursor.
@@ -492,7 +507,12 @@ export async function prettierFormatSubtree<T extends J>(
492
507
  const reconciler = new WhitespaceReconciler();
493
508
  const reconciled = reconciler.reconcile(target as J, formattedTarget as J, undefined, stopAfter);
494
509
 
495
- return reconciled as T;
510
+ // The walk that carries whitespace back pairs the two trees node for node, which a change to
511
+ // the code itself — Prettier dropping redundant parentheses, say — makes impossible
512
+ if (!reconciler.isCompatible()) {
513
+ return undefined;
514
+ }
515
+ return (wholeStatement ? {...reconciled as T, prefix: target.prefix} : reconciled as T);
496
516
  }
497
517
 
498
518
  /**
@@ -554,7 +574,8 @@ export function getPrettierStyle(
554
574
  * @param p The visitor parameter
555
575
  * @param cursor Optional cursor for subtree formatting
556
576
  * @param stopAfter Optional tree to stop after
557
- * @returns The formatted tree
577
+ * @returns The formatted tree, or `undefined` where Prettier's result cannot be carried back,
578
+ * leaving the caller to lay the tree out itself
558
579
  */
559
580
  export async function applyPrettierFormatting<R extends J, P>(
560
581
  tree: R,
@@ -589,31 +610,20 @@ export async function applyPrettierFormatting<R extends J, P>(
589
610
  prettierVersion: prettierStyle.prettierVersion,
590
611
  };
591
612
 
613
+ const wholeFile = t.kind === JS.Kind.CompilationUnit;
592
614
  try {
593
- if (t.kind === JS.Kind.CompilationUnit) {
615
+ if (wholeFile) {
594
616
  // Format and reconcile the entire compilation unit
595
617
  const formatted = await prettierFormat(t as unknown as JS.CompilationUnit, prettierOpts, stopAfter as J | undefined);
596
618
  return formatted as unknown as R;
597
619
  }
598
620
 
599
- if (!cursor) {
600
- // No cursor provided - can't use subtree formatting, return with essential formatting
601
- console.warn('Prettier formatting: No cursor provided for subtree, returning with essential formatting only');
602
- return t;
603
- }
604
-
605
- // Use prettierFormatSubtree for subtree formatting
606
- const formatted = await prettierFormatSubtree(t, cursor, prettierOpts, stopAfter as J | undefined);
607
- if (formatted) {
608
- return formatted as R;
609
- }
610
-
611
- // Subtree formatting failed, return with essential formatting applied
612
- console.warn('Prettier formatting: Subtree formatting failed, returning with essential formatting only');
613
- return t;
621
+ // Pruning and reconciling a subtree both go by where it sits in its file
622
+ return cursor && await prettierFormatSubtree(t, cursor, prettierOpts, stopAfter as J | undefined) as R | undefined;
614
623
  } catch (e) {
615
- // If Prettier fails, return tree with essential formatting applied
616
- console.warn('Prettier formatting failed, returning with essential formatting only:', e);
617
- return t;
624
+ console.warn('Prettier formatting failed:', e);
625
+ // The built-in visitors lay out every line they are given, so a whole file — the author's
626
+ // layout rather than a recipe's — keeps the one it came with; a subtree is handed on.
627
+ return wholeFile ? t : undefined;
618
628
  }
619
629
  }
@@ -32,13 +32,20 @@ export * from "./tree-debug";
32
32
  export * from "./project-parser";
33
33
 
34
34
  export type {Scope} from "./scope";
35
- export {scopeOf, namesDeclaredIn, bindingNames} from "./scope";
35
+ // `cursorOf` is deliberately absent: a visitor of one's own reaches `this.cursor` directly, and
36
+ // the free functions that have no visitor to reach it through are already in this list.
37
+ export {
38
+ scopeOf, namesDeclaredIn, namesDeclaredWithin, namesUsedIn, bindingNames, deconflict,
39
+ isValueReference, declarationsOf, compilationUnitOf, walk
40
+ } from "./scope";
36
41
  export type {QuoteChar, AddImportOptions} from "./add-import";
37
42
  export {ImportStyle, moduleNameOf, AddImport} from "./add-import";
38
43
  // AMD mechanics `recipes-ui5` builds on directly, beyond the `maybeBind` surface below.
39
- export type {AmdBlock} from "./amd";
44
+ export type {AmdBlock, AmdCalleeOptions} from "./amd";
40
45
  export {
41
- DEFAULT_AMD_CALLEES, amdBlockOf, dependencyNames, parameterNames, withDependency, withoutDependencyAt,
46
+ DEFAULT_AMD_CALLEES, amdBlockOf, enclosingAmdBlock, present, elementsOf, parametersOf, identifierOf,
47
+ dependencyNames, parameterNames, bodyOf, references, namesUsed, lastSegment, derivedBindingName,
48
+ withDependency, withUnboundDependency, withoutDependencyAt,
42
49
  RemoveAmdDependency, removeNewlyUnusedAmdBindings
43
50
  } from "./amd";
44
51
  export type {MaybeBindOptions, MaybeUnbindOptions, MaybeRebindOptions, ModuleBindings} from "./binding";
@@ -351,8 +351,9 @@ export function hasFlowAnnotation(sourceFile: ts.SourceFile) {
351
351
  }
352
352
 
353
353
  export function checkSyntaxErrors(program: ts.Program, sourceFile: ts.SourceFile) {
354
- const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile);
355
- // checking Parsing and Syntax Errors
354
+ // Only parse diagnostics decide whether a file gets an LST, so the visitor has to map every slot the
355
+ // parser fills — including ones the checker would reject — or the file loses text on print.
356
+ const diagnostics = program.getSyntacticDiagnostics(sourceFile);
356
357
  let syntaxErrors : [errorMsg: string, errorCode: number][] = [];
357
358
  if (diagnostics.length > 0) {
358
359
  const errors = diagnostics.filter(d => (d.category === ts.DiagnosticCategory.Error) && isCriticalDiagnostic(d.code, sourceFile));
@@ -373,18 +374,11 @@ export function checkSyntaxErrors(program: ts.Program, sourceFile: ts.SourceFile
373
374
  return syntaxErrors;
374
375
  }
375
376
 
376
- const additionalCriticalCodes = new Set([
377
- // Syntax errors
378
- 17019, // "'{0}' at the end of a type is not valid TypeScript syntax. Did you mean to write '{1}'?"
379
- 17020, // "'{0}' at the start of a type is not valid TypeScript syntax. Did you mean to write '{1}'?"
380
-
381
- // Other critical errors
382
- ]);
383
-
384
377
  // errors code description available at https://github.com/microsoft/TypeScript/blob/main/src/compiler/diagnosticMessages.json
385
378
  const excludedCodes = new Set([
386
379
  1039, // Initializers are not allowed in ambient contexts.
387
380
  1064, // The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<{0}>'?
381
+ 1101, // 'with' statements are not allowed in strict mode.
388
382
  1107, // Jump target cannot cross function boundary.
389
383
  1111, // Private field '{0}' must be declared in an enclosing class.
390
384
  1117, // An object literal cannot have multiple properties with the same name.
@@ -446,7 +440,7 @@ function isCriticalDiagnostic(code: number, sourceFile: ts.SourceFile): boolean
446
440
  }
447
441
  }
448
442
 
449
- return (code > 1000 && code < 2000 && !excludedCodes.has(code)) || additionalCriticalCodes.has(code);
443
+ return code > 1000 && code < 2000 && !excludedCodes.has(code);
450
444
  }
451
445
 
452
446
  export function isValidSurrogateRange(unicodeString: string): boolean {
@@ -79,6 +79,15 @@ function getScriptKindFromFileName(fileName: string): ts.ScriptKind {
79
79
  }
80
80
  }
81
81
 
82
+ /**
83
+ * TypeScript's public typings model valid syntax only, so slots its parser still fills for invalid code —
84
+ * an initializer on a type member, a modifier on an object literal member — are missing from them.
85
+ */
86
+ type WithInvalidSyntaxSlots<T> = T & {
87
+ initializer?: ts.Expression;
88
+ modifiers?: ts.NodeArray<ts.ModifierLike>;
89
+ };
90
+
82
91
  export class JavaScriptParser extends Parser {
83
92
 
84
93
  private readonly compilerOptions: ts.CompilerOptions;
@@ -379,14 +388,14 @@ export class JavaScriptParser extends Parser {
379
388
  }
380
389
 
381
390
  try {
382
- yield produce(
391
+ yield await this.requirePrintEqualsInput(produce(
383
392
  new JavaScriptParserVisitor(sourceFile, this.relativePath(input), typeMapping)
384
393
  .visit(sourceFile) as SourceFile,
385
394
  draft => {
386
395
  if (this.styles) {
387
396
  draft.markers = this.styles.reduce((m, s) => replaceMarkerByKind(m, s), draft.markers);
388
397
  }
389
- });
398
+ }), input, escapeLoneSurrogates(sourceFile.getFullText()));
390
399
  } catch (error) {
391
400
  yield this.error(input, error instanceof Error ? error : new Error('Parser threw unknown error: ' + error));
392
401
  }
@@ -573,7 +582,12 @@ export class JavaScriptParserVisitor {
573
582
  | ts.FunctionDeclaration | ts.ParameterDeclaration | ts.MethodDeclaration | ts.EnumDeclaration | ts.InterfaceDeclaration
574
583
  | ts.PropertySignature | ts.ConstructorDeclaration | ts.ModuleDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration
575
584
  | ts.ArrowFunction | ts.IndexSignatureDeclaration | ts.TypeAliasDeclaration | ts.ExportDeclaration | ts.ExportAssignment | ts.FunctionExpression
576
- | ts.ConstructorTypeNode | ts.TypeParameterDeclaration | ts.ImportDeclaration | ts.ImportEqualsDeclaration): J.Modifier[] {
585
+ | ts.ConstructorTypeNode | ts.TypeParameterDeclaration | ts.ImportDeclaration | ts.ImportEqualsDeclaration
586
+ | ts.PropertyAssignment | ts.ShorthandPropertyAssignment): J.Modifier[] {
587
+ if (ts.isPropertyAssignment(node) || ts.isShorthandPropertyAssignment(node)) {
588
+ const modifiers = (node as WithInvalidSyntaxSlots<typeof node>).modifiers;
589
+ return modifiers ? modifiers.filter(ts.isModifier).map(this.mapModifier) : [];
590
+ }
577
591
  if (ts.isVariableStatement(node) || ts.isModuleDeclaration(node) || ts.isClassDeclaration(node) || ts.isEnumDeclaration(node)
578
592
  || ts.isInterfaceDeclaration(node) || ts.isPropertyDeclaration(node) || ts.isPropertySignature(node) || ts.isParameter(node)
579
593
  || ts.isMethodDeclaration(node) || ts.isConstructorDeclaration(node) || ts.isArrowFunction(node)
@@ -1175,6 +1189,8 @@ export class JavaScriptParserVisitor {
1175
1189
  visitPropertySignature(node: ts.PropertySignature): J.VariableDeclarations {
1176
1190
  // FIXME We are mapping the literals in things like type ascii = { " ": 32; "!": 33; } to
1177
1191
  // named variables, which is not a good use of this construct.
1192
+
1193
+ const initializer = (node as WithInvalidSyntaxSlots<ts.PropertySignature>).initializer;
1178
1194
  return {
1179
1195
  kind: J.Kind.VariableDeclarations,
1180
1196
  id: randomId(),
@@ -1193,6 +1209,7 @@ export class JavaScriptParserVisitor {
1193
1209
  draft.markers = this.maybeAddOptionalMarker(draft, node);
1194
1210
  }) as VariableDeclarator,
1195
1211
  dimensionsAfterName: [],
1212
+ initializer: initializer && this.leftPadded(this.prefix(node.getChildAt(node.getChildren().indexOf(initializer) - 1)), this.visit(initializer)),
1196
1213
  variableType: this.mapVariableType(node)
1197
1214
  },
1198
1215
  emptySpace
@@ -1355,7 +1372,8 @@ export class JavaScriptParserVisitor {
1355
1372
 
1356
1373
  private mapTypeInfo(node: ts.MethodDeclaration | ts.PropertyDeclaration | ts.VariableDeclaration | ts.ParameterDeclaration
1357
1374
  | ts.PropertySignature | ts.MethodSignature | ts.ArrowFunction | ts.CallSignatureDeclaration | ts.GetAccessorDeclaration
1358
- | ts.FunctionDeclaration | ts.ConstructSignatureDeclaration | ts.FunctionExpression | ts.NamedTupleMember): JS.TypeInfo | undefined {
1375
+ | ts.FunctionDeclaration | ts.ConstructSignatureDeclaration | ts.FunctionExpression | ts.NamedTupleMember
1376
+ | ts.ConstructorDeclaration | ts.SetAccessorDeclaration): JS.TypeInfo | undefined {
1359
1377
  return node.type && {
1360
1378
  kind: JS.Kind.TypeInfo,
1361
1379
  id: randomId(),
@@ -1397,6 +1415,7 @@ export class JavaScriptParserVisitor {
1397
1415
  leadingAnnotations: this.mapDecorators(node),
1398
1416
  modifiers: this.mapModifiers(node),
1399
1417
  nameAnnotations: [],
1418
+ returnTypeExpression: this.mapTypeInfo(node),
1400
1419
  name: {...this.mapIdentifier(constructorKeyword, constructorKeyword.getText(), false), type: methodType},
1401
1420
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
1402
1421
  dimensionsAfterName: [],
@@ -1416,6 +1435,7 @@ export class JavaScriptParserVisitor {
1416
1435
  markers: emptyMarkers,
1417
1436
  leadingAnnotations: this.mapDecorators(node),
1418
1437
  modifiers: this.mapModifiers(node),
1438
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1419
1439
  returnTypeExpression: this.mapTypeInfo(node),
1420
1440
  name: name as ComputedPropertyName,
1421
1441
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
@@ -1431,6 +1451,7 @@ export class JavaScriptParserVisitor {
1431
1451
  markers: emptyMarkers,
1432
1452
  leadingAnnotations: this.mapDecorators(node),
1433
1453
  modifiers: this.mapModifiers(node),
1454
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1434
1455
  returnTypeExpression: this.mapTypeInfo(node),
1435
1456
  nameAnnotations: [],
1436
1457
  name: name as J.Identifier,
@@ -1452,6 +1473,8 @@ export class JavaScriptParserVisitor {
1452
1473
  markers: emptyMarkers,
1453
1474
  leadingAnnotations: this.mapDecorators(node),
1454
1475
  modifiers: this.mapModifiers(node),
1476
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1477
+ returnTypeExpression: this.mapTypeInfo(node),
1455
1478
  name: name as ComputedPropertyName,
1456
1479
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
1457
1480
  body: node.body && this.convert<J.Block>(node.body),
@@ -1466,6 +1489,8 @@ export class JavaScriptParserVisitor {
1466
1489
  markers: emptyMarkers,
1467
1490
  leadingAnnotations: this.mapDecorators(node),
1468
1491
  modifiers: this.mapModifiers(node),
1492
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1493
+ returnTypeExpression: this.mapTypeInfo(node),
1469
1494
  nameAnnotations: [],
1470
1495
  name: name as J.Identifier,
1471
1496
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
@@ -4219,6 +4244,7 @@ export class JavaScriptParserVisitor {
4219
4244
  id: randomId(),
4220
4245
  prefix: this.prefix(node),
4221
4246
  markers: emptyMarkers,
4247
+ modifiers: this.mapModifiers(node),
4222
4248
  name: this.rightPadded(this.visit(node.name), this.suffix(node.name)),
4223
4249
  assigmentToken: JS.PropertyAssignment.Token.Colon,
4224
4250
  initializer: this.visit(node.initializer)
@@ -4231,6 +4257,7 @@ export class JavaScriptParserVisitor {
4231
4257
  id: randomId(),
4232
4258
  prefix: this.prefix(node),
4233
4259
  markers: emptyMarkers,
4260
+ modifiers: this.mapModifiers(node),
4234
4261
  name: this.rightPadded(this.visit(node.name), this.suffix(node.name)),
4235
4262
  assigmentToken: JS.PropertyAssignment.Token.Equals,
4236
4263
  initializer: node.objectAssignmentInitializer && this.visit(node.objectAssignmentInitializer)
@@ -4654,7 +4681,8 @@ export class JavaScriptParserVisitor {
4654
4681
  }
4655
4682
 
4656
4683
  private mapTypeParametersAsObject(node: ts.MethodDeclaration | ts.MethodSignature | ts.FunctionDeclaration
4657
- | ts.CallSignatureDeclaration | ts.ConstructSignatureDeclaration | ts.FunctionExpression | ts.ArrowFunction | ts.TypeAliasDeclaration | ts.FunctionTypeNode | ts.ConstructorTypeNode): J.TypeParameters | undefined {
4684
+ | ts.CallSignatureDeclaration | ts.ConstructSignatureDeclaration | ts.FunctionExpression | ts.ArrowFunction | ts.TypeAliasDeclaration | ts.FunctionTypeNode | ts.ConstructorTypeNode
4685
+ | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration): J.TypeParameters | undefined {
4658
4686
  const typeParameters = node.typeParameters;
4659
4687
  if (!typeParameters) return undefined;
4660
4688
 
@@ -4806,6 +4834,8 @@ class FlowSyntaxNotSupportedError extends SyntaxError {
4806
4834
 
4807
4835
  const SURR_FIRST = 0xD800;
4808
4836
  const SURR_LAST = 0xDFFF;
4837
+ const HIGH_SURR_LAST = 0xDBFF;
4838
+ const LOW_SURR_FIRST = 0xDC00;
4809
4839
 
4810
4840
  /**
4811
4841
  * Extracts invalid UTF-16 surrogate pairs from a string literal's value source.
@@ -4819,6 +4849,26 @@ const SURR_LAST = 0xDFFF;
4819
4849
  * 1. Unicode escape sequences (\uXXXX) where XXXX is in the surrogate range
4820
4850
  * 2. Raw surrogate characters in the source (character codes 0xD800-0xDFFF)
4821
4851
  */
4852
+ /** The form {@link extractSurrogateEscapes} can hold, against which print idempotence is measured. */
4853
+ function escapeLoneSurrogates(source: string): string {
4854
+ let escaped = '';
4855
+ for (let j = 0; j < source.length; j++) {
4856
+ const charCode = source.charCodeAt(j);
4857
+ if (charCode >= SURR_FIRST && charCode <= SURR_LAST) {
4858
+ const next = source.charCodeAt(j + 1);
4859
+ if (charCode <= HIGH_SURR_LAST && next >= LOW_SURR_FIRST && next <= SURR_LAST) {
4860
+ escaped += source.charAt(j) + source.charAt(j + 1);
4861
+ j++;
4862
+ continue;
4863
+ }
4864
+ escaped += "\\u" + charCode.toString(16).toUpperCase().padStart(4, '0');
4865
+ continue;
4866
+ }
4867
+ escaped += source.charAt(j);
4868
+ }
4869
+ return escaped;
4870
+ }
4871
+
4822
4872
  function extractSurrogateEscapes(valueSource: string): { cleanedSource: string, unicodeEscapes: J.LiteralUnicodeEscape[] } {
4823
4873
  const unicodeEscapes: J.LiteralUnicodeEscape[] = [];
4824
4874
  let cleanedSource = '';
@@ -4842,8 +4892,17 @@ function extractSurrogateEscapes(valueSource: string): { cleanedSource: string,
4842
4892
  }
4843
4893
  }
4844
4894
 
4845
- // Check for raw surrogate characters in the source
4895
+ // A raw surrogate pair spells one code point — an emoji, say — and is carried as itself. A lone
4896
+ // surrogate is not text on its own and does not survive serialization (#6599), so it is held as a
4897
+ // `\uXXXX` escape and prints back as one.
4846
4898
  if (charCode >= SURR_FIRST && charCode <= SURR_LAST) {
4899
+ const next = valueSource.charCodeAt(j + 1);
4900
+ if (charCode <= HIGH_SURR_LAST && next >= LOW_SURR_FIRST && next <= SURR_LAST) {
4901
+ cleanedSource += c + valueSource.charAt(j + 1);
4902
+ cleanedIndex += 2;
4903
+ j++;
4904
+ continue;
4905
+ }
4847
4906
  const codePoint = charCode.toString(16).toUpperCase().padStart(4, '0');
4848
4907
  unicodeEscapes.push({ valueSourceIndex: cleanedIndex, codePoint });
4849
4908
  continue;
@@ -950,6 +950,13 @@ export class JavaScriptPrinter extends JavaScriptVisitor<PrintOutputCapture> {
950
950
  return typeDeclaration;
951
951
  }
952
952
 
953
+ override async visitUnknownSource(source: J.UnknownSource, p: PrintOutputCapture): Promise<J | undefined> {
954
+ await this.beforeSyntax(source, p);
955
+ p.append(source.text);
956
+ await this.afterSyntax(source, p);
957
+ return source;
958
+ }
959
+
953
960
  override async visitLiteralType(literalType: JS.LiteralType, p: PrintOutputCapture): Promise<J | undefined> {
954
961
  await this.beforeSyntax(literalType, p);
955
962
  await this.visit(literalType.literal, p);
@@ -1413,6 +1420,9 @@ export class JavaScriptPrinter extends JavaScriptVisitor<PrintOutputCapture> {
1413
1420
  override async visitPropertyAssignment(propertyAssignment: JS.PropertyAssignment, p: PrintOutputCapture): Promise<J | undefined> {
1414
1421
  await this.beforeSyntax(propertyAssignment, p);
1415
1422
 
1423
+ for (const m of propertyAssignment.modifiers) {
1424
+ await this.visitModifier(m, p);
1425
+ }
1416
1426
  await this.visitRightPadded(propertyAssignment.name, p);
1417
1427
 
1418
1428
  if (propertyAssignment.initializer) {