@openrewrite/rewrite 8.66.0-20251027-095903 → 8.66.0-20251027-133754
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.
- package/dist/java/tree.d.ts +1 -0
- package/dist/java/tree.d.ts.map +1 -1
- package/dist/java/tree.js +6 -1
- package/dist/java/tree.js.map +1 -1
- package/dist/javascript/add-import.d.ts +102 -0
- package/dist/javascript/add-import.d.ts.map +1 -0
- package/dist/javascript/add-import.js +789 -0
- package/dist/javascript/add-import.js.map +1 -0
- package/dist/javascript/format.d.ts.map +1 -1
- package/dist/javascript/format.js +9 -1
- package/dist/javascript/format.js.map +1 -1
- package/dist/javascript/index.d.ts +1 -0
- package/dist/javascript/index.d.ts.map +1 -1
- package/dist/javascript/index.js +1 -0
- package/dist/javascript/index.js.map +1 -1
- package/dist/javascript/type-mapping.js +1 -1
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/version.txt +1 -1
- package/package.json +1 -1
- package/src/java/tree.ts +6 -0
- package/src/javascript/add-import.ts +871 -0
- package/src/javascript/format.ts +10 -2
- package/src/javascript/index.ts +1 -0
- package/src/javascript/type-mapping.ts +1 -1
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AddImport = exports.ImportStyle = void 0;
|
|
13
|
+
exports.maybeAddImport = maybeAddImport;
|
|
14
|
+
const visitor_1 = require("./visitor");
|
|
15
|
+
const java_1 = require("../java");
|
|
16
|
+
const tree_1 = require("./tree");
|
|
17
|
+
const uuid_1 = require("../uuid");
|
|
18
|
+
const markers_1 = require("../markers");
|
|
19
|
+
const execution_1 = require("../execution");
|
|
20
|
+
var ImportStyle;
|
|
21
|
+
(function (ImportStyle) {
|
|
22
|
+
ImportStyle[ImportStyle["ES6Named"] = 0] = "ES6Named";
|
|
23
|
+
ImportStyle[ImportStyle["ES6Namespace"] = 1] = "ES6Namespace";
|
|
24
|
+
ImportStyle[ImportStyle["ES6Default"] = 2] = "ES6Default";
|
|
25
|
+
ImportStyle[ImportStyle["CommonJS"] = 3] = "CommonJS"; // const x = require('module')
|
|
26
|
+
})(ImportStyle || (exports.ImportStyle = ImportStyle = {}));
|
|
27
|
+
/**
|
|
28
|
+
* Register an AddImport visitor to add an import statement to a JavaScript/TypeScript file
|
|
29
|
+
* @param visitor The visitor to add the import addition to
|
|
30
|
+
* @param options Configuration options for the import to add
|
|
31
|
+
*/
|
|
32
|
+
function maybeAddImport(visitor, options) {
|
|
33
|
+
for (const v of visitor.afterVisit || []) {
|
|
34
|
+
if (v instanceof AddImport &&
|
|
35
|
+
v.target === options.target &&
|
|
36
|
+
v.member === options.member &&
|
|
37
|
+
v.alias === options.alias) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
visitor.afterVisit.push(new AddImport(options));
|
|
42
|
+
}
|
|
43
|
+
class AddImport extends visitor_1.JavaScriptVisitor {
|
|
44
|
+
constructor(options) {
|
|
45
|
+
var _a;
|
|
46
|
+
super();
|
|
47
|
+
this.target = options.target;
|
|
48
|
+
this.member = options.member;
|
|
49
|
+
this.alias = options.alias;
|
|
50
|
+
this.onlyIfReferenced = (_a = options.onlyIfReferenced) !== null && _a !== void 0 ? _a : true;
|
|
51
|
+
this.style = options.style;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Extract module name from a module specifier literal
|
|
55
|
+
*/
|
|
56
|
+
getModuleName(moduleSpecifier) {
|
|
57
|
+
var _a;
|
|
58
|
+
if (moduleSpecifier.kind !== java_1.J.Kind.Literal) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
return (_a = moduleSpecifier.value) === null || _a === void 0 ? void 0 : _a.toString();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if a method invocation is a require() call
|
|
65
|
+
*/
|
|
66
|
+
isRequireCall(methodInv) {
|
|
67
|
+
var _a;
|
|
68
|
+
return ((_a = methodInv.name) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier &&
|
|
69
|
+
methodInv.name.simpleName === 'require';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Determine the appropriate import style based on file type and existing imports
|
|
73
|
+
*/
|
|
74
|
+
determineImportStyle(compilationUnit) {
|
|
75
|
+
var _a;
|
|
76
|
+
// If style was explicitly provided, use it
|
|
77
|
+
if (this.style !== undefined) {
|
|
78
|
+
return this.style;
|
|
79
|
+
}
|
|
80
|
+
// Check the file extension from sourcePath
|
|
81
|
+
const sourcePath = compilationUnit.sourcePath;
|
|
82
|
+
const isTypeScript = sourcePath.endsWith('.ts') ||
|
|
83
|
+
sourcePath.endsWith('.tsx') ||
|
|
84
|
+
sourcePath.endsWith('.mts') ||
|
|
85
|
+
sourcePath.endsWith('.cts');
|
|
86
|
+
// Check for .cjs extension - must use CommonJS
|
|
87
|
+
if (sourcePath.endsWith('.cjs')) {
|
|
88
|
+
return ImportStyle.CommonJS;
|
|
89
|
+
}
|
|
90
|
+
// First, check if there's already an import from the same module
|
|
91
|
+
// and match that style
|
|
92
|
+
const existingStyleForModule = this.findExistingImportStyleForModule(compilationUnit);
|
|
93
|
+
if (existingStyleForModule !== null) {
|
|
94
|
+
return existingStyleForModule;
|
|
95
|
+
}
|
|
96
|
+
// For .mjs or TypeScript, prefer ES6
|
|
97
|
+
if (sourcePath.endsWith('.mjs') || isTypeScript) {
|
|
98
|
+
// If we're importing a member, use named imports
|
|
99
|
+
if (this.member !== undefined) {
|
|
100
|
+
return ImportStyle.ES6Named;
|
|
101
|
+
}
|
|
102
|
+
// Otherwise default import
|
|
103
|
+
return ImportStyle.ES6Default;
|
|
104
|
+
}
|
|
105
|
+
// For .js files, check what style is predominantly being used
|
|
106
|
+
let hasNamedImports = false;
|
|
107
|
+
let hasNamespaceImports = false;
|
|
108
|
+
let hasDefaultImports = false;
|
|
109
|
+
let hasCommonJSRequires = false;
|
|
110
|
+
for (const stmt of compilationUnit.statements) {
|
|
111
|
+
const statement = stmt.element;
|
|
112
|
+
// Check for ES6 imports
|
|
113
|
+
if ((statement === null || statement === void 0 ? void 0 : statement.kind) === tree_1.JS.Kind.Import) {
|
|
114
|
+
const jsImport = statement;
|
|
115
|
+
const importClause = jsImport.importClause;
|
|
116
|
+
if (importClause) {
|
|
117
|
+
// Check for named bindings
|
|
118
|
+
if (importClause.namedBindings) {
|
|
119
|
+
if (importClause.namedBindings.kind === tree_1.JS.Kind.NamedImports) {
|
|
120
|
+
hasNamedImports = true;
|
|
121
|
+
}
|
|
122
|
+
else if (importClause.namedBindings.kind === java_1.J.Kind.Identifier ||
|
|
123
|
+
importClause.namedBindings.kind === tree_1.JS.Kind.Alias) {
|
|
124
|
+
// import * as x from 'module'
|
|
125
|
+
hasNamespaceImports = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Check for default import
|
|
129
|
+
if (importClause.name) {
|
|
130
|
+
hasDefaultImports = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Check for CommonJS requires
|
|
135
|
+
if ((statement === null || statement === void 0 ? void 0 : statement.kind) === java_1.J.Kind.VariableDeclarations) {
|
|
136
|
+
const varDecl = statement;
|
|
137
|
+
if (varDecl.variables.length === 1) {
|
|
138
|
+
const namedVar = varDecl.variables[0].element;
|
|
139
|
+
const initializer = (_a = namedVar === null || namedVar === void 0 ? void 0 : namedVar.initializer) === null || _a === void 0 ? void 0 : _a.element;
|
|
140
|
+
if ((initializer === null || initializer === void 0 ? void 0 : initializer.kind) === java_1.J.Kind.MethodInvocation &&
|
|
141
|
+
this.isRequireCall(initializer)) {
|
|
142
|
+
hasCommonJSRequires = true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Prefer matching the predominant style
|
|
148
|
+
// If file uses CommonJS, stick with it
|
|
149
|
+
if (hasCommonJSRequires && !hasNamedImports && !hasNamespaceImports && !hasDefaultImports) {
|
|
150
|
+
return ImportStyle.CommonJS;
|
|
151
|
+
}
|
|
152
|
+
// If importing a member, prefer named imports if they exist in the file
|
|
153
|
+
if (this.member !== undefined) {
|
|
154
|
+
if (hasNamedImports) {
|
|
155
|
+
return ImportStyle.ES6Named;
|
|
156
|
+
}
|
|
157
|
+
if (hasNamespaceImports) {
|
|
158
|
+
return ImportStyle.ES6Namespace;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// For default/whole module imports
|
|
162
|
+
if (this.member === undefined) {
|
|
163
|
+
if (hasNamespaceImports) {
|
|
164
|
+
return ImportStyle.ES6Namespace;
|
|
165
|
+
}
|
|
166
|
+
if (hasDefaultImports) {
|
|
167
|
+
return ImportStyle.ES6Default;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Default to named imports for members, default imports for modules
|
|
171
|
+
return this.member !== undefined ? ImportStyle.ES6Named : ImportStyle.ES6Default;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Find the import style used for an existing import from the same module
|
|
175
|
+
*/
|
|
176
|
+
findExistingImportStyleForModule(compilationUnit) {
|
|
177
|
+
var _a, _b;
|
|
178
|
+
for (const stmt of compilationUnit.statements) {
|
|
179
|
+
const statement = stmt.element;
|
|
180
|
+
// Check ES6 imports
|
|
181
|
+
if ((statement === null || statement === void 0 ? void 0 : statement.kind) === tree_1.JS.Kind.Import) {
|
|
182
|
+
const jsImport = statement;
|
|
183
|
+
const moduleSpecifier = (_a = jsImport.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element;
|
|
184
|
+
if (moduleSpecifier) {
|
|
185
|
+
const moduleName = this.getModuleName(moduleSpecifier);
|
|
186
|
+
if (moduleName === this.target) {
|
|
187
|
+
const importClause = jsImport.importClause;
|
|
188
|
+
if (importClause === null || importClause === void 0 ? void 0 : importClause.namedBindings) {
|
|
189
|
+
if (importClause.namedBindings.kind === tree_1.JS.Kind.NamedImports) {
|
|
190
|
+
return ImportStyle.ES6Named;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
return ImportStyle.ES6Namespace;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (importClause === null || importClause === void 0 ? void 0 : importClause.name) {
|
|
197
|
+
return ImportStyle.ES6Default;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Check CommonJS requires
|
|
203
|
+
if ((statement === null || statement === void 0 ? void 0 : statement.kind) === java_1.J.Kind.VariableDeclarations) {
|
|
204
|
+
const varDecl = statement;
|
|
205
|
+
if (varDecl.variables.length === 1) {
|
|
206
|
+
const namedVar = varDecl.variables[0].element;
|
|
207
|
+
const initializer = (_b = namedVar === null || namedVar === void 0 ? void 0 : namedVar.initializer) === null || _b === void 0 ? void 0 : _b.element;
|
|
208
|
+
if ((initializer === null || initializer === void 0 ? void 0 : initializer.kind) === java_1.J.Kind.MethodInvocation &&
|
|
209
|
+
this.isRequireCall(initializer)) {
|
|
210
|
+
const moduleName = this.getModuleNameFromRequire(initializer);
|
|
211
|
+
if (moduleName === this.target) {
|
|
212
|
+
return ImportStyle.CommonJS;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
visitJsCompilationUnit(compilationUnit, p) {
|
|
221
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
222
|
+
// First, check if the import already exists
|
|
223
|
+
const hasImport = yield this.checkImportExists(compilationUnit);
|
|
224
|
+
if (hasImport) {
|
|
225
|
+
return compilationUnit;
|
|
226
|
+
}
|
|
227
|
+
// If onlyIfReferenced is true, check if the identifier is actually used
|
|
228
|
+
if (this.onlyIfReferenced) {
|
|
229
|
+
const isReferenced = yield this.checkIdentifierReferenced(compilationUnit);
|
|
230
|
+
if (!isReferenced) {
|
|
231
|
+
return compilationUnit;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Determine the appropriate import style
|
|
235
|
+
const importStyle = this.determineImportStyle(compilationUnit);
|
|
236
|
+
// For ES6 named imports, check if we can merge into an existing import from the same module
|
|
237
|
+
if (importStyle === ImportStyle.ES6Named && this.member !== undefined) {
|
|
238
|
+
const mergedCu = yield this.tryMergeIntoExistingImport(compilationUnit, p);
|
|
239
|
+
if (mergedCu !== compilationUnit) {
|
|
240
|
+
return mergedCu;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Add the import using the appropriate style
|
|
244
|
+
if (importStyle === ImportStyle.CommonJS) {
|
|
245
|
+
// TODO: Implement CommonJS require creation
|
|
246
|
+
// For now, fall back to ES6 imports
|
|
247
|
+
// return this.addCommonJSRequire(compilationUnit, p);
|
|
248
|
+
}
|
|
249
|
+
// Add ES6 import (handles ES6Named, ES6Namespace, ES6Default)
|
|
250
|
+
return this.produceJavaScript(compilationUnit, p, (draft) => __awaiter(this, void 0, void 0, function* () {
|
|
251
|
+
// Find the position to insert the import
|
|
252
|
+
const insertionIndex = this.findImportInsertionIndex(compilationUnit);
|
|
253
|
+
const newImport = yield this.createImportStatement(compilationUnit, insertionIndex, p);
|
|
254
|
+
// Insert the import at the appropriate position
|
|
255
|
+
// Create semicolon marker for the import statement
|
|
256
|
+
const semicolonMarkers = (0, markers_1.markers)({
|
|
257
|
+
kind: java_1.J.Markers.Semicolon,
|
|
258
|
+
id: (0, uuid_1.randomId)()
|
|
259
|
+
});
|
|
260
|
+
if (insertionIndex === 0) {
|
|
261
|
+
// Insert at the beginning
|
|
262
|
+
// The `after` space should be empty since semicolon is printed after it
|
|
263
|
+
// The spacing comes from updating the next statement's prefix
|
|
264
|
+
const updatedStatements = compilationUnit.statements.length > 0
|
|
265
|
+
? [
|
|
266
|
+
(0, java_1.rightPadded)(newImport, java_1.emptySpace, semicolonMarkers),
|
|
267
|
+
Object.assign(Object.assign({}, compilationUnit.statements[0]), { element: compilationUnit.statements[0].element
|
|
268
|
+
? Object.assign(Object.assign({}, compilationUnit.statements[0].element), { prefix: (0, java_1.space)("\n\n") }) : undefined }),
|
|
269
|
+
...compilationUnit.statements.slice(1)
|
|
270
|
+
]
|
|
271
|
+
: [(0, java_1.rightPadded)(newImport, java_1.emptySpace, semicolonMarkers)];
|
|
272
|
+
draft.statements = updatedStatements;
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
// Insert after existing imports
|
|
276
|
+
const before = compilationUnit.statements.slice(0, insertionIndex);
|
|
277
|
+
const after = compilationUnit.statements.slice(insertionIndex);
|
|
278
|
+
//The `after` space is empty, spacing comes from next statement's prefix
|
|
279
|
+
// Ensure the next statement has at least one newline in its prefix
|
|
280
|
+
if (after.length > 0 && after[0].element) {
|
|
281
|
+
const currentPrefix = after[0].element.prefix;
|
|
282
|
+
const needsNewline = !currentPrefix.whitespace.includes('\n');
|
|
283
|
+
const updatedNextStatement = needsNewline ? Object.assign(Object.assign({}, after[0]), { element: Object.assign(Object.assign({}, after[0].element), { prefix: (0, java_1.space)("\n" + currentPrefix.whitespace) }) }) : after[0];
|
|
284
|
+
draft.statements = [
|
|
285
|
+
...before,
|
|
286
|
+
(0, java_1.rightPadded)(newImport, java_1.emptySpace, semicolonMarkers),
|
|
287
|
+
updatedNextStatement,
|
|
288
|
+
...after.slice(1)
|
|
289
|
+
];
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
draft.statements = [
|
|
293
|
+
...before,
|
|
294
|
+
(0, java_1.rightPadded)(newImport, java_1.emptySpace, semicolonMarkers),
|
|
295
|
+
...after
|
|
296
|
+
];
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}));
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Try to merge the new member into an existing import from the same module
|
|
304
|
+
*/
|
|
305
|
+
tryMergeIntoExistingImport(compilationUnit, p) {
|
|
306
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
307
|
+
var _a;
|
|
308
|
+
for (let i = 0; i < compilationUnit.statements.length; i++) {
|
|
309
|
+
const stmt = compilationUnit.statements[i];
|
|
310
|
+
const statement = stmt.element;
|
|
311
|
+
if ((statement === null || statement === void 0 ? void 0 : statement.kind) === tree_1.JS.Kind.Import) {
|
|
312
|
+
const jsImport = statement;
|
|
313
|
+
const moduleSpecifier = (_a = jsImport.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element;
|
|
314
|
+
if (!moduleSpecifier) {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const moduleName = this.getModuleName(moduleSpecifier);
|
|
318
|
+
// Check if this is an import from our target module
|
|
319
|
+
if (moduleName !== this.target) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
const importClause = jsImport.importClause;
|
|
323
|
+
if (!importClause || !importClause.namedBindings) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
// Only merge into NamedImports, not namespace imports
|
|
327
|
+
if (importClause.namedBindings.kind !== tree_1.JS.Kind.NamedImports) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
// We found a matching import with named bindings - merge into it
|
|
331
|
+
return this.produceJavaScript(compilationUnit, p, (draft) => __awaiter(this, void 0, void 0, function* () {
|
|
332
|
+
const namedImports = importClause.namedBindings;
|
|
333
|
+
// Create the new specifier with a space prefix (since it's not the first element)
|
|
334
|
+
const newSpecifierBase = this.createImportSpecifier();
|
|
335
|
+
const newSpecifier = Object.assign(Object.assign({}, newSpecifierBase), { prefix: java_1.singleSpace });
|
|
336
|
+
// Add the new specifier to the elements
|
|
337
|
+
const updatedNamedImports = yield this.produceJavaScript(namedImports, p, (namedDraft) => __awaiter(this, void 0, void 0, function* () {
|
|
338
|
+
namedDraft.elements = Object.assign(Object.assign({}, namedImports.elements), { elements: [
|
|
339
|
+
...namedImports.elements.elements,
|
|
340
|
+
(0, java_1.rightPadded)(newSpecifier, java_1.emptySpace)
|
|
341
|
+
] });
|
|
342
|
+
}));
|
|
343
|
+
// Update the import with the new named imports
|
|
344
|
+
const updatedImport = yield this.produceJavaScript(jsImport, p, (importDraft) => __awaiter(this, void 0, void 0, function* () {
|
|
345
|
+
importDraft.importClause = yield this.produceJavaScript(importClause, p, (clauseDraft) => __awaiter(this, void 0, void 0, function* () {
|
|
346
|
+
clauseDraft.namedBindings = updatedNamedImports;
|
|
347
|
+
}));
|
|
348
|
+
}));
|
|
349
|
+
// Replace the statement in the compilation unit
|
|
350
|
+
draft.statements = compilationUnit.statements.map((s, idx) => idx === i ? Object.assign(Object.assign({}, s), { element: updatedImport }) : s);
|
|
351
|
+
}));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return compilationUnit;
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Check if the import already exists in the compilation unit
|
|
359
|
+
*/
|
|
360
|
+
checkImportExists(compilationUnit) {
|
|
361
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
362
|
+
for (const stmt of compilationUnit.statements) {
|
|
363
|
+
const statement = stmt.element;
|
|
364
|
+
// Check ES6 imports
|
|
365
|
+
if ((statement === null || statement === void 0 ? void 0 : statement.kind) === tree_1.JS.Kind.Import) {
|
|
366
|
+
const jsImport = statement;
|
|
367
|
+
if (this.isMatchingImport(jsImport)) {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Check CommonJS require statements
|
|
372
|
+
if ((statement === null || statement === void 0 ? void 0 : statement.kind) === java_1.J.Kind.VariableDeclarations) {
|
|
373
|
+
const varDecl = statement;
|
|
374
|
+
if (this.isMatchingRequire(varDecl)) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return false;
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Check if the import matches what we're trying to add
|
|
384
|
+
*/
|
|
385
|
+
isMatchingImport(jsImport) {
|
|
386
|
+
var _a, _b;
|
|
387
|
+
// Check module specifier
|
|
388
|
+
const moduleSpecifier = (_a = jsImport.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element;
|
|
389
|
+
if (!moduleSpecifier) {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
const moduleName = this.getModuleName(moduleSpecifier);
|
|
393
|
+
if (moduleName !== this.target) {
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
const importClause = jsImport.importClause;
|
|
397
|
+
if (!importClause) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
// Check if the specific member or default import already exists
|
|
401
|
+
if (this.member === undefined) {
|
|
402
|
+
// We're adding a default import, check if one exists
|
|
403
|
+
return importClause.name !== undefined;
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
// We're adding a named import, check if it exists
|
|
407
|
+
const namedBindings = importClause.namedBindings;
|
|
408
|
+
if (!namedBindings) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
if (namedBindings.kind === tree_1.JS.Kind.NamedImports) {
|
|
412
|
+
const namedImports = namedBindings;
|
|
413
|
+
for (const elem of namedImports.elements.elements) {
|
|
414
|
+
if (((_b = elem.element) === null || _b === void 0 ? void 0 : _b.kind) === tree_1.JS.Kind.ImportSpecifier) {
|
|
415
|
+
const specifier = elem.element;
|
|
416
|
+
const importName = this.getImportName(specifier);
|
|
417
|
+
const aliasName = this.getImportAlias(specifier);
|
|
418
|
+
if (importName === this.member && aliasName === this.alias) {
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Check if this is a matching CommonJS require statement
|
|
429
|
+
*/
|
|
430
|
+
isMatchingRequire(varDecl) {
|
|
431
|
+
var _a, _b, _c;
|
|
432
|
+
if (varDecl.variables.length !== 1) {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
const namedVar = varDecl.variables[0].element;
|
|
436
|
+
if (!namedVar) {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
const initializer = (_a = namedVar.initializer) === null || _a === void 0 ? void 0 : _a.element;
|
|
440
|
+
if (!initializer || initializer.kind !== java_1.J.Kind.MethodInvocation) {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
const methodInv = initializer;
|
|
444
|
+
if (!this.isRequireCall(methodInv)) {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
const moduleName = this.getModuleNameFromRequire(methodInv);
|
|
448
|
+
if (moduleName !== this.target) {
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
// Check if the variable name matches what we're trying to add
|
|
452
|
+
const pattern = namedVar.name;
|
|
453
|
+
if (this.member === undefined && (pattern === null || pattern === void 0 ? void 0 : pattern.kind) === java_1.J.Kind.Identifier) {
|
|
454
|
+
// Default import style: const fs = require('fs')
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
else if (this.member !== undefined && (pattern === null || pattern === void 0 ? void 0 : pattern.kind) === tree_1.JS.Kind.ObjectBindingPattern) {
|
|
458
|
+
// Destructured import: const { member } = require('module')
|
|
459
|
+
const objectPattern = pattern;
|
|
460
|
+
for (const elem of objectPattern.bindings.elements) {
|
|
461
|
+
if (((_b = elem.element) === null || _b === void 0 ? void 0 : _b.kind) === tree_1.JS.Kind.BindingElement) {
|
|
462
|
+
const bindingElem = elem.element;
|
|
463
|
+
const name = (_c = bindingElem.name) === null || _c === void 0 ? void 0 : _c.simpleName;
|
|
464
|
+
if (name === (this.alias || this.member)) {
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Check if the identifier is actually referenced in the file
|
|
474
|
+
*/
|
|
475
|
+
checkIdentifierReferenced(compilationUnit) {
|
|
476
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
477
|
+
// Use type attribution to detect if the identifier is referenced
|
|
478
|
+
// Map of module name -> Set of member names used from that module
|
|
479
|
+
const usedImports = new Map();
|
|
480
|
+
// Helper to record usage of a method from a module
|
|
481
|
+
const recordMethodUsage = (methodType) => {
|
|
482
|
+
const moduleName = java_1.Type.FullyQualified.getFullyQualifiedName(methodType.declaringType);
|
|
483
|
+
if (moduleName) {
|
|
484
|
+
if (!usedImports.has(moduleName)) {
|
|
485
|
+
usedImports.set(moduleName, new Set());
|
|
486
|
+
}
|
|
487
|
+
usedImports.get(moduleName).add(methodType.name);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
// Create a visitor to collect used identifiers with their type attribution
|
|
491
|
+
const collector = new class extends visitor_1.JavaScriptVisitor {
|
|
492
|
+
visitIdentifier(identifier, p) {
|
|
493
|
+
const _super = Object.create(null, {
|
|
494
|
+
visitIdentifier: { get: () => super.visitIdentifier }
|
|
495
|
+
});
|
|
496
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
497
|
+
const type = identifier.type;
|
|
498
|
+
if (type && java_1.Type.isMethod(type)) {
|
|
499
|
+
recordMethodUsage(type);
|
|
500
|
+
}
|
|
501
|
+
return _super.visitIdentifier.call(this, identifier, p);
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
visitMethodInvocation(methodInvocation, p) {
|
|
505
|
+
const _super = Object.create(null, {
|
|
506
|
+
visitMethodInvocation: { get: () => super.visitMethodInvocation }
|
|
507
|
+
});
|
|
508
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
509
|
+
if (methodInvocation.methodType) {
|
|
510
|
+
recordMethodUsage(methodInvocation.methodType);
|
|
511
|
+
}
|
|
512
|
+
return _super.visitMethodInvocation.call(this, methodInvocation, p);
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
visitFunctionCall(functionCall, p) {
|
|
516
|
+
const _super = Object.create(null, {
|
|
517
|
+
visitFunctionCall: { get: () => super.visitFunctionCall }
|
|
518
|
+
});
|
|
519
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
520
|
+
if (functionCall.methodType) {
|
|
521
|
+
recordMethodUsage(functionCall.methodType);
|
|
522
|
+
}
|
|
523
|
+
return _super.visitFunctionCall.call(this, functionCall, p);
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
visitFieldAccess(fieldAccess, p) {
|
|
527
|
+
const _super = Object.create(null, {
|
|
528
|
+
visitFieldAccess: { get: () => super.visitFieldAccess }
|
|
529
|
+
});
|
|
530
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
531
|
+
const type = fieldAccess.type;
|
|
532
|
+
if (type && java_1.Type.isMethod(type)) {
|
|
533
|
+
recordMethodUsage(type);
|
|
534
|
+
}
|
|
535
|
+
return _super.visitFieldAccess.call(this, fieldAccess, p);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
yield collector.visit(compilationUnit, new execution_1.ExecutionContext());
|
|
540
|
+
// Check if our target import is used based on type attribution
|
|
541
|
+
const moduleMembers = usedImports.get(this.target);
|
|
542
|
+
if (!moduleMembers) {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
// For specific members, check if that member is used; otherwise check if any member is used
|
|
546
|
+
return this.member ? moduleMembers.has(this.member) : moduleMembers.size > 0;
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Create a new import statement
|
|
551
|
+
*/
|
|
552
|
+
createImportStatement(compilationUnit, insertionIndex, p) {
|
|
553
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
554
|
+
// Determine the appropriate prefix (spacing before the import)
|
|
555
|
+
const prefix = this.determineImportPrefix(compilationUnit, insertionIndex);
|
|
556
|
+
// Create the module specifier
|
|
557
|
+
const moduleSpecifier = {
|
|
558
|
+
id: (0, uuid_1.randomId)(),
|
|
559
|
+
kind: java_1.J.Kind.Literal,
|
|
560
|
+
prefix: java_1.singleSpace,
|
|
561
|
+
markers: markers_1.emptyMarkers,
|
|
562
|
+
value: `'${this.target}'`,
|
|
563
|
+
valueSource: `'${this.target}'`,
|
|
564
|
+
unicodeEscapes: [],
|
|
565
|
+
type: undefined
|
|
566
|
+
};
|
|
567
|
+
let importClause;
|
|
568
|
+
if (this.member === undefined) {
|
|
569
|
+
// Default import: import target from 'module'
|
|
570
|
+
const defaultName = {
|
|
571
|
+
id: (0, uuid_1.randomId)(),
|
|
572
|
+
kind: java_1.J.Kind.Identifier,
|
|
573
|
+
prefix: java_1.singleSpace,
|
|
574
|
+
markers: markers_1.emptyMarkers,
|
|
575
|
+
annotations: [],
|
|
576
|
+
simpleName: this.alias || this.target,
|
|
577
|
+
type: undefined,
|
|
578
|
+
fieldType: undefined
|
|
579
|
+
};
|
|
580
|
+
importClause = {
|
|
581
|
+
id: (0, uuid_1.randomId)(),
|
|
582
|
+
kind: tree_1.JS.Kind.ImportClause,
|
|
583
|
+
prefix: java_1.emptySpace,
|
|
584
|
+
markers: markers_1.emptyMarkers,
|
|
585
|
+
typeOnly: false,
|
|
586
|
+
name: (0, java_1.rightPadded)(defaultName, java_1.emptySpace),
|
|
587
|
+
namedBindings: undefined
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
// Named import: import { member } from 'module'
|
|
592
|
+
const importSpec = this.createImportSpecifier();
|
|
593
|
+
const namedImports = {
|
|
594
|
+
id: (0, uuid_1.randomId)(),
|
|
595
|
+
kind: tree_1.JS.Kind.NamedImports,
|
|
596
|
+
prefix: java_1.singleSpace,
|
|
597
|
+
markers: markers_1.emptyMarkers,
|
|
598
|
+
elements: {
|
|
599
|
+
kind: java_1.J.Kind.Container,
|
|
600
|
+
before: java_1.emptySpace,
|
|
601
|
+
elements: [(0, java_1.rightPadded)(importSpec, java_1.emptySpace)],
|
|
602
|
+
markers: markers_1.emptyMarkers
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
importClause = {
|
|
606
|
+
id: (0, uuid_1.randomId)(),
|
|
607
|
+
kind: tree_1.JS.Kind.ImportClause,
|
|
608
|
+
prefix: java_1.emptySpace,
|
|
609
|
+
markers: markers_1.emptyMarkers,
|
|
610
|
+
typeOnly: false,
|
|
611
|
+
name: undefined,
|
|
612
|
+
namedBindings: namedImports
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
const jsImport = {
|
|
616
|
+
id: (0, uuid_1.randomId)(),
|
|
617
|
+
kind: tree_1.JS.Kind.Import,
|
|
618
|
+
prefix,
|
|
619
|
+
markers: markers_1.emptyMarkers,
|
|
620
|
+
modifiers: [],
|
|
621
|
+
importClause,
|
|
622
|
+
moduleSpecifier: {
|
|
623
|
+
kind: java_1.J.Kind.LeftPadded,
|
|
624
|
+
before: java_1.singleSpace,
|
|
625
|
+
element: moduleSpecifier,
|
|
626
|
+
markers: markers_1.emptyMarkers
|
|
627
|
+
},
|
|
628
|
+
initializer: undefined
|
|
629
|
+
};
|
|
630
|
+
return jsImport;
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Create an import specifier for a named import
|
|
635
|
+
*/
|
|
636
|
+
createImportSpecifier() {
|
|
637
|
+
let specifier;
|
|
638
|
+
if (this.alias) {
|
|
639
|
+
// Aliased import: import { member as alias } from 'module'
|
|
640
|
+
const propertyName = {
|
|
641
|
+
id: (0, uuid_1.randomId)(),
|
|
642
|
+
kind: java_1.J.Kind.Identifier,
|
|
643
|
+
prefix: java_1.emptySpace,
|
|
644
|
+
markers: markers_1.emptyMarkers,
|
|
645
|
+
annotations: [],
|
|
646
|
+
simpleName: this.member,
|
|
647
|
+
type: undefined,
|
|
648
|
+
fieldType: undefined
|
|
649
|
+
};
|
|
650
|
+
const aliasName = {
|
|
651
|
+
id: (0, uuid_1.randomId)(),
|
|
652
|
+
kind: java_1.J.Kind.Identifier,
|
|
653
|
+
prefix: java_1.singleSpace,
|
|
654
|
+
markers: markers_1.emptyMarkers,
|
|
655
|
+
annotations: [],
|
|
656
|
+
simpleName: this.alias,
|
|
657
|
+
type: undefined,
|
|
658
|
+
fieldType: undefined
|
|
659
|
+
};
|
|
660
|
+
specifier = {
|
|
661
|
+
id: (0, uuid_1.randomId)(),
|
|
662
|
+
kind: tree_1.JS.Kind.Alias,
|
|
663
|
+
prefix: java_1.emptySpace,
|
|
664
|
+
markers: markers_1.emptyMarkers,
|
|
665
|
+
propertyName: (0, java_1.rightPadded)(propertyName, java_1.singleSpace),
|
|
666
|
+
alias: aliasName
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
// Regular import: import { member } from 'module'
|
|
671
|
+
specifier = {
|
|
672
|
+
id: (0, uuid_1.randomId)(),
|
|
673
|
+
kind: java_1.J.Kind.Identifier,
|
|
674
|
+
prefix: java_1.emptySpace,
|
|
675
|
+
markers: markers_1.emptyMarkers,
|
|
676
|
+
annotations: [],
|
|
677
|
+
simpleName: this.member,
|
|
678
|
+
type: undefined,
|
|
679
|
+
fieldType: undefined
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
return {
|
|
683
|
+
id: (0, uuid_1.randomId)(),
|
|
684
|
+
kind: tree_1.JS.Kind.ImportSpecifier,
|
|
685
|
+
prefix: java_1.emptySpace,
|
|
686
|
+
markers: markers_1.emptyMarkers,
|
|
687
|
+
importType: {
|
|
688
|
+
kind: java_1.J.Kind.LeftPadded,
|
|
689
|
+
before: java_1.emptySpace,
|
|
690
|
+
element: false,
|
|
691
|
+
markers: markers_1.emptyMarkers
|
|
692
|
+
},
|
|
693
|
+
specifier
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Determine the appropriate spacing before the import statement
|
|
698
|
+
*/
|
|
699
|
+
determineImportPrefix(compilationUnit, insertionIndex) {
|
|
700
|
+
var _a;
|
|
701
|
+
// If inserting at the beginning (index 0), use the prefix of the first statement
|
|
702
|
+
// but only the whitespace part (preserve comments on the original first statement)
|
|
703
|
+
if (insertionIndex === 0 && compilationUnit.statements.length > 0) {
|
|
704
|
+
const firstPrefix = (_a = compilationUnit.statements[0].element) === null || _a === void 0 ? void 0 : _a.prefix;
|
|
705
|
+
if (firstPrefix) {
|
|
706
|
+
// Keep only whitespace, not comments
|
|
707
|
+
return {
|
|
708
|
+
kind: java_1.J.Kind.Space,
|
|
709
|
+
comments: [],
|
|
710
|
+
whitespace: firstPrefix.whitespace
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
return java_1.emptySpace;
|
|
714
|
+
}
|
|
715
|
+
// If inserting after other statements, ensure we have at least one newline
|
|
716
|
+
// to separate from the previous statement
|
|
717
|
+
return (0, java_1.space)("\n");
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Find the index where the new import should be inserted
|
|
721
|
+
*/
|
|
722
|
+
findImportInsertionIndex(compilationUnit) {
|
|
723
|
+
let lastImportIndex = -1;
|
|
724
|
+
for (let i = 0; i < compilationUnit.statements.length; i++) {
|
|
725
|
+
const statement = compilationUnit.statements[i].element;
|
|
726
|
+
if ((statement === null || statement === void 0 ? void 0 : statement.kind) === tree_1.JS.Kind.Import) {
|
|
727
|
+
lastImportIndex = i;
|
|
728
|
+
}
|
|
729
|
+
else if (lastImportIndex >= 0) {
|
|
730
|
+
// We've found a non-import after imports, insert after the last import
|
|
731
|
+
return lastImportIndex + 1;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
// If we found imports, insert after them
|
|
735
|
+
if (lastImportIndex >= 0) {
|
|
736
|
+
return lastImportIndex + 1;
|
|
737
|
+
}
|
|
738
|
+
// No imports found, insert at the beginning
|
|
739
|
+
return 0;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Get the module name from a require() call
|
|
743
|
+
*/
|
|
744
|
+
getModuleNameFromRequire(methodInv) {
|
|
745
|
+
var _a, _b;
|
|
746
|
+
const args = (_a = methodInv.arguments) === null || _a === void 0 ? void 0 : _a.elements;
|
|
747
|
+
if (!args || args.length === 0) {
|
|
748
|
+
return undefined;
|
|
749
|
+
}
|
|
750
|
+
const firstArg = args[0].element;
|
|
751
|
+
if (!firstArg || firstArg.kind !== java_1.J.Kind.Literal || typeof firstArg.value !== 'string') {
|
|
752
|
+
return undefined;
|
|
753
|
+
}
|
|
754
|
+
return (_b = firstArg.value) === null || _b === void 0 ? void 0 : _b.toString();
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Get the import name from an import specifier
|
|
758
|
+
*/
|
|
759
|
+
getImportName(specifier) {
|
|
760
|
+
const spec = specifier.specifier;
|
|
761
|
+
if ((spec === null || spec === void 0 ? void 0 : spec.kind) === tree_1.JS.Kind.Alias) {
|
|
762
|
+
const alias = spec;
|
|
763
|
+
const propertyName = alias.propertyName.element;
|
|
764
|
+
if ((propertyName === null || propertyName === void 0 ? void 0 : propertyName.kind) === java_1.J.Kind.Identifier) {
|
|
765
|
+
return propertyName.simpleName;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
else if ((spec === null || spec === void 0 ? void 0 : spec.kind) === java_1.J.Kind.Identifier) {
|
|
769
|
+
return spec.simpleName;
|
|
770
|
+
}
|
|
771
|
+
return '';
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Get the import alias from an import specifier
|
|
775
|
+
*/
|
|
776
|
+
getImportAlias(specifier) {
|
|
777
|
+
var _a;
|
|
778
|
+
const spec = specifier.specifier;
|
|
779
|
+
if ((spec === null || spec === void 0 ? void 0 : spec.kind) === tree_1.JS.Kind.Alias) {
|
|
780
|
+
const alias = spec;
|
|
781
|
+
if (((_a = alias.alias) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier) {
|
|
782
|
+
return alias.alias.simpleName;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
return undefined;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
exports.AddImport = AddImport;
|
|
789
|
+
//# sourceMappingURL=add-import.js.map
|