@openrewrite/rewrite 8.91.1 → 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 (160) 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 +127 -38
  6. package/dist/javascript/add-import.d.ts.map +1 -1
  7. package/dist/javascript/add-import.js +684 -195
  8. package/dist/javascript/add-import.js.map +1 -1
  9. package/dist/javascript/amd.d.ts +148 -0
  10. package/dist/javascript/amd.d.ts.map +1 -0
  11. package/dist/javascript/amd.js +802 -0
  12. package/dist/javascript/amd.js.map +1 -0
  13. package/dist/javascript/binding.d.ts +102 -0
  14. package/dist/javascript/binding.d.ts.map +1 -0
  15. package/dist/javascript/binding.js +308 -0
  16. package/dist/javascript/binding.js.map +1 -0
  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 +8 -1
  34. package/dist/javascript/index.d.ts.map +1 -1
  35. package/dist/javascript/index.js +46 -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 +7 -9
  39. package/dist/javascript/parser-utils.js.map +1 -1
  40. package/dist/javascript/parser.d.ts +7 -1
  41. package/dist/javascript/parser.d.ts.map +1 -1
  42. package/dist/javascript/parser.js +55 -4
  43. package/dist/javascript/parser.js.map +1 -1
  44. package/dist/javascript/print.d.ts +1 -0
  45. package/dist/javascript/print.d.ts.map +1 -1
  46. package/dist/javascript/print.js +9 -0
  47. package/dist/javascript/print.js.map +1 -1
  48. package/dist/javascript/recipes/change-import.d.ts +4 -3
  49. package/dist/javascript/recipes/change-import.d.ts.map +1 -1
  50. package/dist/javascript/recipes/change-import.js +12 -504
  51. package/dist/javascript/recipes/change-import.js.map +1 -1
  52. package/dist/javascript/remove-import.d.ts +2 -28
  53. package/dist/javascript/remove-import.d.ts.map +1 -1
  54. package/dist/javascript/remove-import.js +19 -57
  55. package/dist/javascript/remove-import.js.map +1 -1
  56. package/dist/javascript/rpc.js +2 -0
  57. package/dist/javascript/rpc.js.map +1 -1
  58. package/dist/javascript/scope.d.ts +82 -0
  59. package/dist/javascript/scope.d.ts.map +1 -0
  60. package/dist/javascript/scope.js +476 -0
  61. package/dist/javascript/scope.js.map +1 -0
  62. package/dist/javascript/templating/bindings.d.ts +0 -11
  63. package/dist/javascript/templating/bindings.d.ts.map +1 -1
  64. package/dist/javascript/templating/bindings.js +2 -49
  65. package/dist/javascript/templating/bindings.js.map +1 -1
  66. package/dist/javascript/templating/comparator.d.ts +12 -0
  67. package/dist/javascript/templating/comparator.d.ts.map +1 -1
  68. package/dist/javascript/templating/comparator.js +68 -14
  69. package/dist/javascript/templating/comparator.js.map +1 -1
  70. package/dist/javascript/templating/engine.d.ts +22 -4
  71. package/dist/javascript/templating/engine.d.ts.map +1 -1
  72. package/dist/javascript/templating/engine.js +68 -24
  73. package/dist/javascript/templating/engine.js.map +1 -1
  74. package/dist/javascript/templating/index.d.ts +1 -1
  75. package/dist/javascript/templating/index.d.ts.map +1 -1
  76. package/dist/javascript/templating/index.js.map +1 -1
  77. package/dist/javascript/templating/pattern.d.ts.map +1 -1
  78. package/dist/javascript/templating/pattern.js +2 -2
  79. package/dist/javascript/templating/pattern.js.map +1 -1
  80. package/dist/javascript/templating/placeholder-replacement.d.ts +2 -0
  81. package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
  82. package/dist/javascript/templating/placeholder-replacement.js +10 -5
  83. package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
  84. package/dist/javascript/templating/precedence.d.ts +12 -4
  85. package/dist/javascript/templating/precedence.d.ts.map +1 -1
  86. package/dist/javascript/templating/precedence.js +18 -11
  87. package/dist/javascript/templating/precedence.js.map +1 -1
  88. package/dist/javascript/templating/rewrite.d.ts.map +1 -1
  89. package/dist/javascript/templating/rewrite.js +7 -1
  90. package/dist/javascript/templating/rewrite.js.map +1 -1
  91. package/dist/javascript/templating/template.d.ts +7 -1
  92. package/dist/javascript/templating/template.d.ts.map +1 -1
  93. package/dist/javascript/templating/template.js +54 -28
  94. package/dist/javascript/templating/template.js.map +1 -1
  95. package/dist/javascript/templating/types.d.ts +15 -18
  96. package/dist/javascript/templating/types.d.ts.map +1 -1
  97. package/dist/javascript/templating/utils.d.ts +1 -1
  98. package/dist/javascript/templating/utils.d.ts.map +1 -1
  99. package/dist/javascript/templating/utils.js +3 -2
  100. package/dist/javascript/templating/utils.js.map +1 -1
  101. package/dist/javascript/tree.d.ts +1 -0
  102. package/dist/javascript/tree.d.ts.map +1 -1
  103. package/dist/javascript/tree.js.map +1 -1
  104. package/dist/javascript/type-mapping.d.ts.map +1 -1
  105. package/dist/javascript/type-mapping.js +14 -0
  106. package/dist/javascript/type-mapping.js.map +1 -1
  107. package/dist/javascript/visitor.d.ts.map +1 -1
  108. package/dist/javascript/visitor.js +1 -0
  109. package/dist/javascript/visitor.js.map +1 -1
  110. package/dist/parser.d.ts +6 -0
  111. package/dist/parser.d.ts.map +1 -1
  112. package/dist/parser.js +23 -1
  113. package/dist/parser.js.map +1 -1
  114. package/dist/print.d.ts.map +1 -1
  115. package/dist/print.js +5 -2
  116. package/dist/print.js.map +1 -1
  117. package/dist/rewrite-javascript-version.txt +1 -1
  118. package/dist/rpc/request/parse-project.d.ts +11 -1
  119. package/dist/rpc/request/parse-project.d.ts.map +1 -1
  120. package/dist/rpc/request/parse-project.js +7 -2
  121. package/dist/rpc/request/parse-project.js.map +1 -1
  122. package/dist/rpc/request/parse.d.ts +13 -1
  123. package/dist/rpc/request/parse.d.ts.map +1 -1
  124. package/dist/rpc/request/parse.js +8 -2
  125. package/dist/rpc/request/parse.js.map +1 -1
  126. package/package.json +2 -2
  127. package/src/execution.ts +23 -0
  128. package/src/javascript/add-import.ts +753 -238
  129. package/src/javascript/amd.ts +954 -0
  130. package/src/javascript/binding.ts +435 -0
  131. package/src/javascript/comparator.ts +6 -1
  132. package/src/javascript/format/format.ts +56 -12
  133. package/src/javascript/format/minimum-viable-spacing-visitor.ts +196 -7
  134. package/src/javascript/format/prettier-format.ts +35 -25
  135. package/src/javascript/index.ts +19 -1
  136. package/src/javascript/parser-utils.ts +7 -11
  137. package/src/javascript/parser.ts +78 -6
  138. package/src/javascript/print.ts +10 -0
  139. package/src/javascript/recipes/change-import.ts +13 -593
  140. package/src/javascript/remove-import.ts +20 -56
  141. package/src/javascript/rpc.ts +2 -0
  142. package/src/javascript/scope.ts +517 -0
  143. package/src/javascript/templating/bindings.ts +2 -55
  144. package/src/javascript/templating/comparator.ts +81 -16
  145. package/src/javascript/templating/engine.ts +107 -38
  146. package/src/javascript/templating/index.ts +0 -1
  147. package/src/javascript/templating/pattern.ts +4 -2
  148. package/src/javascript/templating/placeholder-replacement.ts +16 -6
  149. package/src/javascript/templating/precedence.ts +21 -13
  150. package/src/javascript/templating/rewrite.ts +9 -1
  151. package/src/javascript/templating/template.ts +68 -29
  152. package/src/javascript/templating/types.ts +17 -18
  153. package/src/javascript/templating/utils.ts +4 -2
  154. package/src/javascript/tree.ts +1 -0
  155. package/src/javascript/type-mapping.ts +15 -0
  156. package/src/javascript/visitor.ts +1 -0
  157. package/src/parser.ts +24 -1
  158. package/src/print.ts +5 -2
  159. package/src/rpc/request/parse-project.ts +6 -2
  160. 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
  }
@@ -31,7 +31,25 @@ export * from "./autodetect";
31
31
  export * from "./tree-debug";
32
32
  export * from "./project-parser";
33
33
 
34
- export * from "./add-import";
34
+ export type {Scope} 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";
41
+ export type {QuoteChar, AddImportOptions} from "./add-import";
42
+ export {ImportStyle, moduleNameOf, AddImport} from "./add-import";
43
+ // AMD mechanics `recipes-ui5` builds on directly, beyond the `maybeBind` surface below.
44
+ export type {AmdBlock, AmdCalleeOptions} from "./amd";
45
+ export {
46
+ DEFAULT_AMD_CALLEES, amdBlockOf, enclosingAmdBlock, present, elementsOf, parametersOf, identifierOf,
47
+ dependencyNames, parameterNames, bodyOf, references, namesUsed, lastSegment, derivedBindingName,
48
+ withDependency, withUnboundDependency, withoutDependencyAt,
49
+ RemoveAmdDependency, removeNewlyUnusedAmdBindings
50
+ } from "./amd";
51
+ export type {MaybeBindOptions, MaybeUnbindOptions, MaybeRebindOptions, ModuleBindings} from "./binding";
52
+ export {maybeBind, maybeUnbind, maybeRebind, moduleBindings, isAmdBlock, maybeRemoveImport, maybeAddImport} from "./binding";
35
53
  export * from "./remove-import";
36
54
  export * from "./cleanup/index";
37
55
  export * from "./recipes/index";
@@ -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.
@@ -394,6 +388,7 @@ const excludedCodes = new Set([
394
388
  1183, // An implementation cannot be declared in ambient contexts.
395
389
  1203, // Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.
396
390
  1207, // Decorators cannot be applied to multiple get/set accessors of the same name.
391
+ 1212, // Identifier expected. '{0}' is a reserved word in strict mode. The node is still parsed as an identifier.
397
392
  1215, // Invalid use of '{0}'. Modules are automatically in strict mode.
398
393
  1238, // Unable to resolve signature of class decorator when called as an expression.
399
394
  1239, // Unable to resolve signature of parameter decorator when called as an expression.
@@ -422,6 +417,7 @@ const excludedCodes = new Set([
422
417
  1375, // 'await' expressions are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.
423
418
  1378, // Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
424
419
  1432, // Top-level 'for await' loops are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
420
+ 1540, // A 'namespace' declaration should not be declared using the 'module' keyword. The `module` spelling yields a ModuleDeclaration.
425
421
  ]);
426
422
 
427
423
  // Errors to exclude only for JavaScript files (.js, .jsx, .mjs, .cjs)
@@ -444,7 +440,7 @@ function isCriticalDiagnostic(code: number, sourceFile: ts.SourceFile): boolean
444
440
  }
445
441
  }
446
442
 
447
- return (code > 1000 && code < 2000 && !excludedCodes.has(code)) || additionalCriticalCodes.has(code);
443
+ return code > 1000 && code < 2000 && !excludedCodes.has(code);
448
444
  }
449
445
 
450
446
  export function isValidSurrogateRange(unicodeString: string): boolean {
@@ -54,6 +54,12 @@ import SpreadAttribute = JSX.SpreadAttribute;
54
54
  export interface JavaScriptParserOptions extends ParserOptions {
55
55
  styles?: NamedStyles[],
56
56
  sourceFileCache?: Map<string, ts.SourceFile>,
57
+ /**
58
+ * Type packages to load whose declarations nothing imports. Unset leaves TypeScript's default,
59
+ * which reads `@types/*` and nothing else; a package declaring its modules ambiently needs
60
+ * naming here for those declarations to be in scope.
61
+ */
62
+ types?: string[],
57
63
  }
58
64
 
59
65
  function getScriptKindFromFileName(fileName: string): ts.ScriptKind {
@@ -73,6 +79,15 @@ function getScriptKindFromFileName(fileName: string): ts.ScriptKind {
73
79
  }
74
80
  }
75
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
+
76
91
  export class JavaScriptParser extends Parser {
77
92
 
78
93
  private readonly compilerOptions: ts.CompilerOptions;
@@ -86,6 +101,7 @@ export class JavaScriptParser extends Parser {
86
101
  relativeTo,
87
102
  styles,
88
103
  sourceFileCache,
104
+ types,
89
105
  }: JavaScriptParserOptions = {},
90
106
  ) {
91
107
  super({ctx, relativeTo});
@@ -108,6 +124,12 @@ export class JavaScriptParser extends Parser {
108
124
  emitDecoratorMetadata: true,
109
125
  forceConsistentCasingInFileNames: false,
110
126
  jsx: ts.JsxEmit.Preserve,
127
+ // Strictness is a property of the parsed project rather than of the compiler we pin: under
128
+ // `strictNullChecks` every nullable type carries an extra `null` union constituent.
129
+ strict: false,
130
+ // `types` defaults to `[]`, which attributes ambient `@types/*` packages only when named;
131
+ // `*` enumerates everything under `node_modules/@types`.
132
+ types: types ?? ["*"],
111
133
  baseUrl: relativeTo || process.cwd()
112
134
  };
113
135
  this.styles = styles;
@@ -366,14 +388,14 @@ export class JavaScriptParser extends Parser {
366
388
  }
367
389
 
368
390
  try {
369
- yield produce(
391
+ yield await this.requirePrintEqualsInput(produce(
370
392
  new JavaScriptParserVisitor(sourceFile, this.relativePath(input), typeMapping)
371
393
  .visit(sourceFile) as SourceFile,
372
394
  draft => {
373
395
  if (this.styles) {
374
396
  draft.markers = this.styles.reduce((m, s) => replaceMarkerByKind(m, s), draft.markers);
375
397
  }
376
- });
398
+ }), input, escapeLoneSurrogates(sourceFile.getFullText()));
377
399
  } catch (error) {
378
400
  yield this.error(input, error instanceof Error ? error : new Error('Parser threw unknown error: ' + error));
379
401
  }
@@ -560,7 +582,12 @@ export class JavaScriptParserVisitor {
560
582
  | ts.FunctionDeclaration | ts.ParameterDeclaration | ts.MethodDeclaration | ts.EnumDeclaration | ts.InterfaceDeclaration
561
583
  | ts.PropertySignature | ts.ConstructorDeclaration | ts.ModuleDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration
562
584
  | ts.ArrowFunction | ts.IndexSignatureDeclaration | ts.TypeAliasDeclaration | ts.ExportDeclaration | ts.ExportAssignment | ts.FunctionExpression
563
- | 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
+ }
564
591
  if (ts.isVariableStatement(node) || ts.isModuleDeclaration(node) || ts.isClassDeclaration(node) || ts.isEnumDeclaration(node)
565
592
  || ts.isInterfaceDeclaration(node) || ts.isPropertyDeclaration(node) || ts.isPropertySignature(node) || ts.isParameter(node)
566
593
  || ts.isMethodDeclaration(node) || ts.isConstructorDeclaration(node) || ts.isArrowFunction(node)
@@ -1162,6 +1189,8 @@ export class JavaScriptParserVisitor {
1162
1189
  visitPropertySignature(node: ts.PropertySignature): J.VariableDeclarations {
1163
1190
  // FIXME We are mapping the literals in things like type ascii = { " ": 32; "!": 33; } to
1164
1191
  // named variables, which is not a good use of this construct.
1192
+
1193
+ const initializer = (node as WithInvalidSyntaxSlots<ts.PropertySignature>).initializer;
1165
1194
  return {
1166
1195
  kind: J.Kind.VariableDeclarations,
1167
1196
  id: randomId(),
@@ -1180,6 +1209,7 @@ export class JavaScriptParserVisitor {
1180
1209
  draft.markers = this.maybeAddOptionalMarker(draft, node);
1181
1210
  }) as VariableDeclarator,
1182
1211
  dimensionsAfterName: [],
1212
+ initializer: initializer && this.leftPadded(this.prefix(node.getChildAt(node.getChildren().indexOf(initializer) - 1)), this.visit(initializer)),
1183
1213
  variableType: this.mapVariableType(node)
1184
1214
  },
1185
1215
  emptySpace
@@ -1342,7 +1372,8 @@ export class JavaScriptParserVisitor {
1342
1372
 
1343
1373
  private mapTypeInfo(node: ts.MethodDeclaration | ts.PropertyDeclaration | ts.VariableDeclaration | ts.ParameterDeclaration
1344
1374
  | ts.PropertySignature | ts.MethodSignature | ts.ArrowFunction | ts.CallSignatureDeclaration | ts.GetAccessorDeclaration
1345
- | 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 {
1346
1377
  return node.type && {
1347
1378
  kind: JS.Kind.TypeInfo,
1348
1379
  id: randomId(),
@@ -1384,6 +1415,7 @@ export class JavaScriptParserVisitor {
1384
1415
  leadingAnnotations: this.mapDecorators(node),
1385
1416
  modifiers: this.mapModifiers(node),
1386
1417
  nameAnnotations: [],
1418
+ returnTypeExpression: this.mapTypeInfo(node),
1387
1419
  name: {...this.mapIdentifier(constructorKeyword, constructorKeyword.getText(), false), type: methodType},
1388
1420
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
1389
1421
  dimensionsAfterName: [],
@@ -1403,6 +1435,7 @@ export class JavaScriptParserVisitor {
1403
1435
  markers: emptyMarkers,
1404
1436
  leadingAnnotations: this.mapDecorators(node),
1405
1437
  modifiers: this.mapModifiers(node),
1438
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1406
1439
  returnTypeExpression: this.mapTypeInfo(node),
1407
1440
  name: name as ComputedPropertyName,
1408
1441
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
@@ -1418,6 +1451,7 @@ export class JavaScriptParserVisitor {
1418
1451
  markers: emptyMarkers,
1419
1452
  leadingAnnotations: this.mapDecorators(node),
1420
1453
  modifiers: this.mapModifiers(node),
1454
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1421
1455
  returnTypeExpression: this.mapTypeInfo(node),
1422
1456
  nameAnnotations: [],
1423
1457
  name: name as J.Identifier,
@@ -1439,6 +1473,8 @@ export class JavaScriptParserVisitor {
1439
1473
  markers: emptyMarkers,
1440
1474
  leadingAnnotations: this.mapDecorators(node),
1441
1475
  modifiers: this.mapModifiers(node),
1476
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1477
+ returnTypeExpression: this.mapTypeInfo(node),
1442
1478
  name: name as ComputedPropertyName,
1443
1479
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
1444
1480
  body: node.body && this.convert<J.Block>(node.body),
@@ -1453,6 +1489,8 @@ export class JavaScriptParserVisitor {
1453
1489
  markers: emptyMarkers,
1454
1490
  leadingAnnotations: this.mapDecorators(node),
1455
1491
  modifiers: this.mapModifiers(node),
1492
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1493
+ returnTypeExpression: this.mapTypeInfo(node),
1456
1494
  nameAnnotations: [],
1457
1495
  name: name as J.Identifier,
1458
1496
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
@@ -4206,6 +4244,7 @@ export class JavaScriptParserVisitor {
4206
4244
  id: randomId(),
4207
4245
  prefix: this.prefix(node),
4208
4246
  markers: emptyMarkers,
4247
+ modifiers: this.mapModifiers(node),
4209
4248
  name: this.rightPadded(this.visit(node.name), this.suffix(node.name)),
4210
4249
  assigmentToken: JS.PropertyAssignment.Token.Colon,
4211
4250
  initializer: this.visit(node.initializer)
@@ -4218,6 +4257,7 @@ export class JavaScriptParserVisitor {
4218
4257
  id: randomId(),
4219
4258
  prefix: this.prefix(node),
4220
4259
  markers: emptyMarkers,
4260
+ modifiers: this.mapModifiers(node),
4221
4261
  name: this.rightPadded(this.visit(node.name), this.suffix(node.name)),
4222
4262
  assigmentToken: JS.PropertyAssignment.Token.Equals,
4223
4263
  initializer: node.objectAssignmentInitializer && this.visit(node.objectAssignmentInitializer)
@@ -4641,7 +4681,8 @@ export class JavaScriptParserVisitor {
4641
4681
  }
4642
4682
 
4643
4683
  private mapTypeParametersAsObject(node: ts.MethodDeclaration | ts.MethodSignature | ts.FunctionDeclaration
4644
- | 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 {
4645
4686
  const typeParameters = node.typeParameters;
4646
4687
  if (!typeParameters) return undefined;
4647
4688
 
@@ -4793,6 +4834,8 @@ class FlowSyntaxNotSupportedError extends SyntaxError {
4793
4834
 
4794
4835
  const SURR_FIRST = 0xD800;
4795
4836
  const SURR_LAST = 0xDFFF;
4837
+ const HIGH_SURR_LAST = 0xDBFF;
4838
+ const LOW_SURR_FIRST = 0xDC00;
4796
4839
 
4797
4840
  /**
4798
4841
  * Extracts invalid UTF-16 surrogate pairs from a string literal's value source.
@@ -4806,6 +4849,26 @@ const SURR_LAST = 0xDFFF;
4806
4849
  * 1. Unicode escape sequences (\uXXXX) where XXXX is in the surrogate range
4807
4850
  * 2. Raw surrogate characters in the source (character codes 0xD800-0xDFFF)
4808
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
+
4809
4872
  function extractSurrogateEscapes(valueSource: string): { cleanedSource: string, unicodeEscapes: J.LiteralUnicodeEscape[] } {
4810
4873
  const unicodeEscapes: J.LiteralUnicodeEscape[] = [];
4811
4874
  let cleanedSource = '';
@@ -4829,8 +4892,17 @@ function extractSurrogateEscapes(valueSource: string): { cleanedSource: string,
4829
4892
  }
4830
4893
  }
4831
4894
 
4832
- // 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.
4833
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
+ }
4834
4906
  const codePoint = charCode.toString(16).toUpperCase().padStart(4, '0');
4835
4907
  unicodeEscapes.push({ valueSourceIndex: cleanedIndex, codePoint });
4836
4908
  continue;