@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
@@ -4,7 +4,12 @@ exports.RebindImport = exports.AddImport = exports.ImportStyle = void 0;
4
4
  exports.bindImport = bindImport;
5
5
  exports.memberName = memberName;
6
6
  exports.moduleScopeBindings = moduleScopeBindings;
7
+ exports.hasEsmSyntax = hasEsmSyntax;
8
+ exports.isCommonJs = isCommonJs;
9
+ exports.requiredModuleOfDeclaration = requiredModuleOfDeclaration;
7
10
  exports.requiredModuleOf = requiredModuleOf;
11
+ exports.takenNames = takenNames;
12
+ exports.nameTaken = nameTaken;
8
13
  exports.moduleNameOf = moduleNameOf;
9
14
  exports.existingImportBinding = existingImportBinding;
10
15
  const visitor_1 = require("./visitor");
@@ -15,6 +20,9 @@ const markers_1 = require("../markers");
15
20
  const style_1 = require("./style");
16
21
  const scope_1 = require("./scope");
17
22
  const mutative_1 = require("mutative");
23
+ const type_visitor_1 = require("../java/type-visitor");
24
+ const format_1 = require("./format");
25
+ const prettier_format_1 = require("./format/prettier-format");
18
26
  var ImportStyle;
19
27
  (function (ImportStyle) {
20
28
  ImportStyle[ImportStyle["ES6Named"] = 0] = "ES6Named";
@@ -75,15 +83,13 @@ function bindImport(visitor, options, refuseCreate) {
75
83
  visitor.afterVisit.push(new AddImport(options, derived));
76
84
  return derived;
77
85
  }
78
- // An import already serving this request answers it; queuing one would, on the next cycle,
79
- // derive a suffixed name from the binding this call just added. A caller that named a preference
80
- // takes whatever comes back; one that did not assumes the name it derived, so a binding under
81
- // any other name would leave the references it emits unbound.
86
+ // An import already serving this request answers it, under whatever name the file gave it —
87
+ // only a pinned alias, returned above, asks for a binding of its own. Queuing a second import
88
+ // would, on the next cycle, derive a suffixed name from the binding this call just added.
89
+ const scope = (0, scope_1.scopeOf)(cursor);
82
90
  for (const binding of moduleScopeBindings(cu)) {
83
91
  if (binding.module === module && binding.member === memberName(options.member) &&
84
- binding.typeOnly === typeOnly &&
85
- (options.preferredName !== undefined || anyNameAnswers(options) ||
86
- binding.name === derived)) {
92
+ binding.typeOnly === typeOnly && scope.declaringScope(binding.name) === cu) {
87
93
  return binding.name;
88
94
  }
89
95
  }
@@ -168,6 +174,62 @@ function moduleScopeBindings(cu) {
168
174
  }
169
175
  return bindings;
170
176
  }
177
+ /** Whether a top-level statement already marks the file as a module: an import or any export form. */
178
+ function hasEsmSyntax(cu) {
179
+ return cu.statements.some(stmt => {
180
+ var _a;
181
+ const element = stmt.element;
182
+ // `export {a, b}`, `export * from` and `export default` are their own statement kinds;
183
+ // `export class`/`function`/`const` instead carry `export` as a modifier on the
184
+ // declaration itself, the same way TypeScript's own AST models it.
185
+ const modifiers = element === null || element === void 0 ? void 0 : element.modifiers;
186
+ return (element === null || element === void 0 ? void 0 : element.kind) === tree_1.JS.Kind.Import ||
187
+ (element === null || element === void 0 ? void 0 : element.kind) === tree_1.JS.Kind.ExportDeclaration ||
188
+ (element === null || element === void 0 ? void 0 : element.kind) === tree_1.JS.Kind.ExportAssignment ||
189
+ ((_a = modifiers === null || modifiers === void 0 ? void 0 : modifiers.some(m => m.keyword === "export")) !== null && _a !== void 0 ? _a : false);
190
+ }) || hasTopLevelAwait(cu);
191
+ }
192
+ /**
193
+ * Whether a statement holds an `await` outside any function of its own — legal only at module
194
+ * top level, unlike an `await` inside an `async function`, which says nothing about the file.
195
+ */
196
+ function hasTopLevelAwait(cu) {
197
+ let found = false;
198
+ (0, scope_1.walk)(cu.statements, node => {
199
+ if (found) {
200
+ return false;
201
+ }
202
+ if (node.kind === tree_1.JS.Kind.Await) {
203
+ found = true;
204
+ return false;
205
+ }
206
+ return node.kind !== java_1.J.Kind.MethodDeclaration && node.kind !== java_1.J.Kind.Lambda;
207
+ });
208
+ return found;
209
+ }
210
+ /** Whether the file binds its modules with `require`, which decides whether a create is possible. */
211
+ function isCommonJs(cu) {
212
+ if (cu.sourcePath.endsWith(".cjs") || cu.sourcePath.endsWith(".cts")) {
213
+ return true;
214
+ }
215
+ // Node treats these as ES modules regardless of what they contain, the same way
216
+ // `determineImportStyle` reads them as ES6-preferring; `.js`/`.ts`/`.tsx` stay ambiguous and
217
+ // fall through to the statements below.
218
+ if (cu.sourcePath.endsWith(".mjs") || cu.sourcePath.endsWith(".mts")) {
219
+ return false;
220
+ }
221
+ if (hasEsmSyntax(cu)) {
222
+ return false;
223
+ }
224
+ return cu.statements.some(stmt => (0, scope_1.declarationsOf)(stmt.element).some(d => requiredModuleOfDeclaration(d) !== undefined));
225
+ }
226
+ /** The module a `const X = require('m')` declaration names, for the one variable it declares. */
227
+ function requiredModuleOfDeclaration(declaration) {
228
+ var _a, _b;
229
+ return declaration.variables.length === 1
230
+ ? requiredModule((_b = (_a = declaration.variables[0].element) === null || _a === void 0 ? void 0 : _a.initializer) === null || _b === void 0 ? void 0 : _b.element)
231
+ : undefined;
232
+ }
171
233
  /** The module a `require('...')` initializer names, `undefined` for an initializer that is anything else. */
172
234
  function requiredModule(initializer) {
173
235
  return (initializer === null || initializer === void 0 ? void 0 : initializer.kind) === java_1.J.Kind.MethodInvocation
@@ -257,12 +319,12 @@ function importBindings(jsImport) {
257
319
  return bindings;
258
320
  }
259
321
  /**
260
- * Names in scope, plus those pending `AddImport`s on the `afterVisit` queue have claimed. A queued
322
+ * `names`, plus those pending `AddImport`s on the `afterVisit` queue have claimed. A queued
261
323
  * `RemoveImport` does not free one: it removes only what the file leaves unused, and binding a name
262
324
  * it keeps is an error, where an unnecessary suffix merely reads oddly.
263
325
  */
264
- function takenNames(inScope, visitor) {
265
- const taken = new Set(inScope);
326
+ function takenNames(names, visitor) {
327
+ const taken = new Set(names);
266
328
  for (const v of visitor.afterVisit || []) {
267
329
  if (v instanceof AddImport && v.bindingName) {
268
330
  taken.add(v.bindingName);
@@ -270,6 +332,11 @@ function takenNames(inScope, visitor) {
270
332
  }
271
333
  return taken;
272
334
  }
335
+ /** As {@link takenNames}, for a caller asking about one name rather than probing repeatedly. */
336
+ function nameTaken(name, names, visitor) {
337
+ return names.has(name) ||
338
+ (visitor.afterVisit || []).some(v => v instanceof AddImport && v.bindingName === name);
339
+ }
273
340
  /**
274
341
  * The parser lifts surrogate pairs out of `valueSource` into `unicodeEscapes` and, when it does,
275
342
  * sets `value` to the quoted source (`parser.ts` `mapLiteral`), so neither field alone is the
@@ -371,8 +438,8 @@ async function detectQuote(cu) {
371
438
  if (specifierQuote) {
372
439
  return specifierQuote;
373
440
  }
374
- const prettier = (0, style_1.getStyle)(style_1.StyleKind.PrettierStyle, cu);
375
- if ((prettier === null || prettier === void 0 ? void 0 : prettier.kind) === style_1.StyleKind.PrettierStyle && !prettier.ignored) {
441
+ const prettier = (0, prettier_format_1.getPrettierStyle)(cu);
442
+ if (prettier && !prettier.ignored) {
376
443
  const singleQuote = prettier.config.singleQuote;
377
444
  if (typeof singleQuote === 'boolean') {
378
445
  return singleQuote ? "'" : '"';
@@ -380,6 +447,32 @@ async function detectQuote(cu) {
380
447
  }
381
448
  return double > single ? '"' : "'";
382
449
  }
450
+ /**
451
+ * Lays out the import statement `AddImport` wrote, so that the file's own style — a Prettier
452
+ * configuration's brace spacing and print width, say — reaches it.
453
+ */
454
+ async function formatImport(cu, id, p) {
455
+ return await new class extends visitor_1.JavaScriptVisitor {
456
+ async visitImportDeclaration(jsImport, p) {
457
+ return jsImport.id === id ? await (0, format_1.autoFormat)(jsImport, p, undefined, this.cursor.parent) : jsImport;
458
+ }
459
+ }().visit(cu, p);
460
+ }
461
+ /**
462
+ * As {@link formatImport}, for an import that gained specifiers. Short of a Prettier configuration
463
+ * deciding the whole line, the statement's own text says more about its spacing than a
464
+ * repository-wide style does, and the merge reads it directly.
465
+ */
466
+ async function formatMergedImport(cu, id, p) {
467
+ var _a;
468
+ return ((_a = (0, prettier_format_1.getPrettierStyle)(cu)) === null || _a === void 0 ? void 0 : _a.ignored) === false ? formatImport(cu, id, p) : cu;
469
+ }
470
+ /**
471
+ * Adds the import {@link AddImportOptions} describes, under the name derived there. An import
472
+ * already in the file answers where it binds that same name, or where the request expressed no
473
+ * preference at all. Reuse under some other name belongs to {@link bindImport}, whose caller
474
+ * learns which name came back; a recipe driving this visitor is stuck with the one it derived.
475
+ */
383
476
  class AddImport extends visitor_1.JavaScriptVisitor {
384
477
  constructor(options, bindingName) {
385
478
  var _a, _b, _c;
@@ -581,17 +674,18 @@ class AddImport extends visitor_1.JavaScriptVisitor {
581
674
  return mergedCu;
582
675
  }
583
676
  }
584
- // Add the import using the appropriate style
585
- if (importStyle === ImportStyle.CommonJS) {
586
- // TODO: Implement CommonJS require creation
587
- // For now, fall back to ES6 imports
588
- // return this.addCommonJSRequire(compilationUnit, p);
677
+ // TODO: create a `require` here. Until then the request goes unserved, since `import` would
678
+ // make a file that binds its modules with `require` an ES module and change how everything
679
+ // in it loads — `maybeBind` refuses it for the same reason. An explicit ES6 `style`
680
+ // overrides, which is how a caller converting the file to ESM asks for one.
681
+ if (importStyle === ImportStyle.CommonJS && isCommonJs(compilationUnit)) {
682
+ return compilationUnit;
589
683
  }
590
684
  // Add ES6 import (handles ES6Named, ES6Namespace, ES6Default)
591
- return this.produceJavaScript(compilationUnit, p, async (draft) => {
592
- // Find the position to insert the import
593
- const insertionIndex = this.findImportInsertionIndex(compilationUnit);
594
- const newImport = await this.createImportStatement(compilationUnit, insertionIndex, p);
685
+ // Find the position to insert the import
686
+ const insertionIndex = this.findImportInsertionIndex(compilationUnit);
687
+ const newImport = await this.createImportStatement(compilationUnit, insertionIndex, p);
688
+ const withImport = await this.produceJavaScript(compilationUnit, p, async (draft) => {
595
689
  // Insert the import at the appropriate position
596
690
  // Create semicolon marker for the import statement
597
691
  const semicolonMarkers = (0, markers_1.markers)({
@@ -638,6 +732,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
638
732
  }
639
733
  }
640
734
  });
735
+ return formatImport(withImport, newImport.id, p);
641
736
  }
642
737
  /**
643
738
  * Try to merge the new member into an existing import from the same module
@@ -673,7 +768,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
673
768
  continue;
674
769
  }
675
770
  // We found a matching import with named bindings - merge into it
676
- return this.produceJavaScript(compilationUnit, p, async (draft) => {
771
+ return formatMergedImport(await this.produceJavaScript(compilationUnit, p, async (draft) => {
677
772
  var _a, _b, _c;
678
773
  const namedImports = importClause.namedBindings;
679
774
  const existingElements = namedImports.elements.elements;
@@ -733,12 +828,12 @@ class AddImport extends visitor_1.JavaScriptVisitor {
733
828
  });
734
829
  // Replace the statement in the compilation unit
735
830
  draft.statements = compilationUnit.statements.map((s, idx) => idx === i ? Object.assign(Object.assign({}, s), { element: updatedImport }) : s);
736
- });
831
+ }), jsImport.id, p);
737
832
  }
738
833
  // Case 2: Default import without named bindings - add named bindings
739
834
  // Transform: import React from 'react' -> import React, { useState } from 'react'
740
835
  if (importClause.name && !importClause.namedBindings) {
741
- return this.produceJavaScript(compilationUnit, p, async (draft) => {
836
+ return formatMergedImport(await this.produceJavaScript(compilationUnit, p, async (draft) => {
742
837
  const newSpecifier = this.createImportSpecifier();
743
838
  // Get the spaces style for brace spacing
744
839
  const spacesStyle = (0, style_1.getStyle)(style_1.StyleKind.SpacesStyle, compilationUnit);
@@ -774,7 +869,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
774
869
  });
775
870
  // Replace the statement in the compilation unit
776
871
  draft.statements = compilationUnit.statements.map((s, idx) => idx === i ? Object.assign(Object.assign({}, s), { element: updatedImport }) : s);
777
- });
872
+ }), jsImport.id, p);
778
873
  }
779
874
  }
780
875
  }
@@ -1076,10 +1171,8 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1076
1171
  var _a;
1077
1172
  // Determine the appropriate prefix (spacing before the import)
1078
1173
  const prefix = this.determineImportPrefix(compilationUnit, insertionIndex);
1079
- // Create the module specifier
1080
- // For side-effect imports, use emptySpace since space comes from LeftPadded.before
1081
- // For regular imports with import clause, use emptySpace since space comes from LeftPadded.before
1082
- // However, the printer expects the space after 'from' in the literal's prefix
1174
+ // Whitespace throughout follows the parser's placement, so that formatting the result is a
1175
+ // no-op where the file's style already agrees with it.
1083
1176
  // Note: value is the unquoted module name; valueSource and unicodeEscapes are its printed form
1084
1177
  let valueSource = this.moduleValueSource;
1085
1178
  if (valueSource === undefined) {
@@ -1089,7 +1182,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1089
1182
  const moduleSpecifier = {
1090
1183
  id: (0, uuid_1.randomId)(),
1091
1184
  kind: java_1.J.Kind.Literal,
1092
- prefix: this.sideEffectOnly ? java_1.emptySpace : java_1.singleSpace,
1185
+ prefix: java_1.singleSpace,
1093
1186
  markers: markers_1.emptyMarkers,
1094
1187
  value: this.module,
1095
1188
  valueSource,
@@ -1126,7 +1219,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1126
1219
  const namespaceBinding = {
1127
1220
  id: (0, uuid_1.randomId)(),
1128
1221
  kind: tree_1.JS.Kind.Alias,
1129
- prefix: java_1.singleSpace,
1222
+ prefix: this.typeOnly ? java_1.singleSpace : java_1.emptySpace,
1130
1223
  markers: markers_1.emptyMarkers,
1131
1224
  propertyName: (0, java_1.rightPadded)(propertyName, java_1.singleSpace),
1132
1225
  alias: aliasIdentifier
@@ -1134,7 +1227,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1134
1227
  importClause = {
1135
1228
  id: (0, uuid_1.randomId)(),
1136
1229
  kind: tree_1.JS.Kind.ImportClause,
1137
- prefix: this.typeOnly ? java_1.singleSpace : java_1.emptySpace,
1230
+ prefix: java_1.singleSpace,
1138
1231
  markers: markers_1.emptyMarkers,
1139
1232
  typeOnly: this.typeOnly,
1140
1233
  name: undefined,
@@ -1147,7 +1240,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1147
1240
  const defaultName = {
1148
1241
  id: (0, uuid_1.randomId)(),
1149
1242
  kind: java_1.J.Kind.Identifier,
1150
- prefix: java_1.singleSpace,
1243
+ prefix: this.typeOnly ? java_1.singleSpace : java_1.emptySpace,
1151
1244
  markers: markers_1.emptyMarkers,
1152
1245
  annotations: [],
1153
1246
  simpleName: this.bindingName,
@@ -1157,10 +1250,10 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1157
1250
  importClause = {
1158
1251
  id: (0, uuid_1.randomId)(),
1159
1252
  kind: tree_1.JS.Kind.ImportClause,
1160
- prefix: this.typeOnly ? java_1.singleSpace : java_1.emptySpace,
1253
+ prefix: java_1.singleSpace,
1161
1254
  markers: markers_1.emptyMarkers,
1162
1255
  typeOnly: this.typeOnly,
1163
- name: (0, java_1.rightPadded)(defaultName, java_1.emptySpace),
1256
+ name: (0, java_1.rightPadded)(defaultName, java_1.singleSpace),
1164
1257
  namedBindings: undefined
1165
1258
  };
1166
1259
  }
@@ -1176,11 +1269,11 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1176
1269
  const namedImports = {
1177
1270
  id: (0, uuid_1.randomId)(),
1178
1271
  kind: tree_1.JS.Kind.NamedImports,
1179
- prefix: java_1.singleSpace,
1272
+ prefix: java_1.emptySpace,
1180
1273
  markers: markers_1.emptyMarkers,
1181
1274
  elements: {
1182
1275
  kind: java_1.J.Kind.Container,
1183
- before: java_1.emptySpace,
1276
+ before: this.typeOnly ? java_1.singleSpace : java_1.emptySpace,
1184
1277
  elements: [(0, java_1.rightPadded)(importSpecWithSpacing, braceSpace)],
1185
1278
  markers: markers_1.emptyMarkers
1186
1279
  }
@@ -1188,7 +1281,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1188
1281
  importClause = {
1189
1282
  id: (0, uuid_1.randomId)(),
1190
1283
  kind: tree_1.JS.Kind.ImportClause,
1191
- prefix: this.typeOnly ? java_1.singleSpace : java_1.emptySpace,
1284
+ prefix: java_1.singleSpace,
1192
1285
  markers: markers_1.emptyMarkers,
1193
1286
  typeOnly: this.typeOnly,
1194
1287
  name: undefined,
@@ -1204,7 +1297,9 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1204
1297
  importClause,
1205
1298
  moduleSpecifier: {
1206
1299
  kind: java_1.J.Kind.LeftPadded,
1207
- before: java_1.singleSpace,
1300
+ // Bindings end at `}`, so the space before `from` sits here; a default import's name
1301
+ // and a side-effect import's literal carry their own
1302
+ before: (importClause === null || importClause === void 0 ? void 0 : importClause.namedBindings) ? java_1.singleSpace : java_1.emptySpace,
1208
1303
  element: moduleSpecifier,
1209
1304
  markers: markers_1.emptyMarkers
1210
1305
  },
@@ -1426,15 +1521,20 @@ function existingImportBinding(cu, module, member) {
1426
1521
  }
1427
1522
  const localName = importBinds(element, module, member);
1428
1523
  if (localName !== undefined) {
1429
- return { localName, onlyMemberOfStatement: isOnlyMember(element) };
1524
+ return {
1525
+ localName,
1526
+ onlyMemberOfStatement: isOnlyMember(element),
1527
+ aliased: localName !== memberName(member)
1528
+ };
1430
1529
  }
1431
1530
  }
1432
1531
  return undefined;
1433
1532
  }
1434
1533
  /**
1435
- * Binds `member` under the name `local` already carries, so the file's references to it still
1436
- * resolve. `local` itself becomes the alias, keeping the type attribution it holds, and the alias
1437
- * takes its prefix: that whitespace separates the specifier from a `type` keyword before it.
1534
+ * The `member as local` specifier, for an import binding `member` under `local` or an export
1535
+ * publishing `local`'s binding under that name. `local` itself becomes the alias, keeping the type
1536
+ * attribution it holds, and the alias takes its prefix: that whitespace separates the specifier
1537
+ * from a `type` keyword before it.
1438
1538
  */
1439
1539
  function aliasing(local, member) {
1440
1540
  const propertyName = {
@@ -1514,22 +1614,29 @@ function removeBinding(jsImport, member) {
1514
1614
  return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { namedBindings: updatedNamedImports }) });
1515
1615
  }
1516
1616
  /**
1517
- * Moves the binding `from` names to `to`, keeping the local name it already had. In place when
1518
- * the statement that carries it binds nothing else — module and member specifier rewritten there
1519
- * directly; otherwise the old specifier drops and {@link bindImport} queues the replacement,
1520
- * aliased to the preserved name.
1617
+ * Moves the binding `from` names to `to`, binding it under `boundName`. In place when the
1618
+ * statement that carries it binds nothing else — module and member specifier rewritten there
1619
+ * directly; otherwise the old specifier drops and {@link bindImport} queues the replacement.
1620
+ * A `boundName` of its own renames the binding, and the file's references to it follow.
1521
1621
  *
1522
1622
  * Not built on `RemoveImport`/`maybeUnbind`: those only drop a binding once nothing references
1523
1623
  * it, but a rebind moves one that is still in use — removal here has to be unconditional.
1524
1624
  */
1525
1625
  class RebindImport extends visitor_1.JavaScriptVisitor {
1526
- constructor(from, to, localName) {
1626
+ constructor(from, to, localName, boundName) {
1527
1627
  super();
1528
1628
  this.from = from;
1529
1629
  this.to = to;
1530
1630
  this.localName = localName;
1631
+ this.boundName = boundName;
1531
1632
  this.transformedInPlace = false;
1532
1633
  this.typeOnly = false;
1634
+ this.movedTypes = new MovedTypes(this.from, this.to);
1635
+ /** Identifiers settled as references to the binding, so a parent need not settle it again. */
1636
+ this.references = new Set();
1637
+ }
1638
+ get renaming() {
1639
+ return this.boundName !== this.localName;
1533
1640
  }
1534
1641
  async visitJsCompilationUnit(cu, p) {
1535
1642
  const visited = await super.visitJsCompilationUnit(cu, p);
@@ -1537,7 +1644,7 @@ class RebindImport extends visitor_1.JavaScriptVisitor {
1537
1644
  bindImport(this, {
1538
1645
  module: this.to.module,
1539
1646
  member: this.to.member,
1540
- alias: this.localName,
1647
+ alias: this.boundName,
1541
1648
  typeOnly: this.typeOnly,
1542
1649
  onlyIfReferenced: false
1543
1650
  });
@@ -1548,7 +1655,9 @@ class RebindImport extends visitor_1.JavaScriptVisitor {
1548
1655
  var _a, _b;
1549
1656
  const imp = await super.visitImportDeclaration(jsImport, p);
1550
1657
  const key = memberName(this.from.member);
1551
- if (importBinds(imp, this.from.module, this.from.member) === undefined) {
1658
+ // One call moves one binding, and no two imports bind the same local name, so the name
1659
+ // read from the matched statement is what picks it back out of the file.
1660
+ if (importBinds(imp, this.from.module, this.from.member) !== this.localName) {
1552
1661
  return imp;
1553
1662
  }
1554
1663
  // A moved named specifier's own inline `type` marks it type-only even where the clause
@@ -1566,31 +1675,249 @@ class RebindImport extends visitor_1.JavaScriptVisitor {
1566
1675
  const originalSource = literal.valueSource || `"${this.from.module}"`;
1567
1676
  const quoteChar = originalSource.startsWith("'") ? "'" : '"';
1568
1677
  literal.valueSource = `${quoteChar}${this.to.module}${quoteChar}`;
1569
- // A named specifier's local name has to stay put in the source, since it is what the
1570
- // rest of the file already reads; default and namespace imports carry that name on
1571
- // the clause itself, which needs no edit for a module-only move.
1572
- const toKey = memberName(this.to.member);
1573
- if (key !== undefined && key !== '*' && toKey !== undefined && toKey !== key) {
1574
- const importClause = draft.importClause;
1575
- if (((_a = importClause === null || importClause === void 0 ? void 0 : importClause.namedBindings) === null || _a === void 0 ? void 0 : _a.kind) === tree_1.JS.Kind.NamedImports) {
1576
- const namedImports = importClause.namedBindings;
1577
- for (const elem of namedImports.elements.elements) {
1578
- const specifier = elem.element;
1579
- if (specifier.specifier.kind === java_1.J.Kind.Identifier && specifier.specifier.simpleName === key) {
1580
- specifier.specifier = aliasing(specifier.specifier, toKey);
1581
- }
1582
- else if (specifier.specifier.kind === tree_1.JS.Kind.Alias) {
1583
- const aliasNode = specifier.specifier;
1584
- const propertyName = aliasNode.propertyName.element;
1585
- if (propertyName.kind === java_1.J.Kind.Identifier && propertyName.simpleName === key) {
1586
- propertyName.simpleName = toKey;
1587
- }
1588
- }
1589
- }
1678
+ // A default or namespace import carries its local name on the clause itself; a named
1679
+ // one states the member alongside it, in the specifier.
1680
+ if (key === undefined || key === '*') {
1681
+ if (this.boundName !== this.localName) {
1682
+ renameClauseBinding(draft.importClause, key, this.boundName);
1590
1683
  }
1684
+ return;
1591
1685
  }
1686
+ rewriteNamedSpecifier(draft.importClause, key, (_a = memberName(this.to.member)) !== null && _a !== void 0 ? _a : key, this.boundName);
1592
1687
  });
1593
1688
  }
1689
+ async visitIdentifier(identifier, p) {
1690
+ if (!this.referencesBinding(identifier)) {
1691
+ return super.visitIdentifier(identifier, p);
1692
+ }
1693
+ this.references.add(identifier.id);
1694
+ // The name follows the binding, and so does what the name is attributed to; an aliased
1695
+ // binding keeps its name and still resolves somewhere new.
1696
+ const type = await this.movedTypes.visit(identifier.type, undefined);
1697
+ const fieldType = await this.movedTypes.visit(identifier.fieldType, undefined);
1698
+ return !this.renaming && type === identifier.type && fieldType === identifier.fieldType
1699
+ ? identifier
1700
+ : Object.assign(Object.assign({}, identifier), { simpleName: this.boundName, type, fieldType });
1701
+ }
1702
+ async visitMethodInvocation(method, p) {
1703
+ const m = await super.visitMethodInvocation(method, p);
1704
+ if (!this.referencesMovedBinding(m.name)) {
1705
+ return m;
1706
+ }
1707
+ const methodType = await this.movedTypes.visit(m.methodType, undefined);
1708
+ return methodType === m.methodType ? m : Object.assign(Object.assign({}, m), { methodType });
1709
+ }
1710
+ async visitNewClass(newClass, p) {
1711
+ const nc = await super.visitNewClass(newClass, p);
1712
+ if (!this.referencesMovedBinding(nc.class)) {
1713
+ return nc;
1714
+ }
1715
+ const type = await this.movedTypes.visit(nc.type, undefined);
1716
+ const methodType = await this.movedTypes.visit(nc.methodType, undefined);
1717
+ const constructorType = await this.movedTypes.visit(nc.constructorType, undefined);
1718
+ return type === nc.type && methodType === nc.methodType && constructorType === nc.constructorType
1719
+ ? nc
1720
+ : Object.assign(Object.assign({}, nc), { type, methodType, constructorType });
1721
+ }
1722
+ async visitFunctionCall(functionCall, p) {
1723
+ var _a;
1724
+ const fc = await super.visitFunctionCall(functionCall, p);
1725
+ if (!this.referencesMovedBinding((_a = fc.function) === null || _a === void 0 ? void 0 : _a.element)) {
1726
+ return fc;
1727
+ }
1728
+ const methodType = await this.movedTypes.visit(fc.methodType, undefined);
1729
+ return methodType === fc.methodType ? fc : Object.assign(Object.assign({}, fc), { methodType });
1730
+ }
1731
+ /**
1732
+ * Whether `callee` is the identifier `visitIdentifier` settled as a reference to the binding.
1733
+ * A rewrite keeps the identifier's id, so the one it settled is the one still standing here.
1734
+ */
1735
+ referencesMovedBinding(callee) {
1736
+ return (callee === null || callee === void 0 ? void 0 : callee.kind) === java_1.J.Kind.Identifier && this.references.has(callee.id);
1737
+ }
1738
+ /**
1739
+ * A decorator and the other type-name positions reach the tree through this hook, which the
1740
+ * base visitor holds closed; a reference in one of them is a reference like any other.
1741
+ */
1742
+ async visitTypeName(nameTree, p) {
1743
+ return await this.visit(nameTree, p);
1744
+ }
1745
+ /**
1746
+ * An export specifier's own name is the module's public surface, which its consumers read and
1747
+ * the rename leaves alone. See CLAUDE.md: Which name a rebind binds.
1748
+ */
1749
+ async visitExportSpecifier(exportSpecifier, p) {
1750
+ if (!this.renaming) {
1751
+ return super.visitExportSpecifier(exportSpecifier, p);
1752
+ }
1753
+ // `export {a} from "m"` names `m`'s member rather than anything this file binds.
1754
+ const from = this.cursor.firstEnclosing((v) => (v === null || v === void 0 ? void 0 : v.kind) === tree_1.JS.Kind.ExportDeclaration);
1755
+ if ((from === null || from === void 0 ? void 0 : from.moduleSpecifier) !== undefined) {
1756
+ return exportSpecifier;
1757
+ }
1758
+ const specifier = exportSpecifier.specifier;
1759
+ if (specifier.kind === java_1.J.Kind.Identifier && specifier.simpleName === this.localName) {
1760
+ return Object.assign(Object.assign({}, exportSpecifier), { specifier: aliasing(specifier, this.boundName) });
1761
+ }
1762
+ if (specifier.kind === tree_1.JS.Kind.Alias) {
1763
+ const alias = specifier;
1764
+ const propertyName = alias.propertyName.element;
1765
+ return propertyName.kind === java_1.J.Kind.Identifier && propertyName.simpleName === this.localName
1766
+ ? Object.assign(Object.assign({}, exportSpecifier), { specifier: Object.assign(Object.assign({}, alias), { propertyName: Object.assign(Object.assign({}, alias.propertyName), { element: Object.assign(Object.assign({}, propertyName), { simpleName: this.boundName }) }) }) })
1767
+ : exportSpecifier;
1768
+ }
1769
+ return super.visitExportSpecifier(exportSpecifier, p);
1770
+ }
1771
+ /**
1772
+ * Whether the identifier at the cursor stands for the moved binding: the right name, in a
1773
+ * position that references rather than declares, reaching the module scope that binds it.
1774
+ */
1775
+ referencesBinding(identifier) {
1776
+ var _a;
1777
+ return identifier.simpleName === this.localName &&
1778
+ // The specifier binding the name is the one edit that is not a reference to it.
1779
+ !this.cursor.firstEnclosing((v) => (v === null || v === void 0 ? void 0 : v.kind) === tree_1.JS.Kind.Import) &&
1780
+ (0, scope_1.isValueReference)(this.cursor, identifier) &&
1781
+ ((_a = (0, scope_1.scopeOf)(this.cursor).declaringScope(this.localName)) === null || _a === void 0 ? void 0 : _a.kind) === tree_1.JS.Kind.CompilationUnit;
1782
+ }
1783
+ /** A shorthand property's name slot is also the reference to the binding. */
1784
+ async visitPropertyAssignment(propertyAssignment, p) {
1785
+ var _a;
1786
+ const name = propertyAssignment.name.element;
1787
+ if (this.renaming && propertyAssignment.initializer === undefined &&
1788
+ name.kind === java_1.J.Kind.Identifier && name.simpleName === this.localName &&
1789
+ ((_a = (0, scope_1.scopeOf)(this.cursor).declaringScope(this.localName)) === null || _a === void 0 ? void 0 : _a.kind) === tree_1.JS.Kind.CompilationUnit) {
1790
+ // The key names a property rather than the binding, so it carries no attribution,
1791
+ // the same way `aliasing` builds a property name that stands for nothing.
1792
+ return Object.assign(Object.assign({}, propertyAssignment), { name: Object.assign(Object.assign({}, propertyAssignment.name), { element: Object.assign(Object.assign({}, name), { type: undefined, fieldType: undefined }) }), assigmentToken: tree_1.JS.PropertyAssignment.Token.Colon, initializer: Object.assign(Object.assign({}, name), { prefix: java_1.singleSpace, simpleName: this.boundName }) });
1793
+ }
1794
+ return super.visitPropertyAssignment(propertyAssignment, p);
1795
+ }
1594
1796
  }
1595
1797
  exports.RebindImport = RebindImport;
1798
+ /**
1799
+ * Rewrites the attribution a move invalidates, onto the module and member it moved to. Applied at
1800
+ * a reference to the moved binding. See CLAUDE.md: What a rebind's attribution follows.
1801
+ */
1802
+ class MovedTypes extends type_visitor_1.TypeVisitor {
1803
+ constructor(from, to) {
1804
+ super();
1805
+ this.onPath = new Set();
1806
+ this.answered = new Map();
1807
+ // Where no member is named the two keys coincide, and both name the module.
1808
+ this.renamed = new Map([[from.module, to.module], [qualifiedName(from), qualifiedName(to)]]);
1809
+ this.fromMember = declaredMember(from);
1810
+ this.toMember = declaredMember(to);
1811
+ this.toModule = to.module;
1812
+ }
1813
+ /**
1814
+ * A type reached while it is still being visited is a cycle — a class holds a method whose
1815
+ * declaring type is that class — and answers with itself, which is what ends the walk. Every
1816
+ * reference to a binding shares one type, so a completed walk is remembered for the next.
1817
+ */
1818
+ async visit(type, p) {
1819
+ if (type === undefined || this.onPath.has(type)) {
1820
+ return type;
1821
+ }
1822
+ if (this.answered.has(type)) {
1823
+ return this.answered.get(type);
1824
+ }
1825
+ this.onPath.add(type);
1826
+ try {
1827
+ const answer = await super.visit(type, p);
1828
+ // Only the type a walk entered at is remembered: one reached inside it may have met
1829
+ // a back edge, which answers with the type itself and so stands for the path it was on.
1830
+ if (this.onPath.size === 1) {
1831
+ this.answered.set(type, answer);
1832
+ }
1833
+ return answer;
1834
+ }
1835
+ finally {
1836
+ this.onPath.delete(type);
1837
+ }
1838
+ }
1839
+ async visitClass(aClass, p) {
1840
+ const visited = await super.visitClass(aClass, p);
1841
+ const moved = this.renamed.get(visited.fullyQualifiedName);
1842
+ return moved === undefined || moved === visited.fullyQualifiedName
1843
+ ? visited
1844
+ : Object.assign(Object.assign({}, visited), { fullyQualifiedName: moved });
1845
+ }
1846
+ async visitMethod(method, p) {
1847
+ const visited = await super.visitMethod(method, p);
1848
+ return this.declaresMovedMember(visited.name, visited.declaringType)
1849
+ ? Object.assign(Object.assign({}, visited), { name: this.toMember })
1850
+ : visited;
1851
+ }
1852
+ async visitVariable(variable, p) {
1853
+ const visited = await super.visitVariable(variable, p);
1854
+ return this.declaresMovedMember(visited.name, visited.owner)
1855
+ ? Object.assign(Object.assign({}, visited), { name: this.toMember })
1856
+ : visited;
1857
+ }
1858
+ /**
1859
+ * Whether `name`, declared on `owner`, is the member that moved. `owner` has already followed
1860
+ * the move by the time this runs, so it is the module moved *to* that it has to name. A
1861
+ * default or namespace binding declares no member, so there is no name for one to take.
1862
+ */
1863
+ declaresMovedMember(name, owner) {
1864
+ return this.toMember !== undefined && name === this.fromMember && this.toMember !== this.fromMember &&
1865
+ owner !== undefined && java_1.Type.isFullyQualified(owner) &&
1866
+ java_1.Type.FullyQualified.getFullyQualifiedName(owner) === this.toModule;
1867
+ }
1868
+ }
1869
+ /** A member's qualified name, or the module's own where no member is named. */
1870
+ function qualifiedName(binding) {
1871
+ const key = memberName(binding.member);
1872
+ return key === undefined || key === '*' ? binding.module : `${binding.module}.${key}`;
1873
+ }
1874
+ /** The member the module declares, where the binding names one: a whole module declares none. */
1875
+ function declaredMember(binding) {
1876
+ const key = memberName(binding.member);
1877
+ return key === undefined || key === '*' ? undefined : key;
1878
+ }
1879
+ /** Renames the identifier a default (`key` undefined) or namespace clause binds its module under. */
1880
+ function renameClauseBinding(importClause, key, boundName) {
1881
+ var _a, _b;
1882
+ const bound = key === undefined
1883
+ ? (_a = importClause === null || importClause === void 0 ? void 0 : importClause.name) === null || _a === void 0 ? void 0 : _a.element
1884
+ : (_b = importClause === null || importClause === void 0 ? void 0 : importClause.namedBindings) === null || _b === void 0 ? void 0 : _b.alias;
1885
+ if ((bound === null || bound === void 0 ? void 0 : bound.kind) === java_1.J.Kind.Identifier) {
1886
+ bound.simpleName = boundName;
1887
+ }
1888
+ }
1889
+ /**
1890
+ * Rewrites the specifier importing `key` to import `member` under `boundName` — a bare `{member}`
1891
+ * where the two agree, since an alias saying the same thing is noise the source never had.
1892
+ */
1893
+ function rewriteNamedSpecifier(importClause, key, member, boundName) {
1894
+ var _a, _b;
1895
+ if (((_a = importClause === null || importClause === void 0 ? void 0 : importClause.namedBindings) === null || _a === void 0 ? void 0 : _a.kind) !== tree_1.JS.Kind.NamedImports) {
1896
+ return;
1897
+ }
1898
+ for (const elem of importClause.namedBindings.elements.elements) {
1899
+ const specifier = elem.element;
1900
+ const node = specifier.specifier;
1901
+ if (!namedSpecifierImports(node, key)) {
1902
+ continue;
1903
+ }
1904
+ const alias = node.kind === tree_1.JS.Kind.Alias ? node : undefined;
1905
+ const local = ((_b = alias === null || alias === void 0 ? void 0 : alias.alias) !== null && _b !== void 0 ? _b : node);
1906
+ if (local.kind !== java_1.J.Kind.Identifier) {
1907
+ continue;
1908
+ }
1909
+ if (boundName === member) {
1910
+ // An alias node holds the specifier's leading whitespace, so the bare identifier
1911
+ // standing in for it takes that prefix.
1912
+ specifier.specifier = Object.assign(Object.assign({}, local), { prefix: (alias !== null && alias !== void 0 ? alias : local).prefix, simpleName: member });
1913
+ }
1914
+ else if (alias) {
1915
+ alias.propertyName.element.simpleName = member;
1916
+ local.simpleName = boundName;
1917
+ }
1918
+ else {
1919
+ specifier.specifier = aliasing(Object.assign(Object.assign({}, local), { simpleName: boundName }), member);
1920
+ }
1921
+ }
1922
+ }
1596
1923
  //# sourceMappingURL=add-import.js.map