@openrewrite/rewrite 8.91.0 → 8.91.2

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 (153) hide show
  1. package/dist/java/formatting-utils.d.ts +8 -5
  2. package/dist/java/formatting-utils.d.ts.map +1 -1
  3. package/dist/java/formatting-utils.js +14 -10
  4. package/dist/java/formatting-utils.js.map +1 -1
  5. package/dist/java/type.d.ts +2 -0
  6. package/dist/java/type.d.ts.map +1 -1
  7. package/dist/java/type.js +5 -0
  8. package/dist/java/type.js.map +1 -1
  9. package/dist/javascript/add-import.d.ts +96 -43
  10. package/dist/javascript/add-import.d.ts.map +1 -1
  11. package/dist/javascript/add-import.js +668 -236
  12. package/dist/javascript/add-import.js.map +1 -1
  13. package/dist/javascript/amd.d.ts +138 -0
  14. package/dist/javascript/amd.d.ts.map +1 -0
  15. package/dist/javascript/amd.js +768 -0
  16. package/dist/javascript/amd.js.map +1 -0
  17. package/dist/javascript/assertions.d.ts +7 -0
  18. package/dist/javascript/assertions.d.ts.map +1 -1
  19. package/dist/javascript/assertions.js +31 -5
  20. package/dist/javascript/assertions.js.map +1 -1
  21. package/dist/javascript/binding.d.ts +94 -0
  22. package/dist/javascript/binding.d.ts.map +1 -0
  23. package/dist/javascript/binding.js +335 -0
  24. package/dist/javascript/binding.js.map +1 -0
  25. package/dist/javascript/comparator.d.ts +5 -0
  26. package/dist/javascript/comparator.d.ts.map +1 -1
  27. package/dist/javascript/comparator.js +34 -9
  28. package/dist/javascript/comparator.js.map +1 -1
  29. package/dist/javascript/format/format.d.ts.map +1 -1
  30. package/dist/javascript/format/format.js +8 -9
  31. package/dist/javascript/format/format.js.map +1 -1
  32. package/dist/javascript/format/tabs-and-indents-visitor.d.ts +2 -0
  33. package/dist/javascript/format/tabs-and-indents-visitor.d.ts.map +1 -1
  34. package/dist/javascript/format/tabs-and-indents-visitor.js +17 -11
  35. package/dist/javascript/format/tabs-and-indents-visitor.js.map +1 -1
  36. package/dist/javascript/index.d.ts +8 -1
  37. package/dist/javascript/index.d.ts.map +1 -1
  38. package/dist/javascript/index.js +26 -1
  39. package/dist/javascript/index.js.map +1 -1
  40. package/dist/javascript/node-resolution-result.d.ts +19 -1
  41. package/dist/javascript/node-resolution-result.d.ts.map +1 -1
  42. package/dist/javascript/node-resolution-result.js +37 -2
  43. package/dist/javascript/node-resolution-result.js.map +1 -1
  44. package/dist/javascript/package-exported-types.d.ts.map +1 -1
  45. package/dist/javascript/package-exported-types.js +56 -72
  46. package/dist/javascript/package-exported-types.js.map +1 -1
  47. package/dist/javascript/parser-utils.d.ts.map +1 -1
  48. package/dist/javascript/parser-utils.js +2 -0
  49. package/dist/javascript/parser-utils.js.map +1 -1
  50. package/dist/javascript/parser.d.ts +7 -1
  51. package/dist/javascript/parser.d.ts.map +1 -1
  52. package/dist/javascript/parser.js +17 -4
  53. package/dist/javascript/parser.js.map +1 -1
  54. package/dist/javascript/recipes/change-import.d.ts +2 -4
  55. package/dist/javascript/recipes/change-import.d.ts.map +1 -1
  56. package/dist/javascript/recipes/change-import.js +8 -244
  57. package/dist/javascript/recipes/change-import.js.map +1 -1
  58. package/dist/javascript/remove-import.d.ts +2 -28
  59. package/dist/javascript/remove-import.d.ts.map +1 -1
  60. package/dist/javascript/remove-import.js +19 -57
  61. package/dist/javascript/remove-import.js.map +1 -1
  62. package/dist/javascript/scope.d.ts +49 -0
  63. package/dist/javascript/scope.d.ts.map +1 -0
  64. package/dist/javascript/scope.js +324 -0
  65. package/dist/javascript/scope.js.map +1 -0
  66. package/dist/javascript/style.d.ts.map +1 -1
  67. package/dist/javascript/style.js +3 -10
  68. package/dist/javascript/style.js.map +1 -1
  69. package/dist/javascript/templating/bindings.d.ts +8 -0
  70. package/dist/javascript/templating/bindings.d.ts.map +1 -0
  71. package/dist/javascript/templating/bindings.js +92 -0
  72. package/dist/javascript/templating/bindings.js.map +1 -0
  73. package/dist/javascript/templating/comparator.d.ts +10 -0
  74. package/dist/javascript/templating/comparator.d.ts.map +1 -1
  75. package/dist/javascript/templating/comparator.js +48 -20
  76. package/dist/javascript/templating/comparator.js.map +1 -1
  77. package/dist/javascript/templating/engine.d.ts +34 -10
  78. package/dist/javascript/templating/engine.d.ts.map +1 -1
  79. package/dist/javascript/templating/engine.js +109 -55
  80. package/dist/javascript/templating/engine.js.map +1 -1
  81. package/dist/javascript/templating/index.d.ts +1 -1
  82. package/dist/javascript/templating/index.d.ts.map +1 -1
  83. package/dist/javascript/templating/index.js.map +1 -1
  84. package/dist/javascript/templating/pattern.d.ts.map +1 -1
  85. package/dist/javascript/templating/pattern.js +13 -10
  86. package/dist/javascript/templating/pattern.js.map +1 -1
  87. package/dist/javascript/templating/placeholder-replacement.d.ts +4 -0
  88. package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
  89. package/dist/javascript/templating/placeholder-replacement.js +24 -10
  90. package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
  91. package/dist/javascript/templating/precedence.d.ts +12 -4
  92. package/dist/javascript/templating/precedence.d.ts.map +1 -1
  93. package/dist/javascript/templating/precedence.js +18 -11
  94. package/dist/javascript/templating/precedence.js.map +1 -1
  95. package/dist/javascript/templating/rewrite.d.ts.map +1 -1
  96. package/dist/javascript/templating/rewrite.js +19 -17
  97. package/dist/javascript/templating/rewrite.js.map +1 -1
  98. package/dist/javascript/templating/template.d.ts +14 -0
  99. package/dist/javascript/templating/template.d.ts.map +1 -1
  100. package/dist/javascript/templating/template.js +63 -3
  101. package/dist/javascript/templating/template.js.map +1 -1
  102. package/dist/javascript/templating/types.d.ts +39 -1
  103. package/dist/javascript/templating/types.d.ts.map +1 -1
  104. package/dist/javascript/templating/utils.d.ts +6 -1
  105. package/dist/javascript/templating/utils.d.ts.map +1 -1
  106. package/dist/javascript/templating/utils.js +20 -2
  107. package/dist/javascript/templating/utils.js.map +1 -1
  108. package/dist/javascript/type-mapping.d.ts +1 -0
  109. package/dist/javascript/type-mapping.d.ts.map +1 -1
  110. package/dist/javascript/type-mapping.js +52 -2
  111. package/dist/javascript/type-mapping.js.map +1 -1
  112. package/dist/rewrite-javascript-version.txt +1 -1
  113. package/dist/style.d.ts +2 -0
  114. package/dist/style.d.ts.map +1 -1
  115. package/dist/style.js +5 -0
  116. package/dist/style.js.map +1 -1
  117. package/dist/test/rewrite-test.d.ts +12 -0
  118. package/dist/test/rewrite-test.d.ts.map +1 -1
  119. package/dist/test/rewrite-test.js +40 -3
  120. package/dist/test/rewrite-test.js.map +1 -1
  121. package/package.json +2 -2
  122. package/src/java/formatting-utils.ts +14 -10
  123. package/src/java/type.ts +5 -0
  124. package/src/javascript/add-import.ts +751 -247
  125. package/src/javascript/amd.ts +915 -0
  126. package/src/javascript/assertions.ts +33 -5
  127. package/src/javascript/binding.ts +449 -0
  128. package/src/javascript/comparator.ts +39 -10
  129. package/src/javascript/format/format.ts +9 -10
  130. package/src/javascript/format/tabs-and-indents-visitor.ts +18 -12
  131. package/src/javascript/index.ts +12 -1
  132. package/src/javascript/node-resolution-result.ts +59 -4
  133. package/src/javascript/package-exported-types.ts +60 -74
  134. package/src/javascript/parser-utils.ts +2 -0
  135. package/src/javascript/parser.ts +23 -3
  136. package/src/javascript/recipes/change-import.ts +10 -263
  137. package/src/javascript/remove-import.ts +20 -56
  138. package/src/javascript/scope.ts +338 -0
  139. package/src/javascript/style.ts +3 -11
  140. package/src/javascript/templating/bindings.ts +99 -0
  141. package/src/javascript/templating/comparator.ts +57 -20
  142. package/src/javascript/templating/engine.ts +148 -68
  143. package/src/javascript/templating/index.ts +1 -0
  144. package/src/javascript/templating/pattern.ts +19 -16
  145. package/src/javascript/templating/placeholder-replacement.ts +32 -11
  146. package/src/javascript/templating/precedence.ts +21 -13
  147. package/src/javascript/templating/rewrite.ts +23 -17
  148. package/src/javascript/templating/template.ts +84 -9
  149. package/src/javascript/templating/types.ts +46 -1
  150. package/src/javascript/templating/utils.ts +21 -2
  151. package/src/javascript/type-mapping.ts +54 -2
  152. package/src/style.ts +5 -0
  153. package/src/test/rewrite-test.ts +61 -3
@@ -1,13 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AddImport = exports.ImportStyle = void 0;
4
- exports.maybeAddImport = maybeAddImport;
3
+ exports.RebindImport = exports.AddImport = exports.ImportStyle = void 0;
4
+ exports.bindImport = bindImport;
5
+ exports.memberName = memberName;
6
+ exports.moduleScopeBindings = moduleScopeBindings;
7
+ exports.requiredModuleOf = requiredModuleOf;
8
+ exports.moduleNameOf = moduleNameOf;
9
+ exports.existingImportBinding = existingImportBinding;
5
10
  const visitor_1 = require("./visitor");
6
11
  const java_1 = require("../java");
7
12
  const tree_1 = require("./tree");
8
13
  const uuid_1 = require("../uuid");
9
14
  const markers_1 = require("../markers");
10
15
  const style_1 = require("./style");
16
+ const scope_1 = require("./scope");
17
+ const mutative_1 = require("mutative");
11
18
  var ImportStyle;
12
19
  (function (ImportStyle) {
13
20
  ImportStyle[ImportStyle["ES6Named"] = 0] = "ES6Named";
@@ -16,96 +23,389 @@ var ImportStyle;
16
23
  ImportStyle[ImportStyle["CommonJS"] = 3] = "CommonJS"; // const x = require('module')
17
24
  })(ImportStyle || (exports.ImportStyle = ImportStyle = {}));
18
25
  /**
19
- * Register an AddImport visitor to add an import statement to a JavaScript/TypeScript file
20
- * @param visitor The visitor to add the import addition to
21
- * @param options Configuration options for the import to add
22
- *
23
- * @example
24
- * // Add a named import
25
- * maybeAddImport(visitor, { module: 'fs', member: 'readFile' });
26
- *
27
- * @example
28
- * // Add a default import using the 'default' member specifier
29
- * maybeAddImport(visitor, { module: 'react', member: 'default', alias: 'React' });
30
- *
31
- * @example
32
- * // Add a default import (legacy way, without specifying member)
33
- * maybeAddImport(visitor, { module: 'react', alias: 'React' });
34
- *
35
- * @example
36
- * // Add a namespace import
37
- * maybeAddImport(visitor, { module: 'crypto', member: '*', alias: 'crypto' });
38
- *
39
- * @example
40
- * // Add a side-effect import
41
- * maybeAddImport(visitor, { module: 'core-js/stable', sideEffectOnly: true });
26
+ * Ensures an import for `options.module` exists, queuing an `AddImport` edit where none already
27
+ * serves the request. `maybeBind`'s ESM/CommonJS lane is this function; its own JSDoc carries the
28
+ * return-value contract. `refuseCreate` answers `undefined` instead of queuing a new import,
29
+ * without affecting whether an existing binding answers the request first.
42
30
  */
43
- function maybeAddImport(visitor, options) {
31
+ function bindImport(visitor, options, refuseCreate) {
44
32
  var _a, _b;
33
+ validate(options);
34
+ const module = moduleNameOf(options.module);
35
+ const sideEffectOnly = (_a = options.sideEffectOnly) !== null && _a !== void 0 ? _a : false;
36
+ const typeOnly = (_b = options.typeOnly) !== null && _b !== void 0 ? _b : false;
37
+ // A queued import binds the module as much as one already in the file, so it answers a later
38
+ // request the same way; a name a merged request never emits would be referenced but not bound.
45
39
  for (const v of visitor.afterVisit || []) {
46
- if (v instanceof AddImport &&
47
- v.module === options.module &&
48
- v.member === options.member &&
49
- v.alias === options.alias &&
50
- v.sideEffectOnly === ((_a = options.sideEffectOnly) !== null && _a !== void 0 ? _a : false) &&
51
- v.typeOnly === ((_b = options.typeOnly) !== null && _b !== void 0 ? _b : false)) {
52
- return;
40
+ if (!(v instanceof AddImport) || v.module !== module ||
41
+ v.sideEffectOnly !== sideEffectOnly || v.typeOnly !== typeOnly) {
42
+ continue;
43
+ }
44
+ // How a specifier prints, or what name would be nice, does not say which binding is wanted.
45
+ // A pinned alias does: it asks for a binding of its own, which only the same request answers.
46
+ const answers = options.alias
47
+ ? v.alias === options.alias && v.member === options.member
48
+ : memberName(v.member) === memberName(options.member);
49
+ if (answers) {
50
+ return v.bindingName;
51
+ }
52
+ }
53
+ if (sideEffectOnly) {
54
+ if (refuseCreate) {
55
+ return undefined;
56
+ }
57
+ visitor.afterVisit.push(new AddImport(options));
58
+ return undefined;
59
+ }
60
+ // A pinned alias is the whole answer, so the file has no say and nothing below needs to read it.
61
+ if (options.alias) {
62
+ if (refuseCreate) {
63
+ return undefined;
64
+ }
65
+ visitor.afterVisit.push(new AddImport(options, options.alias));
66
+ return options.alias;
67
+ }
68
+ const derived = derivedName(options);
69
+ const cursor = (0, scope_1.cursorOf)(visitor);
70
+ const cu = cursor && (0, scope_1.compilationUnitOf)(cursor);
71
+ if (!cu) {
72
+ if (refuseCreate) {
73
+ return undefined;
74
+ }
75
+ visitor.afterVisit.push(new AddImport(options, derived));
76
+ return derived;
77
+ }
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.
82
+ for (const binding of moduleScopeBindings(cu)) {
83
+ if (binding.module === module && binding.member === memberName(options.member) &&
84
+ binding.typeOnly === typeOnly &&
85
+ (options.preferredName !== undefined || anyNameAnswers(options) ||
86
+ binding.name === derived)) {
87
+ return binding.name;
53
88
  }
54
89
  }
55
- visitor.afterVisit.push(new AddImport(options));
90
+ if (refuseCreate) {
91
+ return undefined;
92
+ }
93
+ // Only the module scope answers for a name, but any scope in the file occupies one. The queue
94
+ // gives every later request for this module the name chosen here, so it has to clear the scopes
95
+ // those references will sit in, which are not known yet.
96
+ const taken = takenNames((0, scope_1.namesDeclaredIn)(cu), visitor);
97
+ const name = (0, scope_1.deconflict)(derived, candidate => taken.has(candidate));
98
+ visitor.afterVisit.push(new AddImport(options, name));
99
+ return name;
56
100
  }
57
- class AddImport extends visitor_1.JavaScriptVisitor {
58
- constructor(options) {
101
+ /** Rejects a request on its own terms, whatever the queue already holds. */
102
+ function validate(options) {
103
+ // Validate that a name is provided when member is 'default'
104
+ if (options.member === 'default' && !options.alias && !options.preferredName) {
105
+ throw new Error("When member is 'default', the alias parameter is required");
106
+ }
107
+ // Validate that a name is provided when member is '*' (namespace import)
108
+ if (options.member === '*' && !options.alias && !options.preferredName) {
109
+ throw new Error("When member is '*', the alias parameter is required");
110
+ }
111
+ // Validate that sideEffectOnly is not combined with incompatible options
112
+ if (options.sideEffectOnly) {
113
+ if (options.member !== undefined) {
114
+ throw new Error("Cannot combine sideEffectOnly with member");
115
+ }
116
+ if (options.alias !== undefined) {
117
+ throw new Error("Cannot combine sideEffectOnly with alias");
118
+ }
119
+ if (options.preferredName !== undefined) {
120
+ throw new Error("Cannot combine sideEffectOnly with preferredName");
121
+ }
122
+ if (options.onlyIfReferenced !== undefined) {
123
+ throw new Error("Cannot combine sideEffectOnly with onlyIfReferenced");
124
+ }
125
+ if (options.typeOnly) {
126
+ throw new Error("Cannot combine sideEffectOnly with typeOnly");
127
+ }
128
+ }
129
+ }
130
+ /**
131
+ * A request that named no local name and asks for no member of the module. Having expressed no
132
+ * preference it has none to disappoint, so whatever the file already calls that module answers it.
133
+ */
134
+ function anyNameAnswers(options) {
135
+ return !options.sideEffectOnly && memberName(options.member) === undefined &&
136
+ options.alias === undefined && options.preferredName === undefined;
137
+ }
138
+ /** The local name a request asks for, before the file has a say in it. */
139
+ function derivedName(options) {
140
+ var _a, _b, _c;
141
+ return (_c = (_b = (_a = options.alias) !== null && _a !== void 0 ? _a : options.preferredName) !== null && _b !== void 0 ? _b : memberName(options.member)) !== null && _c !== void 0 ? _c : moduleNameOf(options.module);
142
+ }
143
+ /** `'default'` and an absent member both name a default import, which binds no member name of its own. */
144
+ function memberName(member) {
145
+ return member === 'default' ? undefined : member;
146
+ }
147
+ /** What the file's imports and `require`s bind at module scope, and the module each name comes from. */
148
+ function moduleScopeBindings(cu) {
149
+ const bindings = [];
150
+ const declaredByVariables = (varDecl) => {
59
151
  var _a, _b, _c;
60
- super();
61
- // Validate that alias is provided when member is 'default'
62
- if (options.member === 'default' && !options.alias) {
63
- throw new Error("When member is 'default', the alias parameter is required");
64
- }
65
- // Validate that alias is provided when member is '*' (namespace import)
66
- if (options.member === '*' && !options.alias) {
67
- throw new Error("When member is '*', the alias parameter is required");
68
- }
69
- // Validate that sideEffectOnly is not combined with incompatible options
70
- if (options.sideEffectOnly) {
71
- if (options.member !== undefined) {
72
- throw new Error("Cannot combine sideEffectOnly with member");
152
+ for (const variable of varDecl.variables) {
153
+ const required = requiredModule((_b = (_a = variable.element) === null || _a === void 0 ? void 0 : _a.initializer) === null || _b === void 0 ? void 0 : _b.element);
154
+ if (required !== undefined) {
155
+ bindings.push(...requireBindings((_c = variable.element) === null || _c === void 0 ? void 0 : _c.name, required));
73
156
  }
74
- if (options.alias !== undefined) {
75
- throw new Error("Cannot combine sideEffectOnly with alias");
157
+ }
158
+ };
159
+ for (const stmt of cu.statements) {
160
+ const statement = stmt.element;
161
+ if ((statement === null || statement === void 0 ? void 0 : statement.kind) === tree_1.JS.Kind.Import) {
162
+ bindings.push(...importBindings(statement));
163
+ continue;
164
+ }
165
+ for (const declaration of (0, scope_1.declarationsOf)(statement)) {
166
+ declaredByVariables(declaration);
167
+ }
168
+ }
169
+ return bindings;
170
+ }
171
+ /** The module a `require('...')` initializer names, `undefined` for an initializer that is anything else. */
172
+ function requiredModule(initializer) {
173
+ return (initializer === null || initializer === void 0 ? void 0 : initializer.kind) === java_1.J.Kind.MethodInvocation
174
+ ? requiredModuleOf(initializer)
175
+ : undefined;
176
+ }
177
+ /** A `require(...)` call, whatever it is passed. `obj.require('x')` selects a method rather than loading a module. */
178
+ function isRequireCall(methodInv) {
179
+ var _a;
180
+ return !methodInv.select && ((_a = methodInv.name) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier &&
181
+ methodInv.name.simpleName === 'require';
182
+ }
183
+ /**
184
+ * The module a `require(...)` call loads. `obj.require('x')` selects a method rather than loading a
185
+ * module, and a specifier that is not a string literal names none that can be read here.
186
+ */
187
+ function requiredModuleOf(methodInv) {
188
+ var _a, _b;
189
+ if (!isRequireCall(methodInv)) {
190
+ return undefined;
191
+ }
192
+ const argument = (_b = (_a = methodInv.arguments) === null || _a === void 0 ? void 0 : _a.elements[0]) === null || _b === void 0 ? void 0 : _b.element;
193
+ return (argument === null || argument === void 0 ? void 0 : argument.kind) === java_1.J.Kind.Literal && typeof argument.value === 'string'
194
+ ? moduleNameOf(argument)
195
+ : undefined;
196
+ }
197
+ /** A `require` binds a module the way an import does, so the pool records it the same way. */
198
+ function requireBindings(pattern, module) {
199
+ // A whole-module require binds no member, exactly as a default import does.
200
+ if ((pattern === null || pattern === void 0 ? void 0 : pattern.kind) === java_1.J.Kind.Identifier) {
201
+ return [{ name: pattern.simpleName, module, member: undefined, typeOnly: false }];
202
+ }
203
+ return (0, scope_1.bindingNames)(pattern).map(bound => bound.member === undefined
204
+ ? { name: bound.name }
205
+ : { name: bound.name, module, member: bound.member, typeOnly: false });
206
+ }
207
+ /** The member a specifier imports and the name it binds it under, which are the same absent an alias. */
208
+ function specifierBinding(specifier) {
209
+ var _a, _b, _c, _d, _e;
210
+ if (((_a = specifier.specifier) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier) {
211
+ const name = specifier.specifier.simpleName;
212
+ return { name, member: name };
213
+ }
214
+ if (((_b = specifier.specifier) === null || _b === void 0 ? void 0 : _b.kind) !== tree_1.JS.Kind.Alias) {
215
+ return undefined;
216
+ }
217
+ const alias = specifier.specifier;
218
+ return ((_d = (_c = alias.propertyName) === null || _c === void 0 ? void 0 : _c.element) === null || _d === void 0 ? void 0 : _d.kind) === java_1.J.Kind.Identifier && ((_e = alias.alias) === null || _e === void 0 ? void 0 : _e.kind) === java_1.J.Kind.Identifier
219
+ ? { name: alias.alias.simpleName, member: alias.propertyName.element.simpleName }
220
+ : undefined;
221
+ }
222
+ function importBindings(jsImport) {
223
+ var _a, _b, _c, _d;
224
+ const moduleSpecifier = (_a = jsImport.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element;
225
+ const module = (moduleSpecifier === null || moduleSpecifier === void 0 ? void 0 : moduleSpecifier.kind) === java_1.J.Kind.Literal
226
+ ? moduleNameOf(moduleSpecifier)
227
+ : undefined;
228
+ const importClause = jsImport.importClause;
229
+ if (!importClause) {
230
+ return [];
231
+ }
232
+ const typeOnly = importClause.typeOnly;
233
+ const bindings = [];
234
+ if (((_c = (_b = importClause.name) === null || _b === void 0 ? void 0 : _b.element) === null || _c === void 0 ? void 0 : _c.kind) === java_1.J.Kind.Identifier) {
235
+ bindings.push({ name: importClause.name.element.simpleName, module, member: undefined, typeOnly });
236
+ }
237
+ const namedBindings = importClause.namedBindings;
238
+ if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === java_1.J.Kind.Identifier) {
239
+ bindings.push({ name: namedBindings.simpleName, module, member: '*', typeOnly });
240
+ }
241
+ else if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_1.JS.Kind.Alias) {
242
+ const alias = namedBindings.alias;
243
+ if ((alias === null || alias === void 0 ? void 0 : alias.kind) === java_1.J.Kind.Identifier) {
244
+ bindings.push({ name: alias.simpleName, module, member: '*', typeOnly });
245
+ }
246
+ }
247
+ else if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_1.JS.Kind.NamedImports) {
248
+ for (const elem of namedBindings.elements.elements) {
249
+ const bound = ((_d = elem.element) === null || _d === void 0 ? void 0 : _d.kind) === tree_1.JS.Kind.ImportSpecifier
250
+ ? specifierBinding(elem.element)
251
+ : undefined;
252
+ if (bound) {
253
+ bindings.push(Object.assign(Object.assign({}, bound), { module, typeOnly }));
76
254
  }
77
- if (options.onlyIfReferenced !== undefined) {
78
- throw new Error("Cannot combine sideEffectOnly with onlyIfReferenced");
255
+ }
256
+ }
257
+ return bindings;
258
+ }
259
+ /**
260
+ * Names in scope, plus those pending `AddImport`s on the `afterVisit` queue have claimed. A queued
261
+ * `RemoveImport` does not free one: it removes only what the file leaves unused, and binding a name
262
+ * it keeps is an error, where an unnecessary suffix merely reads oddly.
263
+ */
264
+ function takenNames(inScope, visitor) {
265
+ const taken = new Set(inScope);
266
+ for (const v of visitor.afterVisit || []) {
267
+ if (v instanceof AddImport && v.bindingName) {
268
+ taken.add(v.bindingName);
269
+ }
270
+ }
271
+ return taken;
272
+ }
273
+ /**
274
+ * The parser lifts surrogate pairs out of `valueSource` into `unicodeEscapes` and, when it does,
275
+ * sets `value` to the quoted source (`parser.ts` `mapLiteral`), so neither field alone is the
276
+ * module name. Reuniting them and stripping the quotes yields it for either shape of literal.
277
+ */
278
+ function moduleNameOf(module) {
279
+ var _a;
280
+ if (typeof module === 'string') {
281
+ return module;
282
+ }
283
+ const source = module.valueSource;
284
+ if (source === undefined) {
285
+ return String(module.value);
286
+ }
287
+ let restored = '';
288
+ let cut = 0;
289
+ for (const escape of (_a = module.unicodeEscapes) !== null && _a !== void 0 ? _a : []) {
290
+ restored += source.slice(cut, escape.valueSourceIndex) + String.fromCharCode(parseInt(escape.codePoint, 16));
291
+ cut = escape.valueSourceIndex;
292
+ }
293
+ restored += source.slice(cut);
294
+ const quote = restored.charAt(0);
295
+ return (quote === "'" || quote === '"') && restored.endsWith(quote) && restored.length > 1
296
+ ? restored.slice(1, -1)
297
+ : restored;
298
+ }
299
+ function quoteOf(expression) {
300
+ if ((expression === null || expression === void 0 ? void 0 : expression.kind) !== java_1.J.Kind.Literal) {
301
+ return undefined;
302
+ }
303
+ const source = expression.valueSource;
304
+ const quote = source === null || source === void 0 ? void 0 : source.charAt(0);
305
+ // JsxText carries raw text as its `valueSource`, so a leading quote alone does not make a
306
+ // literal quote-delimited; requiring a matching pair keeps prose out of the tally.
307
+ return (quote === "'" || quote === '"') && source.length > 1 && source.endsWith(quote)
308
+ ? quote
309
+ : undefined;
310
+ }
311
+ /**
312
+ * Pick the quote character for a module specifier being added to `cu`, so that it agrees
313
+ * with the file it lands in. An existing specifier is the most direct precedent; a Prettier
314
+ * config states intent even where the file itself has not been formatted to match.
315
+ */
316
+ async function detectQuote(cu) {
317
+ var _a, _b;
318
+ // Static imports are top-level, so the highest-ranked signal needs no traversal to find.
319
+ for (const statement of cu.statements) {
320
+ const element = statement.element;
321
+ const topLevelQuote = (element === null || element === void 0 ? void 0 : element.kind) === tree_1.JS.Kind.Import
322
+ ? quoteOf((_a = element.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element)
323
+ : (element === null || element === void 0 ? void 0 : element.kind) === tree_1.JS.Kind.ExportDeclaration
324
+ ? quoteOf((_b = element.moduleSpecifier) === null || _b === void 0 ? void 0 : _b.element)
325
+ : undefined;
326
+ if (topLevelQuote) {
327
+ return topLevelQuote;
328
+ }
329
+ }
330
+ let importQuote;
331
+ let requireQuote;
332
+ let single = 0;
333
+ let double = 0;
334
+ await new class extends visitor_1.JavaScriptVisitor {
335
+ async visitImportDeclaration(jsImport, p) {
336
+ var _a;
337
+ importQuote !== null && importQuote !== void 0 ? importQuote : (importQuote = quoteOf((_a = jsImport.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element));
338
+ return super.visitImportDeclaration(jsImport, p);
339
+ }
340
+ async visitExportDeclaration(exportDeclaration, p) {
341
+ var _a;
342
+ importQuote !== null && importQuote !== void 0 ? importQuote : (importQuote = quoteOf((_a = exportDeclaration.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element));
343
+ return super.visitExportDeclaration(exportDeclaration, p);
344
+ }
345
+ async visitMethodInvocation(method, p) {
346
+ var _a, _b, _c;
347
+ if (!method.select && ((_a = method.name) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier &&
348
+ method.name.simpleName === 'require') {
349
+ requireQuote !== null && requireQuote !== void 0 ? requireQuote : (requireQuote = quoteOf((_c = (_b = method.arguments) === null || _b === void 0 ? void 0 : _b.elements[0]) === null || _c === void 0 ? void 0 : _c.element));
79
350
  }
80
- if (options.typeOnly) {
81
- throw new Error("Cannot combine sideEffectOnly with typeOnly");
351
+ return super.visitMethodInvocation(method, p);
352
+ }
353
+ async visitJsxAttribute(attribute, p) {
354
+ // JSX attribute quoting answers to Prettier's `jsxSingleQuote`, so it is no evidence
355
+ // about the quote style of ordinary strings.
356
+ return attribute;
357
+ }
358
+ async visitLiteral(literal, p) {
359
+ const quote = quoteOf(literal);
360
+ if (quote === "'") {
361
+ single++;
82
362
  }
363
+ else if (quote === '"') {
364
+ double++;
365
+ }
366
+ return super.visitLiteral(literal, p);
367
+ }
368
+ }().visit(cu, undefined);
369
+ // Imports outrank requires; that ordering is what makes the import-only scan above sound.
370
+ const specifierQuote = importQuote !== null && importQuote !== void 0 ? importQuote : requireQuote;
371
+ if (specifierQuote) {
372
+ return specifierQuote;
373
+ }
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) {
376
+ const singleQuote = prettier.config.singleQuote;
377
+ if (typeof singleQuote === 'boolean') {
378
+ return singleQuote ? "'" : '"';
83
379
  }
84
- this.module = options.module;
380
+ }
381
+ return double > single ? '"' : "'";
382
+ }
383
+ class AddImport extends visitor_1.JavaScriptVisitor {
384
+ constructor(options, bindingName) {
385
+ var _a, _b, _c;
386
+ super();
387
+ validate(options);
388
+ this.module = moduleNameOf(options.module);
389
+ this.moduleValueSource = typeof options.module === 'string' ? undefined : options.module.valueSource;
390
+ this.moduleUnicodeEscapes = typeof options.module === 'string' ? undefined : options.module.unicodeEscapes;
85
391
  this.member = options.member;
86
392
  this.alias = options.alias;
87
393
  this.onlyIfReferenced = (_a = options.onlyIfReferenced) !== null && _a !== void 0 ? _a : true;
88
394
  this.sideEffectOnly = (_b = options.sideEffectOnly) !== null && _b !== void 0 ? _b : false;
89
395
  this.typeOnly = (_c = options.typeOnly) !== null && _c !== void 0 ? _c : false;
90
396
  this.style = options.style;
397
+ this.quoteStyle = options.quoteStyle;
398
+ this.bindingName = this.sideEffectOnly ? undefined : bindingName !== null && bindingName !== void 0 ? bindingName : derivedName(options);
399
+ this.anyNameAnswers = anyNameAnswers(options);
91
400
  }
92
401
  /**
93
402
  * Extract module name from a module specifier literal
94
403
  */
95
404
  getModuleName(moduleSpecifier) {
96
- var _a;
97
405
  if (moduleSpecifier.kind !== java_1.J.Kind.Literal) {
98
406
  return undefined;
99
407
  }
100
- return (_a = moduleSpecifier.value) === null || _a === void 0 ? void 0 : _a.toString();
101
- }
102
- /**
103
- * Check if a method invocation is a require() call
104
- */
105
- isRequireCall(methodInv) {
106
- var _a;
107
- return ((_a = methodInv.name) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier &&
108
- methodInv.name.simpleName === 'require';
408
+ return moduleNameOf(moduleSpecifier);
109
409
  }
110
410
  /**
111
411
  * Determine the appropriate import style based on file type and existing imports
@@ -177,7 +477,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
177
477
  const namedVar = varDecl.variables[0].element;
178
478
  const initializer = (_a = namedVar === null || namedVar === void 0 ? void 0 : namedVar.initializer) === null || _a === void 0 ? void 0 : _a.element;
179
479
  if ((initializer === null || initializer === void 0 ? void 0 : initializer.kind) === java_1.J.Kind.MethodInvocation &&
180
- this.isRequireCall(initializer)) {
480
+ isRequireCall(initializer)) {
181
481
  hasCommonJSRequires = true;
182
482
  }
183
483
  }
@@ -246,12 +546,8 @@ class AddImport extends visitor_1.JavaScriptVisitor {
246
546
  if (varDecl.variables.length === 1) {
247
547
  const namedVar = varDecl.variables[0].element;
248
548
  const initializer = (_b = namedVar === null || namedVar === void 0 ? void 0 : namedVar.initializer) === null || _b === void 0 ? void 0 : _b.element;
249
- if ((initializer === null || initializer === void 0 ? void 0 : initializer.kind) === java_1.J.Kind.MethodInvocation &&
250
- this.isRequireCall(initializer)) {
251
- const moduleName = this.getModuleNameFromRequire(initializer);
252
- if (moduleName === this.module) {
253
- return ImportStyle.CommonJS;
254
- }
549
+ if (requiredModule(initializer) === this.module) {
550
+ return ImportStyle.CommonJS;
255
551
  }
256
552
  }
257
553
  }
@@ -382,11 +678,11 @@ class AddImport extends visitor_1.JavaScriptVisitor {
382
678
  const namedImports = importClause.namedBindings;
383
679
  const existingElements = namedImports.elements.elements;
384
680
  // Find the correct insertion position (alphabetical, case-insensitive)
385
- const newName = (this.alias || this.member).toLowerCase();
681
+ const newName = this.bindingName.toLowerCase();
386
682
  let insertIndex = existingElements.findIndex(elem => {
387
- var _a;
683
+ var _a, _b, _c;
388
684
  if (((_a = elem.element) === null || _a === void 0 ? void 0 : _a.kind) === tree_1.JS.Kind.ImportSpecifier) {
389
- const name = this.getImportAlias(elem.element) || this.getImportName(elem.element);
685
+ const name = (_c = (_b = specifierBinding(elem.element)) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : '';
390
686
  return newName.localeCompare(name.toLowerCase()) < 0;
391
687
  }
392
688
  return false;
@@ -511,135 +807,40 @@ class AddImport extends visitor_1.JavaScriptVisitor {
511
807
  * Check if the import matches what we're trying to add
512
808
  */
513
809
  isMatchingImport(jsImport) {
514
- var _a, _b, _c, _d;
515
- // Check module specifier
810
+ var _a;
516
811
  const moduleSpecifier = (_a = jsImport.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element;
517
- if (!moduleSpecifier) {
518
- return false;
519
- }
520
- const moduleName = this.getModuleName(moduleSpecifier);
521
- if (moduleName !== this.module) {
812
+ if (!moduleSpecifier || this.getModuleName(moduleSpecifier) !== this.module) {
522
813
  return false;
523
814
  }
815
+ // A side-effect import is the clause-less one, and no import carrying bindings stands in
816
+ // for it — nor it for them.
524
817
  const importClause = jsImport.importClause;
525
- // Handle side-effect imports (no import clause)
526
- if (!importClause) {
527
- // If we're trying to add a side-effect import and one already exists, it's a match
528
- return this.sideEffectOnly;
818
+ if (!importClause || this.sideEffectOnly) {
819
+ return !importClause && this.sideEffectOnly;
529
820
  }
530
- // If we're adding a side-effect import but there's an existing import with bindings,
531
- // it's not a match (side-effect import should be separate)
532
- if (this.sideEffectOnly) {
533
- return false;
534
- }
535
- // Check if the typeOnly flag matches - type-only and value imports are separate
536
- if (importClause.typeOnly !== this.typeOnly) {
537
- return false;
538
- }
539
- // Check if the specific member or default import already exists
540
- if (this.member === '*') {
541
- // We're adding a namespace import, check if one exists
542
- const namedBindings = importClause.namedBindings;
543
- if (!namedBindings) {
544
- return false;
545
- }
546
- // Namespace imports can be represented as J.Identifier or JS.Alias
547
- if (namedBindings.kind === java_1.J.Kind.Identifier) {
548
- const identifier = namedBindings;
549
- return identifier.simpleName === this.alias;
550
- }
551
- else if (namedBindings.kind === tree_1.JS.Kind.Alias) {
552
- const alias = namedBindings;
553
- if (((_b = alias.alias) === null || _b === void 0 ? void 0 : _b.kind) === java_1.J.Kind.Identifier) {
554
- return alias.alias.simpleName === this.alias;
555
- }
556
- }
557
- return false;
558
- }
559
- else if (this.member === undefined || this.member === 'default') {
560
- // We're adding a default import, check if one exists
561
- // For member === 'default', also verify the alias matches if specified
562
- if (importClause.name === undefined) {
563
- return false;
564
- }
565
- // If we have an alias, check that it matches
566
- if (this.alias && ((_c = importClause.name.element) === null || _c === void 0 ? void 0 : _c.kind) === java_1.J.Kind.Identifier) {
567
- const existingName = importClause.name.element.simpleName;
568
- return existingName === this.alias;
569
- }
570
- return true;
571
- }
572
- else {
573
- // We're adding a named import, check if it exists
574
- const namedBindings = importClause.namedBindings;
575
- if (!namedBindings) {
576
- return false;
577
- }
578
- if (namedBindings.kind === tree_1.JS.Kind.NamedImports) {
579
- const namedImports = namedBindings;
580
- for (const elem of namedImports.elements.elements) {
581
- if (((_d = elem.element) === null || _d === void 0 ? void 0 : _d.kind) === tree_1.JS.Kind.ImportSpecifier) {
582
- const specifier = elem.element;
583
- const importName = this.getImportName(specifier);
584
- const aliasName = this.getImportAlias(specifier);
585
- if (importName === this.member && aliasName === this.alias) {
586
- return true;
587
- }
588
- }
589
- }
590
- }
591
- }
592
- return false;
821
+ return importBindings(jsImport).some(binding => this.answeredBy(binding));
822
+ }
823
+ /** Whether a binding the file already has serves this request. */
824
+ answeredBy(binding) {
825
+ return binding.module === this.module &&
826
+ binding.member === memberName(this.member) &&
827
+ binding.typeOnly === this.typeOnly &&
828
+ (this.anyNameAnswers || binding.name === this.bindingName);
593
829
  }
594
830
  /**
595
831
  * Check if this is a matching CommonJS require statement
596
832
  */
597
833
  isMatchingRequire(varDecl) {
598
- var _a, _b, _c;
834
+ var _a;
599
835
  if (varDecl.variables.length !== 1) {
600
836
  return false;
601
837
  }
602
838
  const namedVar = varDecl.variables[0].element;
603
- if (!namedVar) {
839
+ const module = requiredModule((_a = namedVar === null || namedVar === void 0 ? void 0 : namedVar.initializer) === null || _a === void 0 ? void 0 : _a.element);
840
+ if (module !== this.module) {
604
841
  return false;
605
842
  }
606
- const initializer = (_a = namedVar.initializer) === null || _a === void 0 ? void 0 : _a.element;
607
- if (!initializer || initializer.kind !== java_1.J.Kind.MethodInvocation) {
608
- return false;
609
- }
610
- const methodInv = initializer;
611
- if (!this.isRequireCall(methodInv)) {
612
- return false;
613
- }
614
- const moduleName = this.getModuleNameFromRequire(methodInv);
615
- if (moduleName !== this.module) {
616
- return false;
617
- }
618
- // Check if the variable name matches what we're trying to add
619
- const pattern = namedVar.name;
620
- if ((this.member === undefined || this.member === 'default') && (pattern === null || pattern === void 0 ? void 0 : pattern.kind) === java_1.J.Kind.Identifier) {
621
- // Default import style: const fs = require('fs')
622
- // For member === 'default', also check the alias matches if specified
623
- if (this.alias) {
624
- const varName = pattern.simpleName;
625
- return varName === this.alias;
626
- }
627
- return true;
628
- }
629
- else if (this.member !== undefined && this.member !== 'default' && (pattern === null || pattern === void 0 ? void 0 : pattern.kind) === tree_1.JS.Kind.ObjectBindingPattern) {
630
- // Destructured import: const { member } = require('module')
631
- const objectPattern = pattern;
632
- for (const elem of objectPattern.bindings.elements) {
633
- if (((_b = elem.element) === null || _b === void 0 ? void 0 : _b.kind) === tree_1.JS.Kind.BindingElement) {
634
- const bindingElem = elem.element;
635
- const name = (_c = bindingElem.name) === null || _c === void 0 ? void 0 : _c.simpleName;
636
- if (name === (this.alias || this.member)) {
637
- return true;
638
- }
639
- }
640
- }
641
- }
642
- return false;
843
+ return requireBindings(namedVar.name, module).some(binding => this.answeredBy(binding));
643
844
  }
644
845
  /**
645
846
  * Extract the module name from a class type by traversing the owningClass chain
@@ -754,7 +955,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
754
955
  }
755
956
  }
756
957
  // Step 2: Look for references that match
757
- const targetName = this.alias || this.member;
958
+ const targetName = this.bindingName;
758
959
  const targetModule = this.module;
759
960
  let found = false;
760
961
  const self = this;
@@ -872,21 +1073,27 @@ class AddImport extends visitor_1.JavaScriptVisitor {
872
1073
  * Create a new import statement
873
1074
  */
874
1075
  async createImportStatement(compilationUnit, insertionIndex, p) {
1076
+ var _a;
875
1077
  // Determine the appropriate prefix (spacing before the import)
876
1078
  const prefix = this.determineImportPrefix(compilationUnit, insertionIndex);
877
1079
  // Create the module specifier
878
1080
  // For side-effect imports, use emptySpace since space comes from LeftPadded.before
879
1081
  // For regular imports with import clause, use emptySpace since space comes from LeftPadded.before
880
1082
  // However, the printer expects the space after 'from' in the literal's prefix
881
- // Note: value contains the unquoted string, valueSource contains the quoted version for printing
1083
+ // Note: value is the unquoted module name; valueSource and unicodeEscapes are its printed form
1084
+ let valueSource = this.moduleValueSource;
1085
+ if (valueSource === undefined) {
1086
+ const quote = (_a = this.quoteStyle) !== null && _a !== void 0 ? _a : await detectQuote(compilationUnit);
1087
+ valueSource = `${quote}${this.module}${quote}`;
1088
+ }
882
1089
  const moduleSpecifier = {
883
1090
  id: (0, uuid_1.randomId)(),
884
1091
  kind: java_1.J.Kind.Literal,
885
1092
  prefix: this.sideEffectOnly ? java_1.emptySpace : java_1.singleSpace,
886
1093
  markers: markers_1.emptyMarkers,
887
1094
  value: this.module,
888
- valueSource: `'${this.module}'`,
889
- unicodeEscapes: [],
1095
+ valueSource,
1096
+ unicodeEscapes: this.moduleUnicodeEscapes,
890
1097
  type: undefined
891
1098
  };
892
1099
  let importClause;
@@ -912,7 +1119,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
912
1119
  prefix: java_1.singleSpace,
913
1120
  markers: markers_1.emptyMarkers,
914
1121
  annotations: [],
915
- simpleName: this.alias,
1122
+ simpleName: this.bindingName,
916
1123
  type: undefined,
917
1124
  fieldType: undefined
918
1125
  };
@@ -943,7 +1150,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
943
1150
  prefix: java_1.singleSpace,
944
1151
  markers: markers_1.emptyMarkers,
945
1152
  annotations: [],
946
- simpleName: this.alias || this.module,
1153
+ simpleName: this.bindingName,
947
1154
  type: undefined,
948
1155
  fieldType: undefined
949
1156
  };
@@ -1010,7 +1217,8 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1010
1217
  */
1011
1218
  createImportSpecifier() {
1012
1219
  let specifier;
1013
- if (this.alias) {
1220
+ // An alias equal to the member says nothing `{member}` alone does not.
1221
+ if (this.bindingName !== this.member) {
1014
1222
  // Aliased import: import { member as alias } from 'module'
1015
1223
  const propertyName = {
1016
1224
  id: (0, uuid_1.randomId)(),
@@ -1028,7 +1236,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1028
1236
  prefix: java_1.singleSpace,
1029
1237
  markers: markers_1.emptyMarkers,
1030
1238
  annotations: [],
1031
- simpleName: this.alias,
1239
+ simpleName: this.bindingName,
1032
1240
  type: undefined,
1033
1241
  fieldType: undefined
1034
1242
  };
@@ -1113,52 +1321,276 @@ class AddImport extends visitor_1.JavaScriptVisitor {
1113
1321
  // No imports found, insert at the beginning
1114
1322
  return 0;
1115
1323
  }
1116
- /**
1117
- * Get the module name from a require() call
1118
- */
1119
- getModuleNameFromRequire(methodInv) {
1120
- var _a, _b;
1121
- const args = (_a = methodInv.arguments) === null || _a === void 0 ? void 0 : _a.elements;
1122
- if (!args || args.length === 0) {
1123
- return undefined;
1324
+ }
1325
+ exports.AddImport = AddImport;
1326
+ /**
1327
+ * The local name `jsImport` binds `member` of `module` to, or `undefined` where it binds
1328
+ * something else. `'default'` and an absent `member` both mean the default import, matching
1329
+ * {@link memberName}.
1330
+ */
1331
+ function importBinds(jsImport, module, member) {
1332
+ var _a, _b;
1333
+ const specifier = (_a = jsImport.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element;
1334
+ if ((specifier === null || specifier === void 0 ? void 0 : specifier.kind) !== java_1.J.Kind.Literal || specifier.value !== module) {
1335
+ return undefined;
1336
+ }
1337
+ const importClause = jsImport.importClause;
1338
+ if (!importClause) {
1339
+ return undefined;
1340
+ }
1341
+ const key = memberName(member);
1342
+ if (key === undefined) {
1343
+ const nameElem = (_b = importClause.name) === null || _b === void 0 ? void 0 : _b.element;
1344
+ return nameElem && (0, java_1.isIdentifier)(nameElem) ? nameElem.simpleName : undefined;
1345
+ }
1346
+ if (key === '*') {
1347
+ const namedBindings = importClause.namedBindings;
1348
+ return (namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_1.JS.Kind.Alias && (0, java_1.isIdentifier)(namedBindings.alias)
1349
+ ? namedBindings.alias.simpleName
1350
+ : undefined;
1351
+ }
1352
+ const namedBindings = importClause.namedBindings;
1353
+ if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) !== tree_1.JS.Kind.NamedImports) {
1354
+ return undefined;
1355
+ }
1356
+ for (const elem of namedBindings.elements.elements) {
1357
+ const specifierNode = elem.element.specifier;
1358
+ if (!namedSpecifierImports(specifierNode, key)) {
1359
+ continue;
1124
1360
  }
1125
- const firstArg = args[0].element;
1126
- if (!firstArg || firstArg.kind !== java_1.J.Kind.Literal || typeof firstArg.value !== 'string') {
1127
- return undefined;
1361
+ if ((0, java_1.isIdentifier)(specifierNode)) {
1362
+ return specifierNode.simpleName;
1363
+ }
1364
+ const alias = specifierNode.alias;
1365
+ if ((0, java_1.isIdentifier)(alias)) {
1366
+ return alias.simpleName;
1128
1367
  }
1129
- return (_b = firstArg.value) === null || _b === void 0 ? void 0 : _b.toString();
1130
1368
  }
1131
- /**
1132
- * Get the import name from an import specifier
1133
- */
1134
- getImportName(specifier) {
1135
- const spec = specifier.specifier;
1136
- if ((spec === null || spec === void 0 ? void 0 : spec.kind) === tree_1.JS.Kind.Alias) {
1137
- const alias = spec;
1138
- const propertyName = alias.propertyName.element;
1139
- if ((propertyName === null || propertyName === void 0 ? void 0 : propertyName.kind) === java_1.J.Kind.Identifier) {
1140
- return propertyName.simpleName;
1141
- }
1369
+ return undefined;
1370
+ }
1371
+ /** Whether `specifier` imports the member `key`, under whatever local name it binds it to. */
1372
+ function namedSpecifierImports(specifier, key) {
1373
+ if ((0, java_1.isIdentifier)(specifier)) {
1374
+ return specifier.simpleName === key;
1375
+ }
1376
+ if (specifier.kind === tree_1.JS.Kind.Alias) {
1377
+ const propertyName = specifier.propertyName.element;
1378
+ return (0, java_1.isIdentifier)(propertyName) && propertyName.simpleName === key;
1379
+ }
1380
+ return false;
1381
+ }
1382
+ /** Whether the named specifier binding `key` carries its own inline `type`, as in `{type a, b}`. */
1383
+ function namedSpecifierIsTypeOnly(imp, key) {
1384
+ var _a;
1385
+ const namedBindings = (_a = imp.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings;
1386
+ if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) !== tree_1.JS.Kind.NamedImports) {
1387
+ return false;
1388
+ }
1389
+ for (const elem of namedBindings.elements.elements) {
1390
+ if (namedSpecifierImports(elem.element.specifier, key)) {
1391
+ return elem.element.importType.element;
1142
1392
  }
1143
- else if ((spec === null || spec === void 0 ? void 0 : spec.kind) === java_1.J.Kind.Identifier) {
1144
- return spec.simpleName;
1393
+ }
1394
+ return false;
1395
+ }
1396
+ /** How many named specifiers `jsImport` carries, `0` for a default, namespace or side-effect import. */
1397
+ function namedImportCount(jsImport) {
1398
+ var _a;
1399
+ const namedBindings = (_a = jsImport.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings;
1400
+ return (namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_1.JS.Kind.NamedImports ? namedBindings.elements.elements.length : 0;
1401
+ }
1402
+ /**
1403
+ * Whether `jsImport`'s clause binds exactly one thing — a default, a namespace, or one named
1404
+ * specifier. That is the only shape a module-only move can rewrite in place: whichever binding
1405
+ * `importBinds` already matched is this one, so nothing else the clause carries goes along with it.
1406
+ */
1407
+ function isOnlyMember(jsImport) {
1408
+ var _a;
1409
+ const importClause = jsImport.importClause;
1410
+ if (!importClause) {
1411
+ return false;
1412
+ }
1413
+ const hasDefault = importClause.name !== undefined;
1414
+ const hasNamespace = ((_a = importClause.namedBindings) === null || _a === void 0 ? void 0 : _a.kind) === tree_1.JS.Kind.Alias;
1415
+ return (hasDefault ? 1 : 0) + (hasNamespace ? 1 : 0) + namedImportCount(jsImport) === 1;
1416
+ }
1417
+ /**
1418
+ * The existing binding for `member` of `module`, read from `cu`'s own import statements — what
1419
+ * `maybeRebind` reads before committing to a `RebindImport` edit.
1420
+ */
1421
+ function existingImportBinding(cu, module, member) {
1422
+ for (const stmt of cu.statements) {
1423
+ const element = stmt.element;
1424
+ if ((element === null || element === void 0 ? void 0 : element.kind) !== tree_1.JS.Kind.Import) {
1425
+ continue;
1426
+ }
1427
+ const localName = importBinds(element, module, member);
1428
+ if (localName !== undefined) {
1429
+ return { localName, onlyMemberOfStatement: isOnlyMember(element) };
1145
1430
  }
1146
- return '';
1147
1431
  }
1148
- /**
1149
- * Get the import alias from an import specifier
1150
- */
1151
- getImportAlias(specifier) {
1152
- var _a;
1153
- const spec = specifier.specifier;
1154
- if ((spec === null || spec === void 0 ? void 0 : spec.kind) === tree_1.JS.Kind.Alias) {
1155
- const alias = spec;
1156
- if (((_a = alias.alias) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier) {
1157
- return alias.alias.simpleName;
1158
- }
1432
+ return undefined;
1433
+ }
1434
+ /**
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.
1438
+ */
1439
+ function aliasing(local, member) {
1440
+ const propertyName = {
1441
+ id: (0, uuid_1.randomId)(),
1442
+ kind: java_1.J.Kind.Identifier,
1443
+ prefix: java_1.emptySpace,
1444
+ markers: markers_1.emptyMarkers,
1445
+ annotations: [],
1446
+ simpleName: member,
1447
+ type: undefined,
1448
+ fieldType: undefined
1449
+ };
1450
+ return {
1451
+ id: (0, uuid_1.randomId)(),
1452
+ kind: tree_1.JS.Kind.Alias,
1453
+ prefix: local.prefix,
1454
+ markers: markers_1.emptyMarkers,
1455
+ propertyName: (0, java_1.rightPadded)(propertyName, java_1.singleSpace),
1456
+ alias: Object.assign(Object.assign({}, local), { prefix: java_1.singleSpace })
1457
+ };
1458
+ }
1459
+ /**
1460
+ * Drops the binding for `member` from `jsImport`'s clause — the default, the namespace alias, or
1461
+ * one entry of the named list, whichever `member` names — keeping everything else the clause
1462
+ * binds. `ElementRemovalFormatter` carries the dropped binding's prefix onto whatever prints
1463
+ * next, the same way `RemoveImport` keeps formatting sane when trimming a list.
1464
+ */
1465
+ function removeBinding(jsImport, member) {
1466
+ var _a;
1467
+ const importClause = jsImport.importClause;
1468
+ if (!importClause) {
1469
+ return jsImport;
1470
+ }
1471
+ const key = memberName(member);
1472
+ if (key === undefined) {
1473
+ if (!importClause.name) {
1474
+ return jsImport;
1159
1475
  }
1160
- return undefined;
1476
+ const namedBindings = importClause.namedBindings;
1477
+ if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_1.JS.Kind.NamedImports) {
1478
+ // `NamedImports` keeps the space before its own `{` on the container's `before`,
1479
+ // not on its own prefix, so the removed default's prefix has to land there instead.
1480
+ const namedImports = namedBindings;
1481
+ const updated = Object.assign(Object.assign({}, namedImports), { elements: Object.assign(Object.assign({}, namedImports.elements), { before: importClause.name.element.prefix }) });
1482
+ return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { name: undefined, namedBindings: updated }) });
1483
+ }
1484
+ if (namedBindings) {
1485
+ const formatter = new java_1.ElementRemovalFormatter();
1486
+ formatter.markRemoved(importClause.name.element);
1487
+ return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { name: undefined, namedBindings: formatter.processKept(namedBindings) }) });
1488
+ }
1489
+ return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { name: undefined }) });
1490
+ }
1491
+ if (key === '*') {
1492
+ return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { namedBindings: undefined }) });
1161
1493
  }
1494
+ if (((_a = importClause.namedBindings) === null || _a === void 0 ? void 0 : _a.kind) !== tree_1.JS.Kind.NamedImports) {
1495
+ return jsImport;
1496
+ }
1497
+ const namedImports = importClause.namedBindings;
1498
+ const formatter = new java_1.ElementRemovalFormatter();
1499
+ const kept = [];
1500
+ for (const entry of namedImports.elements.elements) {
1501
+ if (namedSpecifierImports(entry.element.specifier, key)) {
1502
+ formatter.markRemoved(entry.element);
1503
+ }
1504
+ else {
1505
+ kept.push(Object.assign(Object.assign({}, entry), { element: formatter.processKept(entry.element) }));
1506
+ }
1507
+ }
1508
+ if (kept.length === 0) {
1509
+ // An emptied brace list still prints, as `import D, {} from "m"`, so it goes with its
1510
+ // last member; the caller drops the whole statement when no default remains either.
1511
+ return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { namedBindings: undefined }) });
1512
+ }
1513
+ const updatedNamedImports = Object.assign(Object.assign({}, namedImports), { elements: Object.assign(Object.assign({}, namedImports.elements), { elements: kept }) });
1514
+ return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { namedBindings: updatedNamedImports }) });
1162
1515
  }
1163
- exports.AddImport = AddImport;
1516
+ /**
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.
1521
+ *
1522
+ * Not built on `RemoveImport`/`maybeUnbind`: those only drop a binding once nothing references
1523
+ * it, but a rebind moves one that is still in use — removal here has to be unconditional.
1524
+ */
1525
+ class RebindImport extends visitor_1.JavaScriptVisitor {
1526
+ constructor(from, to, localName) {
1527
+ super();
1528
+ this.from = from;
1529
+ this.to = to;
1530
+ this.localName = localName;
1531
+ this.transformedInPlace = false;
1532
+ this.typeOnly = false;
1533
+ }
1534
+ async visitJsCompilationUnit(cu, p) {
1535
+ const visited = await super.visitJsCompilationUnit(cu, p);
1536
+ if (!this.transformedInPlace) {
1537
+ bindImport(this, {
1538
+ module: this.to.module,
1539
+ member: this.to.member,
1540
+ alias: this.localName,
1541
+ typeOnly: this.typeOnly,
1542
+ onlyIfReferenced: false
1543
+ });
1544
+ }
1545
+ return visited;
1546
+ }
1547
+ async visitImportDeclaration(jsImport, p) {
1548
+ var _a, _b;
1549
+ const imp = await super.visitImportDeclaration(jsImport, p);
1550
+ const key = memberName(this.from.member);
1551
+ if (importBinds(imp, this.from.module, this.from.member) === undefined) {
1552
+ return imp;
1553
+ }
1554
+ // A moved named specifier's own inline `type` marks it type-only even where the clause
1555
+ // it's leaving is not — the replacement needs the same answer to stay type-safe.
1556
+ this.typeOnly = ((_b = (_a = imp.importClause) === null || _a === void 0 ? void 0 : _a.typeOnly) !== null && _b !== void 0 ? _b : false) ||
1557
+ (key !== undefined && key !== '*' && namedSpecifierIsTypeOnly(imp, key));
1558
+ if (!isOnlyMember(imp)) {
1559
+ return removeBinding(imp, this.from.member);
1560
+ }
1561
+ this.transformedInPlace = true;
1562
+ return (0, mutative_1.create)(imp, draft => {
1563
+ var _a;
1564
+ const literal = draft.moduleSpecifier.element;
1565
+ literal.value = this.to.module;
1566
+ const originalSource = literal.valueSource || `"${this.from.module}"`;
1567
+ const quoteChar = originalSource.startsWith("'") ? "'" : '"';
1568
+ 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
+ }
1590
+ }
1591
+ }
1592
+ });
1593
+ }
1594
+ }
1595
+ exports.RebindImport = RebindImport;
1164
1596
  //# sourceMappingURL=add-import.js.map