@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
@@ -1,11 +1,14 @@
1
1
  import {JavaScriptVisitor} from "./visitor";
2
- import {ElementRemovalFormatter, emptySpace, isIdentifier, J, rightPadded, singleSpace, space, Statement, Type} from "../java";
2
+ import {ElementRemovalFormatter, emptySpace, isIdentifier, J, NameTree, rightPadded, singleSpace, space, Statement, Type} from "../java";
3
3
  import {JS, JSX} from "./tree";
4
- import {randomId} from "../uuid";
4
+ import {randomId, UUID} from "../uuid";
5
5
  import {emptyMarkers, markers} from "../markers";
6
- import {getStyle, PrettierStyle, SpacesStyle, StyleKind} from "./style";
7
- import {bindingNames, compilationUnitOf, cursorOf, declarationsOf, deconflict, namesDeclaredIn} from "./scope";
6
+ import {getStyle, SpacesStyle, StyleKind} from "./style";
7
+ import {bindingNames, compilationUnitOf, cursorOf, declarationsOf, deconflict, isValueReference, namesDeclaredIn, scopeOf, walk} from "./scope";
8
8
  import {create as produce, Draft} from "mutative";
9
+ import {TypeVisitor} from "../java/type-visitor";
10
+ import {autoFormat} from "./format";
11
+ import {getPrettierStyle} from "./format/prettier-format";
9
12
 
10
13
  export type QuoteChar = "'" | '"';
11
14
 
@@ -18,8 +21,9 @@ export enum ImportStyle {
18
21
 
19
22
  export interface AddImportOptions {
20
23
  /** The module name (e.g., 'fs', 'react') to import from.
21
- * Pass a `J.Literal` to reuse its source form verbatim, which carries the quoting,
22
- * escapes and unicode form of a specifier being moved from elsewhere in the source. */
24
+ * Pass a `J.Literal` to reuse its source form, which carries the escapes and unicode form of a
25
+ * specifier being moved from elsewhere in the source. A Prettier configuration settles the
26
+ * quoting; see `quoteStyle`. */
23
27
  module: string | J.Literal;
24
28
 
25
29
  /** Optionally, the specific member to import from the module.
@@ -61,7 +65,8 @@ export interface AddImportOptions {
61
65
  style?: ImportStyle;
62
66
 
63
67
  /** Quote character for the module specifier. If not specified, detected from the file.
64
- * A `J.Literal` module carries its own quoting and takes precedence over this. */
68
+ * A `J.Literal` module carries its own quoting and takes precedence over this. A Prettier
69
+ * configuration outranks both, since running Prettier would arrive at its quote anyway. */
65
70
  quoteStyle?: QuoteChar;
66
71
  }
67
72
 
@@ -126,15 +131,13 @@ export function bindImport(
126
131
  return derived;
127
132
  }
128
133
 
129
- // An import already serving this request answers it; queuing one would, on the next cycle,
130
- // derive a suffixed name from the binding this call just added. A caller that named a preference
131
- // takes whatever comes back; one that did not assumes the name it derived, so a binding under
132
- // any other name would leave the references it emits unbound.
134
+ // An import already serving this request answers it, under whatever name the file gave it —
135
+ // only a pinned alias, returned above, asks for a binding of its own. Queuing a second import
136
+ // would, on the next cycle, derive a suffixed name from the binding this call just added.
137
+ const scope = scopeOf(cursor);
133
138
  for (const binding of moduleScopeBindings(cu)) {
134
139
  if (binding.module === module && binding.member === memberName(options.member) &&
135
- binding.typeOnly === typeOnly &&
136
- (options.preferredName !== undefined || anyNameAnswers(options) ||
137
- binding.name === derived)) {
140
+ binding.typeOnly === typeOnly && scope.declaringScope(binding.name) === cu) {
138
141
  return binding.name;
139
142
  }
140
143
  }
@@ -239,6 +242,65 @@ export function moduleScopeBindings(cu: JS.CompilationUnit): ModuleScopeBinding[
239
242
  return bindings;
240
243
  }
241
244
 
245
+ /** Whether a top-level statement already marks the file as a module: an import or any export form. */
246
+ export function hasEsmSyntax(cu: JS.CompilationUnit): boolean {
247
+ return cu.statements.some(stmt => {
248
+ const element = stmt.element;
249
+ // `export {a, b}`, `export * from` and `export default` are their own statement kinds;
250
+ // `export class`/`function`/`const` instead carry `export` as a modifier on the
251
+ // declaration itself, the same way TypeScript's own AST models it.
252
+ const modifiers = (element as {modifiers?: J.Modifier[]} | undefined)?.modifiers;
253
+ return element?.kind === JS.Kind.Import ||
254
+ element?.kind === JS.Kind.ExportDeclaration ||
255
+ element?.kind === JS.Kind.ExportAssignment ||
256
+ (modifiers?.some(m => m.keyword === "export") ?? false);
257
+ }) || hasTopLevelAwait(cu);
258
+ }
259
+
260
+ /**
261
+ * Whether a statement holds an `await` outside any function of its own — legal only at module
262
+ * top level, unlike an `await` inside an `async function`, which says nothing about the file.
263
+ */
264
+ function hasTopLevelAwait(cu: JS.CompilationUnit): boolean {
265
+ let found = false;
266
+ walk(cu.statements, node => {
267
+ if (found) {
268
+ return false;
269
+ }
270
+ if (node.kind === JS.Kind.Await) {
271
+ found = true;
272
+ return false;
273
+ }
274
+ return node.kind !== J.Kind.MethodDeclaration && node.kind !== J.Kind.Lambda;
275
+ });
276
+ return found;
277
+ }
278
+
279
+ /** Whether the file binds its modules with `require`, which decides whether a create is possible. */
280
+ export function isCommonJs(cu: JS.CompilationUnit): boolean {
281
+ if (cu.sourcePath.endsWith(".cjs") || cu.sourcePath.endsWith(".cts")) {
282
+ return true;
283
+ }
284
+ // Node treats these as ES modules regardless of what they contain, the same way
285
+ // `determineImportStyle` reads them as ES6-preferring; `.js`/`.ts`/`.tsx` stay ambiguous and
286
+ // fall through to the statements below.
287
+ if (cu.sourcePath.endsWith(".mjs") || cu.sourcePath.endsWith(".mts")) {
288
+ return false;
289
+ }
290
+ if (hasEsmSyntax(cu)) {
291
+ return false;
292
+ }
293
+ return cu.statements.some(stmt =>
294
+ declarationsOf(stmt.element).some(d => requiredModuleOfDeclaration(d) !== undefined));
295
+ }
296
+
297
+ /** The module a `const X = require('m')` declaration names, for the one variable it declares. */
298
+ export function requiredModuleOfDeclaration(declaration: J.VariableDeclarations): string | undefined {
299
+ return declaration.variables.length === 1
300
+ ? requiredModule(declaration.variables[0].element?.initializer?.element)
301
+ : undefined;
302
+ }
303
+
242
304
  /** The module a `require('...')` initializer names, `undefined` for an initializer that is anything else. */
243
305
  function requiredModule(initializer: J | undefined): string | undefined {
244
306
  return initializer?.kind === J.Kind.MethodInvocation
@@ -332,12 +394,12 @@ function importBindings(jsImport: JS.Import): ModuleScopeBinding[] {
332
394
  }
333
395
 
334
396
  /**
335
- * Names in scope, plus those pending `AddImport`s on the `afterVisit` queue have claimed. A queued
397
+ * `names`, plus those pending `AddImport`s on the `afterVisit` queue have claimed. A queued
336
398
  * `RemoveImport` does not free one: it removes only what the file leaves unused, and binding a name
337
399
  * it keeps is an error, where an unnecessary suffix merely reads oddly.
338
400
  */
339
- function takenNames(inScope: ReadonlySet<string>, visitor: JavaScriptVisitor<any>): Set<string> {
340
- const taken = new Set<string>(inScope);
401
+ export function takenNames(names: ReadonlySet<string>, visitor: JavaScriptVisitor<any>): Set<string> {
402
+ const taken = new Set<string>(names);
341
403
 
342
404
  for (const v of visitor.afterVisit || []) {
343
405
  if (v instanceof AddImport && v.bindingName) {
@@ -348,6 +410,12 @@ function takenNames(inScope: ReadonlySet<string>, visitor: JavaScriptVisitor<any
348
410
  return taken;
349
411
  }
350
412
 
413
+ /** As {@link takenNames}, for a caller asking about one name rather than probing repeatedly. */
414
+ export function nameTaken(name: string, names: ReadonlySet<string>, visitor: JavaScriptVisitor<any>): boolean {
415
+ return names.has(name) ||
416
+ (visitor.afterVisit || []).some(v => v instanceof AddImport && v.bindingName === name);
417
+ }
418
+
351
419
  /**
352
420
  * The parser lifts surrogate pairs out of `valueSource` into `unicodeEscapes` and, when it does,
353
421
  * sets `value` to the quoted source (`parser.ts` `mapLiteral`), so neither field alone is the
@@ -453,8 +521,8 @@ async function detectQuote(cu: JS.CompilationUnit): Promise<QuoteChar> {
453
521
  return specifierQuote;
454
522
  }
455
523
 
456
- const prettier = getStyle(StyleKind.PrettierStyle, cu) as PrettierStyle | undefined;
457
- if (prettier?.kind === StyleKind.PrettierStyle && !prettier.ignored) {
524
+ const prettier = getPrettierStyle(cu);
525
+ if (prettier && !prettier.ignored) {
458
526
  const singleQuote = prettier.config.singleQuote;
459
527
  if (typeof singleQuote === 'boolean') {
460
528
  return singleQuote ? "'" : '"';
@@ -464,6 +532,33 @@ async function detectQuote(cu: JS.CompilationUnit): Promise<QuoteChar> {
464
532
  return double > single ? '"' : "'";
465
533
  }
466
534
 
535
+ /**
536
+ * Lays out the import statement `AddImport` wrote, so that the file's own style — a Prettier
537
+ * configuration's brace spacing and print width, say — reaches it.
538
+ */
539
+ async function formatImport<P>(cu: JS.CompilationUnit, id: UUID, p: P): Promise<JS.CompilationUnit> {
540
+ return await new class extends JavaScriptVisitor<P> {
541
+ override async visitImportDeclaration(jsImport: JS.Import, p: P): Promise<J | undefined> {
542
+ return jsImport.id === id ? await autoFormat(jsImport, p, undefined, this.cursor.parent) : jsImport;
543
+ }
544
+ }().visit(cu, p) as JS.CompilationUnit;
545
+ }
546
+
547
+ /**
548
+ * As {@link formatImport}, for an import that gained specifiers. Short of a Prettier configuration
549
+ * deciding the whole line, the statement's own text says more about its spacing than a
550
+ * repository-wide style does, and the merge reads it directly.
551
+ */
552
+ async function formatMergedImport<P>(cu: JS.CompilationUnit, id: UUID, p: P): Promise<JS.CompilationUnit> {
553
+ return getPrettierStyle(cu)?.ignored === false ? formatImport(cu, id, p) : cu;
554
+ }
555
+
556
+ /**
557
+ * Adds the import {@link AddImportOptions} describes, under the name derived there. An import
558
+ * already in the file answers where it binds that same name, or where the request expressed no
559
+ * preference at all. Reuse under some other name belongs to {@link bindImport}, whose caller
560
+ * learns which name came back; a recipe driving this visitor is stuck with the one it derived.
561
+ */
467
562
  export class AddImport<P> extends JavaScriptVisitor<P> {
468
563
  readonly module: string;
469
564
  /** Set when the caller supplied the module specifier as a literal; printed verbatim. */
@@ -710,20 +805,20 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
710
805
  }
711
806
  }
712
807
 
713
- // Add the import using the appropriate style
714
- if (importStyle === ImportStyle.CommonJS) {
715
- // TODO: Implement CommonJS require creation
716
- // For now, fall back to ES6 imports
717
- // return this.addCommonJSRequire(compilationUnit, p);
808
+ // TODO: create a `require` here. Until then the request goes unserved, since `import` would
809
+ // make a file that binds its modules with `require` an ES module and change how everything
810
+ // in it loads — `maybeBind` refuses it for the same reason. An explicit ES6 `style`
811
+ // overrides, which is how a caller converting the file to ESM asks for one.
812
+ if (importStyle === ImportStyle.CommonJS && isCommonJs(compilationUnit)) {
813
+ return compilationUnit;
718
814
  }
719
815
 
720
816
  // Add ES6 import (handles ES6Named, ES6Namespace, ES6Default)
721
- return this.produceJavaScript(compilationUnit, p, async draft => {
722
- // Find the position to insert the import
723
- const insertionIndex = this.findImportInsertionIndex(compilationUnit);
724
-
725
- const newImport = await this.createImportStatement(compilationUnit, insertionIndex, p);
817
+ // Find the position to insert the import
818
+ const insertionIndex = this.findImportInsertionIndex(compilationUnit);
819
+ const newImport = await this.createImportStatement(compilationUnit, insertionIndex, p);
726
820
 
821
+ const withImport = await this.produceJavaScript(compilationUnit, p, async draft => {
727
822
  // Insert the import at the appropriate position
728
823
  // Create semicolon marker for the import statement
729
824
  const semicolonMarkers = markers({
@@ -780,6 +875,8 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
780
875
  }
781
876
  }
782
877
  });
878
+
879
+ return formatImport(withImport, newImport.id, p);
783
880
  }
784
881
 
785
882
  /**
@@ -823,7 +920,7 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
823
920
  }
824
921
 
825
922
  // We found a matching import with named bindings - merge into it
826
- return this.produceJavaScript(compilationUnit, p, async draft => {
923
+ return formatMergedImport(await this.produceJavaScript(compilationUnit, p, async draft => {
827
924
  const namedImports = importClause.namedBindings as JS.NamedImports;
828
925
  const existingElements = namedImports.elements.elements;
829
926
 
@@ -896,13 +993,13 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
896
993
  draft.statements = compilationUnit.statements.map((s, idx) =>
897
994
  idx === i ? {...s, element: updatedImport} : s
898
995
  );
899
- });
996
+ }), jsImport.id, p);
900
997
  }
901
998
 
902
999
  // Case 2: Default import without named bindings - add named bindings
903
1000
  // Transform: import React from 'react' -> import React, { useState } from 'react'
904
1001
  if (importClause.name && !importClause.namedBindings) {
905
- return this.produceJavaScript(compilationUnit, p, async draft => {
1002
+ return formatMergedImport(await this.produceJavaScript(compilationUnit, p, async draft => {
906
1003
  const newSpecifier = this.createImportSpecifier();
907
1004
 
908
1005
  // Get the spaces style for brace spacing
@@ -951,7 +1048,7 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
951
1048
  draft.statements = compilationUnit.statements.map((s, idx) =>
952
1049
  idx === i ? {...s, element: updatedImport} : s
953
1050
  );
954
- });
1051
+ }), jsImport.id, p);
955
1052
  }
956
1053
  }
957
1054
  }
@@ -1280,10 +1377,8 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
1280
1377
  // Determine the appropriate prefix (spacing before the import)
1281
1378
  const prefix = this.determineImportPrefix(compilationUnit, insertionIndex);
1282
1379
 
1283
- // Create the module specifier
1284
- // For side-effect imports, use emptySpace since space comes from LeftPadded.before
1285
- // For regular imports with import clause, use emptySpace since space comes from LeftPadded.before
1286
- // However, the printer expects the space after 'from' in the literal's prefix
1380
+ // Whitespace throughout follows the parser's placement, so that formatting the result is a
1381
+ // no-op where the file's style already agrees with it.
1287
1382
  // Note: value is the unquoted module name; valueSource and unicodeEscapes are its printed form
1288
1383
  let valueSource = this.moduleValueSource;
1289
1384
  if (valueSource === undefined) {
@@ -1293,7 +1388,7 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
1293
1388
  const moduleSpecifier: J.Literal = {
1294
1389
  id: randomId(),
1295
1390
  kind: J.Kind.Literal,
1296
- prefix: this.sideEffectOnly ? emptySpace : singleSpace,
1391
+ prefix: singleSpace,
1297
1392
  markers: emptyMarkers,
1298
1393
  value: this.module,
1299
1394
  valueSource,
@@ -1333,7 +1428,7 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
1333
1428
  const namespaceBinding: JS.Alias = {
1334
1429
  id: randomId(),
1335
1430
  kind: JS.Kind.Alias,
1336
- prefix: singleSpace,
1431
+ prefix: this.typeOnly ? singleSpace : emptySpace,
1337
1432
  markers: emptyMarkers,
1338
1433
  propertyName: rightPadded(propertyName, singleSpace),
1339
1434
  alias: aliasIdentifier
@@ -1342,7 +1437,7 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
1342
1437
  importClause = {
1343
1438
  id: randomId(),
1344
1439
  kind: JS.Kind.ImportClause,
1345
- prefix: this.typeOnly ? singleSpace : emptySpace,
1440
+ prefix: singleSpace,
1346
1441
  markers: emptyMarkers,
1347
1442
  typeOnly: this.typeOnly,
1348
1443
  name: undefined,
@@ -1354,7 +1449,7 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
1354
1449
  const defaultName: J.Identifier = {
1355
1450
  id: randomId(),
1356
1451
  kind: J.Kind.Identifier,
1357
- prefix: singleSpace,
1452
+ prefix: this.typeOnly ? singleSpace : emptySpace,
1358
1453
  markers: emptyMarkers,
1359
1454
  annotations: [],
1360
1455
  simpleName: this.bindingName!,
@@ -1365,10 +1460,10 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
1365
1460
  importClause = {
1366
1461
  id: randomId(),
1367
1462
  kind: JS.Kind.ImportClause,
1368
- prefix: this.typeOnly ? singleSpace : emptySpace,
1463
+ prefix: singleSpace,
1369
1464
  markers: emptyMarkers,
1370
1465
  typeOnly: this.typeOnly,
1371
- name: rightPadded(defaultName, emptySpace),
1466
+ name: rightPadded(defaultName, singleSpace),
1372
1467
  namedBindings: undefined
1373
1468
  };
1374
1469
  } else {
@@ -1386,11 +1481,11 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
1386
1481
  const namedImports: JS.NamedImports = {
1387
1482
  id: randomId(),
1388
1483
  kind: JS.Kind.NamedImports,
1389
- prefix: singleSpace,
1484
+ prefix: emptySpace,
1390
1485
  markers: emptyMarkers,
1391
1486
  elements: {
1392
1487
  kind: J.Kind.Container,
1393
- before: emptySpace,
1488
+ before: this.typeOnly ? singleSpace : emptySpace,
1394
1489
  elements: [rightPadded(importSpecWithSpacing, braceSpace)],
1395
1490
  markers: emptyMarkers
1396
1491
  }
@@ -1399,7 +1494,7 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
1399
1494
  importClause = {
1400
1495
  id: randomId(),
1401
1496
  kind: JS.Kind.ImportClause,
1402
- prefix: this.typeOnly ? singleSpace : emptySpace,
1497
+ prefix: singleSpace,
1403
1498
  markers: emptyMarkers,
1404
1499
  typeOnly: this.typeOnly,
1405
1500
  name: undefined,
@@ -1416,7 +1511,9 @@ export class AddImport<P> extends JavaScriptVisitor<P> {
1416
1511
  importClause,
1417
1512
  moduleSpecifier: {
1418
1513
  kind: J.Kind.LeftPadded,
1419
- before: singleSpace,
1514
+ // Bindings end at `}`, so the space before `from` sits here; a default import's name
1515
+ // and a side-effect import's literal carry their own
1516
+ before: importClause?.namedBindings ? singleSpace : emptySpace,
1420
1517
  element: moduleSpecifier,
1421
1518
  markers: emptyMarkers
1422
1519
  },
@@ -1642,6 +1739,9 @@ function isOnlyMember(jsImport: JS.Import): boolean {
1642
1739
  export interface ExistingImportBinding {
1643
1740
  localName: string;
1644
1741
  onlyMemberOfStatement: boolean;
1742
+
1743
+ /** Whether the source states this local name — `import {a as b}` — or takes it from the member. */
1744
+ aliased: boolean;
1645
1745
  }
1646
1746
 
1647
1747
  /**
@@ -1660,16 +1760,21 @@ export function existingImportBinding(
1660
1760
  }
1661
1761
  const localName = importBinds(element as JS.Import, module, member);
1662
1762
  if (localName !== undefined) {
1663
- return {localName, onlyMemberOfStatement: isOnlyMember(element as JS.Import)};
1763
+ return {
1764
+ localName,
1765
+ onlyMemberOfStatement: isOnlyMember(element as JS.Import),
1766
+ aliased: localName !== memberName(member)
1767
+ };
1664
1768
  }
1665
1769
  }
1666
1770
  return undefined;
1667
1771
  }
1668
1772
 
1669
1773
  /**
1670
- * Binds `member` under the name `local` already carries, so the file's references to it still
1671
- * resolve. `local` itself becomes the alias, keeping the type attribution it holds, and the alias
1672
- * takes its prefix: that whitespace separates the specifier from a `type` keyword before it.
1774
+ * The `member as local` specifier, for an import binding `member` under `local` or an export
1775
+ * publishing `local`'s binding under that name. `local` itself becomes the alias, keeping the type
1776
+ * attribution it holds, and the alias takes its prefix: that whitespace separates the specifier
1777
+ * from a `type` keyword before it.
1673
1778
  */
1674
1779
  function aliasing(local: J.Identifier, member: string): JS.Alias {
1675
1780
  const propertyName: J.Identifier = {
@@ -1755,10 +1860,10 @@ function removeBinding(jsImport: JS.Import, member: string | undefined): JS.Impo
1755
1860
  }
1756
1861
 
1757
1862
  /**
1758
- * Moves the binding `from` names to `to`, keeping the local name it already had. In place when
1759
- * the statement that carries it binds nothing else — module and member specifier rewritten there
1760
- * directly; otherwise the old specifier drops and {@link bindImport} queues the replacement,
1761
- * aliased to the preserved name.
1863
+ * Moves the binding `from` names to `to`, binding it under `boundName`. In place when the
1864
+ * statement that carries it binds nothing else — module and member specifier rewritten there
1865
+ * directly; otherwise the old specifier drops and {@link bindImport} queues the replacement.
1866
+ * A `boundName` of its own renames the binding, and the file's references to it follow.
1762
1867
  *
1763
1868
  * Not built on `RemoveImport`/`maybeUnbind`: those only drop a binding once nothing references
1764
1869
  * it, but a rebind moves one that is still in use — removal here has to be unconditional.
@@ -1767,13 +1872,21 @@ export class RebindImport<P> extends JavaScriptVisitor<P> {
1767
1872
  constructor(
1768
1873
  readonly from: {module: string; member?: string},
1769
1874
  readonly to: {module: string; member?: string},
1770
- readonly localName: string
1875
+ readonly localName: string,
1876
+ readonly boundName: string
1771
1877
  ) {
1772
1878
  super();
1773
1879
  }
1774
1880
 
1775
1881
  private transformedInPlace = false;
1776
1882
  private typeOnly = false;
1883
+ private readonly movedTypes = new MovedTypes(this.from, this.to);
1884
+ /** Identifiers settled as references to the binding, so a parent need not settle it again. */
1885
+ private readonly references = new Set<string>();
1886
+
1887
+ private get renaming(): boolean {
1888
+ return this.boundName !== this.localName;
1889
+ }
1777
1890
 
1778
1891
  override async visitJsCompilationUnit(cu: JS.CompilationUnit, p: P): Promise<J | undefined> {
1779
1892
  const visited = await super.visitJsCompilationUnit(cu, p) as JS.CompilationUnit;
@@ -1781,7 +1894,7 @@ export class RebindImport<P> extends JavaScriptVisitor<P> {
1781
1894
  bindImport(this, {
1782
1895
  module: this.to.module,
1783
1896
  member: this.to.member,
1784
- alias: this.localName,
1897
+ alias: this.boundName,
1785
1898
  typeOnly: this.typeOnly,
1786
1899
  onlyIfReferenced: false
1787
1900
  });
@@ -1793,7 +1906,9 @@ export class RebindImport<P> extends JavaScriptVisitor<P> {
1793
1906
  const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
1794
1907
 
1795
1908
  const key = memberName(this.from.member);
1796
- if (importBinds(imp, this.from.module, this.from.member) === undefined) {
1909
+ // One call moves one binding, and no two imports bind the same local name, so the name
1910
+ // read from the matched statement is what picks it back out of the file.
1911
+ if (importBinds(imp, this.from.module, this.from.member) !== this.localName) {
1797
1912
  return imp;
1798
1913
  }
1799
1914
  // A moved named specifier's own inline `type` marks it type-only even where the clause
@@ -1813,28 +1928,284 @@ export class RebindImport<P> extends JavaScriptVisitor<P> {
1813
1928
  const quoteChar = originalSource.startsWith("'") ? "'" : '"';
1814
1929
  literal.valueSource = `${quoteChar}${this.to.module}${quoteChar}`;
1815
1930
 
1816
- // A named specifier's local name has to stay put in the source, since it is what the
1817
- // rest of the file already reads; default and namespace imports carry that name on
1818
- // the clause itself, which needs no edit for a module-only move.
1819
- const toKey = memberName(this.to.member);
1820
- if (key !== undefined && key !== '*' && toKey !== undefined && toKey !== key) {
1821
- const importClause = draft.importClause;
1822
- if (importClause?.namedBindings?.kind === JS.Kind.NamedImports) {
1823
- const namedImports = importClause.namedBindings as Draft<JS.NamedImports>;
1824
- for (const elem of namedImports.elements.elements) {
1825
- const specifier = elem.element;
1826
- if (specifier.specifier.kind === J.Kind.Identifier && specifier.specifier.simpleName === key) {
1827
- specifier.specifier = aliasing(specifier.specifier as Draft<J.Identifier>, toKey) as Draft<JS.Alias>;
1828
- } else if (specifier.specifier.kind === JS.Kind.Alias) {
1829
- const aliasNode = specifier.specifier as Draft<JS.Alias>;
1830
- const propertyName = aliasNode.propertyName.element;
1831
- if (propertyName.kind === J.Kind.Identifier && propertyName.simpleName === key) {
1832
- propertyName.simpleName = toKey;
1833
- }
1834
- }
1835
- }
1931
+ // A default or namespace import carries its local name on the clause itself; a named
1932
+ // one states the member alongside it, in the specifier.
1933
+ if (key === undefined || key === '*') {
1934
+ if (this.boundName !== this.localName) {
1935
+ renameClauseBinding(draft.importClause, key, this.boundName);
1836
1936
  }
1937
+ return;
1837
1938
  }
1939
+ rewriteNamedSpecifier(draft.importClause, key, memberName(this.to.member) ?? key, this.boundName);
1838
1940
  });
1839
1941
  }
1942
+
1943
+ override async visitIdentifier(identifier: J.Identifier, p: P): Promise<J | undefined> {
1944
+ if (!this.referencesBinding(identifier)) {
1945
+ return super.visitIdentifier(identifier, p);
1946
+ }
1947
+ this.references.add(identifier.id);
1948
+ // The name follows the binding, and so does what the name is attributed to; an aliased
1949
+ // binding keeps its name and still resolves somewhere new.
1950
+ const type = await this.movedTypes.visit(identifier.type, undefined);
1951
+ const fieldType = await this.movedTypes.visit(identifier.fieldType, undefined) as Type.Variable | undefined;
1952
+ return !this.renaming && type === identifier.type && fieldType === identifier.fieldType
1953
+ ? identifier
1954
+ : {...identifier, simpleName: this.boundName, type, fieldType} as J.Identifier;
1955
+ }
1956
+
1957
+ override async visitMethodInvocation(method: J.MethodInvocation, p: P): Promise<J | undefined> {
1958
+ const m = await super.visitMethodInvocation(method, p) as J.MethodInvocation;
1959
+ if (!this.referencesMovedBinding(m.name)) {
1960
+ return m;
1961
+ }
1962
+ const methodType = await this.movedTypes.visit(m.methodType, undefined) as Type.Method | undefined;
1963
+ return methodType === m.methodType ? m : {...m, methodType} as J.MethodInvocation;
1964
+ }
1965
+
1966
+ override async visitNewClass(newClass: J.NewClass, p: P): Promise<J | undefined> {
1967
+ const nc = await super.visitNewClass(newClass, p) as J.NewClass;
1968
+ if (!this.referencesMovedBinding(nc.class)) {
1969
+ return nc;
1970
+ }
1971
+ const type = await this.movedTypes.visit(nc.type, undefined);
1972
+ const methodType = await this.movedTypes.visit(nc.methodType, undefined) as Type.Method | undefined;
1973
+ const constructorType = await this.movedTypes.visit(nc.constructorType, undefined) as Type.Method | undefined;
1974
+ return type === nc.type && methodType === nc.methodType && constructorType === nc.constructorType
1975
+ ? nc
1976
+ : {...nc, type, methodType, constructorType} as J.NewClass;
1977
+ }
1978
+
1979
+ override async visitFunctionCall(functionCall: JS.FunctionCall, p: P): Promise<J | undefined> {
1980
+ const fc = await super.visitFunctionCall(functionCall, p) as JS.FunctionCall;
1981
+ if (!this.referencesMovedBinding(fc.function?.element)) {
1982
+ return fc;
1983
+ }
1984
+ const methodType = await this.movedTypes.visit(fc.methodType, undefined) as Type.Method | undefined;
1985
+ return methodType === fc.methodType ? fc : {...fc, methodType} as JS.FunctionCall;
1986
+ }
1987
+
1988
+ /**
1989
+ * Whether `callee` is the identifier `visitIdentifier` settled as a reference to the binding.
1990
+ * A rewrite keeps the identifier's id, so the one it settled is the one still standing here.
1991
+ */
1992
+ private referencesMovedBinding(callee: J | undefined): boolean {
1993
+ return callee?.kind === J.Kind.Identifier && this.references.has(callee.id);
1994
+ }
1995
+
1996
+ /**
1997
+ * A decorator and the other type-name positions reach the tree through this hook, which the
1998
+ * base visitor holds closed; a reference in one of them is a reference like any other.
1999
+ */
2000
+ protected override async visitTypeName<N extends NameTree>(nameTree: N, p: P): Promise<N> {
2001
+ return await this.visit(nameTree, p) as N;
2002
+ }
2003
+
2004
+ /**
2005
+ * An export specifier's own name is the module's public surface, which its consumers read and
2006
+ * the rename leaves alone. See CLAUDE.md: Which name a rebind binds.
2007
+ */
2008
+ override async visitExportSpecifier(exportSpecifier: JS.ExportSpecifier, p: P): Promise<J | undefined> {
2009
+ if (!this.renaming) {
2010
+ return super.visitExportSpecifier(exportSpecifier, p);
2011
+ }
2012
+ // `export {a} from "m"` names `m`'s member rather than anything this file binds.
2013
+ const from = this.cursor.firstEnclosing(
2014
+ (v): v is JS.ExportDeclaration => (v as J | undefined)?.kind === JS.Kind.ExportDeclaration);
2015
+ if (from?.moduleSpecifier !== undefined) {
2016
+ return exportSpecifier;
2017
+ }
2018
+ const specifier = exportSpecifier.specifier;
2019
+ if (specifier.kind === J.Kind.Identifier && (specifier as J.Identifier).simpleName === this.localName) {
2020
+ return {...exportSpecifier, specifier: aliasing(specifier as J.Identifier, this.boundName)} as JS.ExportSpecifier;
2021
+ }
2022
+ if (specifier.kind === JS.Kind.Alias) {
2023
+ const alias = specifier as JS.Alias;
2024
+ const propertyName = alias.propertyName.element;
2025
+ return propertyName.kind === J.Kind.Identifier && (propertyName as J.Identifier).simpleName === this.localName
2026
+ ? {
2027
+ ...exportSpecifier,
2028
+ specifier: {
2029
+ ...alias,
2030
+ propertyName: {...alias.propertyName, element: {...propertyName, simpleName: this.boundName}}
2031
+ }
2032
+ } as JS.ExportSpecifier
2033
+ : exportSpecifier;
2034
+ }
2035
+ return super.visitExportSpecifier(exportSpecifier, p);
2036
+ }
2037
+
2038
+ /**
2039
+ * Whether the identifier at the cursor stands for the moved binding: the right name, in a
2040
+ * position that references rather than declares, reaching the module scope that binds it.
2041
+ */
2042
+ private referencesBinding(identifier: J.Identifier): boolean {
2043
+ return identifier.simpleName === this.localName &&
2044
+ // The specifier binding the name is the one edit that is not a reference to it.
2045
+ !this.cursor.firstEnclosing((v): v is JS.Import => (v as J | undefined)?.kind === JS.Kind.Import) &&
2046
+ isValueReference(this.cursor, identifier) &&
2047
+ scopeOf(this.cursor).declaringScope(this.localName)?.kind === JS.Kind.CompilationUnit;
2048
+ }
2049
+
2050
+ /** A shorthand property's name slot is also the reference to the binding. */
2051
+ override async visitPropertyAssignment(propertyAssignment: JS.PropertyAssignment, p: P): Promise<J | undefined> {
2052
+ const name = propertyAssignment.name.element;
2053
+ if (this.renaming && propertyAssignment.initializer === undefined &&
2054
+ name.kind === J.Kind.Identifier && (name as J.Identifier).simpleName === this.localName &&
2055
+ scopeOf(this.cursor).declaringScope(this.localName)?.kind === JS.Kind.CompilationUnit) {
2056
+ // The key names a property rather than the binding, so it carries no attribution,
2057
+ // the same way `aliasing` builds a property name that stands for nothing.
2058
+ return {
2059
+ ...propertyAssignment,
2060
+ name: {...propertyAssignment.name, element: {...name, type: undefined, fieldType: undefined}},
2061
+ assigmentToken: JS.PropertyAssignment.Token.Colon,
2062
+ initializer: {...(name as J.Identifier), prefix: singleSpace, simpleName: this.boundName}
2063
+ } as JS.PropertyAssignment;
2064
+ }
2065
+ return super.visitPropertyAssignment(propertyAssignment, p);
2066
+ }
2067
+ }
2068
+
2069
+ /**
2070
+ * Rewrites the attribution a move invalidates, onto the module and member it moved to. Applied at
2071
+ * a reference to the moved binding. See CLAUDE.md: What a rebind's attribution follows.
2072
+ */
2073
+ class MovedTypes extends TypeVisitor<undefined> {
2074
+ private readonly onPath = new Set<Type>();
2075
+ private readonly answered = new Map<Type, Type | undefined>();
2076
+ private readonly renamed: ReadonlyMap<string, string>;
2077
+ private readonly fromMember?: string;
2078
+ private readonly toMember?: string;
2079
+ private readonly toModule: string;
2080
+
2081
+ constructor(from: {module: string; member?: string}, to: {module: string; member?: string}) {
2082
+ super();
2083
+ // Where no member is named the two keys coincide, and both name the module.
2084
+ this.renamed = new Map([[from.module, to.module], [qualifiedName(from), qualifiedName(to)]]);
2085
+ this.fromMember = declaredMember(from);
2086
+ this.toMember = declaredMember(to);
2087
+ this.toModule = to.module;
2088
+ }
2089
+
2090
+ /**
2091
+ * A type reached while it is still being visited is a cycle — a class holds a method whose
2092
+ * declaring type is that class — and answers with itself, which is what ends the walk. Every
2093
+ * reference to a binding shares one type, so a completed walk is remembered for the next.
2094
+ */
2095
+ override async visit<T extends Type>(type: T | undefined, p: undefined): Promise<T | undefined> {
2096
+ if (type === undefined || this.onPath.has(type)) {
2097
+ return type;
2098
+ }
2099
+ if (this.answered.has(type)) {
2100
+ return this.answered.get(type) as T | undefined;
2101
+ }
2102
+ this.onPath.add(type);
2103
+ try {
2104
+ const answer = await super.visit(type, p);
2105
+ // Only the type a walk entered at is remembered: one reached inside it may have met
2106
+ // a back edge, which answers with the type itself and so stands for the path it was on.
2107
+ if (this.onPath.size === 1) {
2108
+ this.answered.set(type, answer);
2109
+ }
2110
+ return answer;
2111
+ } finally {
2112
+ this.onPath.delete(type);
2113
+ }
2114
+ }
2115
+
2116
+ protected override async visitClass(aClass: Type.Class, p: undefined): Promise<Type | undefined> {
2117
+ const visited = await super.visitClass(aClass, p) as Type.Class;
2118
+ const moved = this.renamed.get(visited.fullyQualifiedName);
2119
+ return moved === undefined || moved === visited.fullyQualifiedName
2120
+ ? visited
2121
+ : {...visited, fullyQualifiedName: moved} as Type.Class;
2122
+ }
2123
+
2124
+ protected override async visitMethod(method: Type.Method, p: undefined): Promise<Type | undefined> {
2125
+ const visited = await super.visitMethod(method, p) as Type.Method;
2126
+ return this.declaresMovedMember(visited.name, visited.declaringType)
2127
+ ? {...visited, name: this.toMember} as Type.Method
2128
+ : visited;
2129
+ }
2130
+
2131
+ protected override async visitVariable(variable: Type.Variable, p: undefined): Promise<Type | undefined> {
2132
+ const visited = await super.visitVariable(variable, p) as Type.Variable;
2133
+ return this.declaresMovedMember(visited.name, visited.owner)
2134
+ ? {...visited, name: this.toMember} as Type.Variable
2135
+ : visited;
2136
+ }
2137
+
2138
+ /**
2139
+ * Whether `name`, declared on `owner`, is the member that moved. `owner` has already followed
2140
+ * the move by the time this runs, so it is the module moved *to* that it has to name. A
2141
+ * default or namespace binding declares no member, so there is no name for one to take.
2142
+ */
2143
+ private declaresMovedMember(name: string, owner: Type | undefined): boolean {
2144
+ return this.toMember !== undefined && name === this.fromMember && this.toMember !== this.fromMember &&
2145
+ owner !== undefined && Type.isFullyQualified(owner) &&
2146
+ Type.FullyQualified.getFullyQualifiedName(owner) === this.toModule;
2147
+ }
2148
+ }
2149
+
2150
+ /** A member's qualified name, or the module's own where no member is named. */
2151
+ function qualifiedName(binding: {module: string; member?: string}): string {
2152
+ const key = memberName(binding.member);
2153
+ return key === undefined || key === '*' ? binding.module : `${binding.module}.${key}`;
2154
+ }
2155
+
2156
+ /** The member the module declares, where the binding names one: a whole module declares none. */
2157
+ function declaredMember(binding: {module: string; member?: string}): string | undefined {
2158
+ const key = memberName(binding.member);
2159
+ return key === undefined || key === '*' ? undefined : key;
2160
+ }
2161
+
2162
+ /** Renames the identifier a default (`key` undefined) or namespace clause binds its module under. */
2163
+ function renameClauseBinding(
2164
+ importClause: Draft<JS.ImportClause> | undefined,
2165
+ key: string | undefined,
2166
+ boundName: string
2167
+ ): void {
2168
+ const bound = key === undefined
2169
+ ? importClause?.name?.element
2170
+ : (importClause?.namedBindings as Draft<JS.Alias> | undefined)?.alias;
2171
+ if (bound?.kind === J.Kind.Identifier) {
2172
+ (bound as Draft<J.Identifier>).simpleName = boundName;
2173
+ }
2174
+ }
2175
+
2176
+ /**
2177
+ * Rewrites the specifier importing `key` to import `member` under `boundName` — a bare `{member}`
2178
+ * where the two agree, since an alias saying the same thing is noise the source never had.
2179
+ */
2180
+ function rewriteNamedSpecifier(
2181
+ importClause: Draft<JS.ImportClause> | undefined,
2182
+ key: string,
2183
+ member: string,
2184
+ boundName: string
2185
+ ): void {
2186
+ if (importClause?.namedBindings?.kind !== JS.Kind.NamedImports) {
2187
+ return;
2188
+ }
2189
+ for (const elem of (importClause.namedBindings as Draft<JS.NamedImports>).elements.elements) {
2190
+ const specifier = elem.element;
2191
+ const node = specifier.specifier;
2192
+ if (!namedSpecifierImports(node as JS.ImportSpecifier["specifier"], key)) {
2193
+ continue;
2194
+ }
2195
+ const alias = node.kind === JS.Kind.Alias ? node as Draft<JS.Alias> : undefined;
2196
+ const local = (alias?.alias ?? node) as Draft<J.Identifier>;
2197
+ if (local.kind !== J.Kind.Identifier) {
2198
+ continue;
2199
+ }
2200
+ if (boundName === member) {
2201
+ // An alias node holds the specifier's leading whitespace, so the bare identifier
2202
+ // standing in for it takes that prefix.
2203
+ specifier.specifier = {...local, prefix: (alias ?? local).prefix, simpleName: member};
2204
+ } else if (alias) {
2205
+ (alias.propertyName.element as Draft<J.Identifier>).simpleName = member;
2206
+ local.simpleName = boundName;
2207
+ } else {
2208
+ specifier.specifier = aliasing({...local, simpleName: boundName}, member) as Draft<JS.Alias>;
2209
+ }
2210
+ }
1840
2211
  }