@openrewrite/rewrite 8.91.0 → 8.91.1
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/formatting-utils.d.ts +8 -5
- package/dist/java/formatting-utils.d.ts.map +1 -1
- package/dist/java/formatting-utils.js +14 -10
- package/dist/java/formatting-utils.js.map +1 -1
- package/dist/java/type.d.ts +2 -0
- package/dist/java/type.d.ts.map +1 -1
- package/dist/java/type.js +5 -0
- package/dist/java/type.js.map +1 -1
- package/dist/javascript/add-import.d.ts +39 -20
- package/dist/javascript/add-import.d.ts.map +1 -1
- package/dist/javascript/add-import.js +513 -243
- package/dist/javascript/add-import.js.map +1 -1
- package/dist/javascript/assertions.d.ts +7 -0
- package/dist/javascript/assertions.d.ts.map +1 -1
- package/dist/javascript/assertions.js +31 -5
- package/dist/javascript/assertions.js.map +1 -1
- package/dist/javascript/comparator.d.ts +5 -0
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js +34 -9
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/format/format.d.ts.map +1 -1
- package/dist/javascript/format/format.js +8 -9
- package/dist/javascript/format/format.js.map +1 -1
- package/dist/javascript/format/tabs-and-indents-visitor.d.ts +2 -0
- package/dist/javascript/format/tabs-and-indents-visitor.d.ts.map +1 -1
- package/dist/javascript/format/tabs-and-indents-visitor.js +17 -11
- package/dist/javascript/format/tabs-and-indents-visitor.js.map +1 -1
- package/dist/javascript/node-resolution-result.d.ts +19 -1
- package/dist/javascript/node-resolution-result.d.ts.map +1 -1
- package/dist/javascript/node-resolution-result.js +37 -2
- package/dist/javascript/node-resolution-result.js.map +1 -1
- package/dist/javascript/package-exported-types.d.ts.map +1 -1
- package/dist/javascript/package-exported-types.js +56 -72
- package/dist/javascript/package-exported-types.js.map +1 -1
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +10 -3
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/recipes/change-import.d.ts.map +1 -1
- package/dist/javascript/recipes/change-import.js +4 -8
- package/dist/javascript/recipes/change-import.js.map +1 -1
- package/dist/javascript/style.d.ts.map +1 -1
- package/dist/javascript/style.js +3 -10
- package/dist/javascript/style.js.map +1 -1
- package/dist/javascript/templating/bindings.d.ts +19 -0
- package/dist/javascript/templating/bindings.d.ts.map +1 -0
- package/dist/javascript/templating/bindings.js +121 -0
- package/dist/javascript/templating/bindings.js.map +1 -0
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +5 -6
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/engine.d.ts +18 -7
- package/dist/javascript/templating/engine.d.ts.map +1 -1
- package/dist/javascript/templating/engine.js +70 -42
- package/dist/javascript/templating/engine.js.map +1 -1
- package/dist/javascript/templating/index.d.ts +1 -1
- package/dist/javascript/templating/index.d.ts.map +1 -1
- package/dist/javascript/templating/index.js.map +1 -1
- package/dist/javascript/templating/pattern.d.ts.map +1 -1
- package/dist/javascript/templating/pattern.js +13 -10
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.d.ts +2 -0
- package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.js +14 -5
- package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
- package/dist/javascript/templating/rewrite.d.ts.map +1 -1
- package/dist/javascript/templating/rewrite.js +14 -18
- package/dist/javascript/templating/rewrite.js.map +1 -1
- package/dist/javascript/templating/template.d.ts +8 -0
- package/dist/javascript/templating/template.d.ts.map +1 -1
- package/dist/javascript/templating/template.js +43 -3
- package/dist/javascript/templating/template.js.map +1 -1
- package/dist/javascript/templating/types.d.ts +47 -1
- package/dist/javascript/templating/types.d.ts.map +1 -1
- package/dist/javascript/templating/utils.d.ts +5 -0
- package/dist/javascript/templating/utils.d.ts.map +1 -1
- package/dist/javascript/templating/utils.js +17 -0
- package/dist/javascript/templating/utils.js.map +1 -1
- package/dist/javascript/type-mapping.d.ts +1 -0
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +52 -2
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/rewrite-javascript-version.txt +1 -1
- package/dist/style.d.ts +2 -0
- package/dist/style.d.ts.map +1 -1
- package/dist/style.js +5 -0
- package/dist/style.js.map +1 -1
- package/dist/test/rewrite-test.d.ts +12 -0
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +40 -3
- package/dist/test/rewrite-test.js.map +1 -1
- package/package.json +1 -1
- package/src/java/formatting-utils.ts +14 -10
- package/src/java/type.ts +5 -0
- package/src/javascript/add-import.ts +590 -230
- package/src/javascript/assertions.ts +33 -5
- package/src/javascript/comparator.ts +39 -10
- package/src/javascript/format/format.ts +9 -10
- package/src/javascript/format/tabs-and-indents-visitor.ts +18 -12
- package/src/javascript/node-resolution-result.ts +59 -4
- package/src/javascript/package-exported-types.ts +60 -74
- package/src/javascript/parser.ts +10 -3
- package/src/javascript/recipes/change-import.ts +4 -7
- package/src/javascript/style.ts +3 -11
- package/src/javascript/templating/bindings.ts +129 -0
- package/src/javascript/templating/comparator.ts +5 -6
- package/src/javascript/templating/engine.ts +82 -44
- package/src/javascript/templating/index.ts +2 -0
- package/src/javascript/templating/pattern.ts +16 -15
- package/src/javascript/templating/placeholder-replacement.ts +16 -5
- package/src/javascript/templating/rewrite.ts +16 -18
- package/src/javascript/templating/template.ts +45 -3
- package/src/javascript/templating/types.ts +53 -1
- package/src/javascript/templating/utils.ts +17 -0
- package/src/javascript/type-mapping.ts +54 -2
- package/src/style.ts +5 -0
- package/src/test/rewrite-test.ts +61 -3
|
@@ -15,97 +15,505 @@ var ImportStyle;
|
|
|
15
15
|
ImportStyle[ImportStyle["ES6Default"] = 2] = "ES6Default";
|
|
16
16
|
ImportStyle[ImportStyle["CommonJS"] = 3] = "CommonJS"; // const x = require('module')
|
|
17
17
|
})(ImportStyle || (exports.ImportStyle = ImportStyle = {}));
|
|
18
|
-
/**
|
|
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 });
|
|
42
|
-
*/
|
|
43
18
|
function maybeAddImport(visitor, options) {
|
|
44
19
|
var _a, _b;
|
|
20
|
+
validate(options);
|
|
21
|
+
const module = moduleNameOf(options.module);
|
|
22
|
+
const sideEffectOnly = (_a = options.sideEffectOnly) !== null && _a !== void 0 ? _a : false;
|
|
23
|
+
const typeOnly = (_b = options.typeOnly) !== null && _b !== void 0 ? _b : false;
|
|
24
|
+
// A queued import binds the module as much as one already in the file, so it answers a later
|
|
25
|
+
// request the same way; a name a merged request never emits would be referenced but not bound.
|
|
45
26
|
for (const v of visitor.afterVisit || []) {
|
|
46
|
-
if (v instanceof AddImport
|
|
47
|
-
v.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
27
|
+
if (!(v instanceof AddImport) || v.module !== module ||
|
|
28
|
+
v.sideEffectOnly !== sideEffectOnly || v.typeOnly !== typeOnly) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
// How a specifier prints, or what name would be nice, does not say which binding is wanted.
|
|
32
|
+
// A pinned alias does: it asks for a binding of its own, which only the same request answers.
|
|
33
|
+
const answers = options.alias
|
|
34
|
+
? v.alias === options.alias && v.member === options.member
|
|
35
|
+
: memberName(v.member) === memberName(options.member);
|
|
36
|
+
if (answers) {
|
|
37
|
+
return v.bindingName;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (sideEffectOnly) {
|
|
41
|
+
visitor.afterVisit.push(new AddImport(options));
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
// A pinned alias is the whole answer, so the file has no say and nothing below needs to read it.
|
|
45
|
+
if (options.alias) {
|
|
46
|
+
visitor.afterVisit.push(new AddImport(options, options.alias));
|
|
47
|
+
return options.alias;
|
|
48
|
+
}
|
|
49
|
+
const derived = derivedName(options);
|
|
50
|
+
const cu = compilationUnitOf(visitor);
|
|
51
|
+
if (!cu) {
|
|
52
|
+
visitor.afterVisit.push(new AddImport(options, derived));
|
|
53
|
+
return derived;
|
|
54
|
+
}
|
|
55
|
+
const bindings = bindingsInScope(cu, cursorOf(visitor));
|
|
56
|
+
// An import already serving this request answers it; queuing one would, on the next cycle,
|
|
57
|
+
// derive a suffixed name from the binding this call just added. A caller that named a preference
|
|
58
|
+
// takes whatever comes back; one that did not assumes the name it derived, so a binding under
|
|
59
|
+
// any other name would leave the references it emits unbound.
|
|
60
|
+
for (const binding of bindings) {
|
|
61
|
+
if (binding.module === module && binding.member === memberName(options.member) &&
|
|
62
|
+
binding.typeOnly === typeOnly &&
|
|
63
|
+
(options.preferredName !== undefined || anyNameAnswers(options) ||
|
|
64
|
+
binding.name === derived)) {
|
|
65
|
+
return binding.name;
|
|
53
66
|
}
|
|
54
67
|
}
|
|
55
|
-
|
|
68
|
+
const name = deconflict(derived, takenNames(bindings, visitor));
|
|
69
|
+
visitor.afterVisit.push(new AddImport(options, name));
|
|
70
|
+
return name;
|
|
56
71
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
/** Rejects a request on its own terms, whatever the queue already holds. */
|
|
73
|
+
function validate(options) {
|
|
74
|
+
// Validate that a name is provided when member is 'default'
|
|
75
|
+
if (options.member === 'default' && !options.alias && !options.preferredName) {
|
|
76
|
+
throw new Error("When member is 'default', the alias parameter is required");
|
|
77
|
+
}
|
|
78
|
+
// Validate that a name is provided when member is '*' (namespace import)
|
|
79
|
+
if (options.member === '*' && !options.alias && !options.preferredName) {
|
|
80
|
+
throw new Error("When member is '*', the alias parameter is required");
|
|
81
|
+
}
|
|
82
|
+
// Validate that sideEffectOnly is not combined with incompatible options
|
|
83
|
+
if (options.sideEffectOnly) {
|
|
84
|
+
if (options.member !== undefined) {
|
|
85
|
+
throw new Error("Cannot combine sideEffectOnly with member");
|
|
86
|
+
}
|
|
87
|
+
if (options.alias !== undefined) {
|
|
88
|
+
throw new Error("Cannot combine sideEffectOnly with alias");
|
|
89
|
+
}
|
|
90
|
+
if (options.preferredName !== undefined) {
|
|
91
|
+
throw new Error("Cannot combine sideEffectOnly with preferredName");
|
|
92
|
+
}
|
|
93
|
+
if (options.onlyIfReferenced !== undefined) {
|
|
94
|
+
throw new Error("Cannot combine sideEffectOnly with onlyIfReferenced");
|
|
95
|
+
}
|
|
96
|
+
if (options.typeOnly) {
|
|
97
|
+
throw new Error("Cannot combine sideEffectOnly with typeOnly");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A request that named no local name and asks for no member of the module. Having expressed no
|
|
103
|
+
* preference it has none to disappoint, so whatever the file already calls that module answers it.
|
|
104
|
+
*/
|
|
105
|
+
function anyNameAnswers(options) {
|
|
106
|
+
return !options.sideEffectOnly && memberName(options.member) === undefined &&
|
|
107
|
+
options.alias === undefined && options.preferredName === undefined;
|
|
108
|
+
}
|
|
109
|
+
/** The local name a request asks for, before the file has a say in it. */
|
|
110
|
+
function derivedName(options) {
|
|
111
|
+
var _a, _b, _c;
|
|
112
|
+
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);
|
|
113
|
+
}
|
|
114
|
+
/** `'default'` and an absent member both name a default import, which binds no member name of its own. */
|
|
115
|
+
function memberName(member) {
|
|
116
|
+
return member === 'default' ? undefined : member;
|
|
117
|
+
}
|
|
118
|
+
function cursorOf(visitor) {
|
|
119
|
+
return visitor.cursor;
|
|
120
|
+
}
|
|
121
|
+
function compilationUnitOf(visitor) {
|
|
122
|
+
var _a;
|
|
123
|
+
// `cursor` is protected on `TreeVisitor`, and the `maybeAddImport`/`maybeRemoveImport`
|
|
124
|
+
// API is free functions, so reaching it takes a cast.
|
|
125
|
+
return (_a = cursorOf(visitor)) === null || _a === void 0 ? void 0 : _a.firstEnclosing((v) => (v === null || v === void 0 ? void 0 : v.kind) === tree_1.JS.Kind.CompilationUnit);
|
|
126
|
+
}
|
|
127
|
+
/** Every name in scope at `cursor`: what the file binds, and what each block enclosing it declares. */
|
|
128
|
+
function bindingsInScope(cu, cursor) {
|
|
129
|
+
const bindings = statementBindings(cu.statements);
|
|
130
|
+
// A local shadows an import for the code the template lands in, so a name a block declares is
|
|
131
|
+
// taken there even though the module never answers for it.
|
|
132
|
+
for (let c = cursor; c && c.value !== cu; c = c.parent) {
|
|
133
|
+
for (const name of scopeNames(c.value)) {
|
|
134
|
+
bindings.push({ name });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return bindings;
|
|
138
|
+
}
|
|
139
|
+
/** The names a scope introduces directly: a block's declarations, or a function's parameters. */
|
|
140
|
+
function scopeNames(scope) {
|
|
141
|
+
const node = scope;
|
|
142
|
+
if ((node === null || node === void 0 ? void 0 : node.kind) === java_1.J.Kind.Block) {
|
|
143
|
+
return statementBindings(node.statements).map(binding => binding.name);
|
|
144
|
+
}
|
|
145
|
+
if ((node === null || node === void 0 ? void 0 : node.kind) === java_1.J.Kind.MethodDeclaration) {
|
|
146
|
+
return node.parameters.elements
|
|
147
|
+
.flatMap(param => {
|
|
148
|
+
var _a;
|
|
149
|
+
return ((_a = param.element) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.VariableDeclarations
|
|
150
|
+
? param.element.variables.flatMap(v => { var _a; return patternNames((_a = v.element) === null || _a === void 0 ? void 0 : _a.name); })
|
|
151
|
+
: patternNames(param.element);
|
|
152
|
+
})
|
|
153
|
+
.map(bound => bound.name);
|
|
154
|
+
}
|
|
155
|
+
if ((node === null || node === void 0 ? void 0 : node.kind) === java_1.J.Kind.Lambda) {
|
|
156
|
+
return node.parameters.parameters
|
|
157
|
+
.flatMap(param => {
|
|
158
|
+
var _a;
|
|
159
|
+
return ((_a = param.element) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.VariableDeclarations
|
|
160
|
+
? param.element.variables.flatMap(v => { var _a; return patternNames((_a = v.element) === null || _a === void 0 ? void 0 : _a.name); })
|
|
161
|
+
: patternNames(param.element);
|
|
162
|
+
})
|
|
163
|
+
.map(bound => bound.name);
|
|
164
|
+
}
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
/** Imports are top-level statements and so is anything that can shadow one, so a flat scan sees every name. */
|
|
168
|
+
function statementBindings(statements) {
|
|
169
|
+
var _a;
|
|
170
|
+
const bindings = [];
|
|
171
|
+
const declaredBy = (name) => {
|
|
172
|
+
for (const bound of patternNames(name)) {
|
|
173
|
+
bindings.push({ name: bound.name });
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const declaredByVariables = (varDecl) => {
|
|
177
|
+
var _a, _b, _c, _d;
|
|
178
|
+
for (const variable of varDecl.variables) {
|
|
179
|
+
const required = requiredModule((_b = (_a = variable.element) === null || _a === void 0 ? void 0 : _a.initializer) === null || _b === void 0 ? void 0 : _b.element);
|
|
180
|
+
if (required !== undefined) {
|
|
181
|
+
bindings.push(...requireBindings((_c = variable.element) === null || _c === void 0 ? void 0 : _c.name, required));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
declaredBy((_d = variable.element) === null || _d === void 0 ? void 0 : _d.name);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
for (const stmt of statements) {
|
|
189
|
+
const statement = stmt.element;
|
|
190
|
+
switch (statement === null || statement === void 0 ? void 0 : statement.kind) {
|
|
191
|
+
case tree_1.JS.Kind.Import:
|
|
192
|
+
bindings.push(...importBindings(statement));
|
|
193
|
+
break;
|
|
194
|
+
case java_1.J.Kind.VariableDeclarations:
|
|
195
|
+
declaredByVariables(statement);
|
|
196
|
+
break;
|
|
197
|
+
case tree_1.JS.Kind.ScopedVariableDeclarations:
|
|
198
|
+
for (const variable of statement.variables) {
|
|
199
|
+
if (((_a = variable.element) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.VariableDeclarations) {
|
|
200
|
+
declaredByVariables(variable.element);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
case java_1.J.Kind.MethodDeclaration:
|
|
205
|
+
declaredBy(statement.name);
|
|
206
|
+
break;
|
|
207
|
+
case java_1.J.Kind.ClassDeclaration:
|
|
208
|
+
declaredBy(statement.name);
|
|
209
|
+
break;
|
|
210
|
+
case tree_1.JS.Kind.NamespaceDeclaration:
|
|
211
|
+
declaredBy(statement.name.element);
|
|
212
|
+
break;
|
|
213
|
+
case tree_1.JS.Kind.TypeDeclaration:
|
|
214
|
+
declaredBy(statement.name.element);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return bindings;
|
|
219
|
+
}
|
|
220
|
+
/** The module a `require('...')` initializer names, `undefined` for an initializer that is anything else. */
|
|
221
|
+
function requiredModule(initializer) {
|
|
222
|
+
return (initializer === null || initializer === void 0 ? void 0 : initializer.kind) === java_1.J.Kind.MethodInvocation
|
|
223
|
+
? requiredModuleOf(initializer)
|
|
224
|
+
: undefined;
|
|
225
|
+
}
|
|
226
|
+
/** A `require(...)` call, whatever it is passed. `obj.require('x')` selects a method rather than loading a module. */
|
|
227
|
+
function isRequireCall(methodInv) {
|
|
228
|
+
var _a;
|
|
229
|
+
return !methodInv.select && ((_a = methodInv.name) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier &&
|
|
230
|
+
methodInv.name.simpleName === 'require';
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* The module a `require(...)` call loads. `obj.require('x')` selects a method rather than loading a
|
|
234
|
+
* module, and a specifier that is not a string literal names none that can be read here.
|
|
235
|
+
*/
|
|
236
|
+
function requiredModuleOf(methodInv) {
|
|
237
|
+
var _a, _b;
|
|
238
|
+
if (!isRequireCall(methodInv)) {
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
const argument = (_b = (_a = methodInv.arguments) === null || _a === void 0 ? void 0 : _a.elements[0]) === null || _b === void 0 ? void 0 : _b.element;
|
|
242
|
+
return (argument === null || argument === void 0 ? void 0 : argument.kind) === java_1.J.Kind.Literal && typeof argument.value === 'string'
|
|
243
|
+
? moduleNameOf(argument)
|
|
244
|
+
: undefined;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Every name a binding pattern introduces. `member` is the property a name takes its value from,
|
|
248
|
+
* and only a name bound directly by the pattern has one — anything deeper reads a property of a
|
|
249
|
+
* property, so it occupies its name without binding a member of the module.
|
|
250
|
+
*/
|
|
251
|
+
function patternNames(pattern) {
|
|
252
|
+
var _a, _b, _c;
|
|
253
|
+
if ((pattern === null || pattern === void 0 ? void 0 : pattern.kind) === java_1.J.Kind.Identifier) {
|
|
254
|
+
return [{ name: pattern.simpleName }];
|
|
255
|
+
}
|
|
256
|
+
// An array pattern binds by position, so its elements name no member of what they destructure.
|
|
257
|
+
if ((pattern === null || pattern === void 0 ? void 0 : pattern.kind) === tree_1.JS.Kind.ArrayBindingPattern) {
|
|
258
|
+
return pattern.elements.elements
|
|
259
|
+
.flatMap(elem => {
|
|
260
|
+
var _a;
|
|
261
|
+
return ((_a = elem.element) === null || _a === void 0 ? void 0 : _a.kind) === tree_1.JS.Kind.BindingElement
|
|
262
|
+
? patternNames(elem.element.name)
|
|
263
|
+
: patternNames(elem.element);
|
|
264
|
+
})
|
|
265
|
+
.map(bound => ({ name: bound.name }));
|
|
266
|
+
}
|
|
267
|
+
if ((pattern === null || pattern === void 0 ? void 0 : pattern.kind) !== tree_1.JS.Kind.ObjectBindingPattern) {
|
|
268
|
+
return [];
|
|
269
|
+
}
|
|
270
|
+
const names = [];
|
|
271
|
+
for (const elem of pattern.bindings.elements) {
|
|
272
|
+
if (((_a = elem.element) === null || _a === void 0 ? void 0 : _a.kind) !== tree_1.JS.Kind.BindingElement) {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
const bindingElem = elem.element;
|
|
276
|
+
if (((_b = bindingElem.name) === null || _b === void 0 ? void 0 : _b.kind) === java_1.J.Kind.Identifier) {
|
|
277
|
+
const name = bindingElem.name.simpleName;
|
|
278
|
+
const propertyName = (_c = bindingElem.propertyName) === null || _c === void 0 ? void 0 : _c.element;
|
|
279
|
+
names.push({
|
|
280
|
+
name,
|
|
281
|
+
member: (propertyName === null || propertyName === void 0 ? void 0 : propertyName.kind) === java_1.J.Kind.Identifier
|
|
282
|
+
? propertyName.simpleName
|
|
283
|
+
: name
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
names.push(...patternNames(bindingElem.name).map(bound => ({ name: bound.name })));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return names;
|
|
291
|
+
}
|
|
292
|
+
/** A `require` binds a module the way an import does, so the pool records it the same way. */
|
|
293
|
+
function requireBindings(pattern, module) {
|
|
294
|
+
// A whole-module require binds no member, exactly as a default import does.
|
|
295
|
+
if ((pattern === null || pattern === void 0 ? void 0 : pattern.kind) === java_1.J.Kind.Identifier) {
|
|
296
|
+
return [{ name: pattern.simpleName, module, member: undefined, typeOnly: false }];
|
|
297
|
+
}
|
|
298
|
+
return patternNames(pattern).map(bound => bound.member === undefined
|
|
299
|
+
? { name: bound.name }
|
|
300
|
+
: { name: bound.name, module, member: bound.member, typeOnly: false });
|
|
301
|
+
}
|
|
302
|
+
/** The member a specifier imports and the name it binds it under, which are the same absent an alias. */
|
|
303
|
+
function specifierBinding(specifier) {
|
|
304
|
+
var _a, _b, _c, _d, _e;
|
|
305
|
+
if (((_a = specifier.specifier) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier) {
|
|
306
|
+
const name = specifier.specifier.simpleName;
|
|
307
|
+
return { name, member: name };
|
|
308
|
+
}
|
|
309
|
+
if (((_b = specifier.specifier) === null || _b === void 0 ? void 0 : _b.kind) !== tree_1.JS.Kind.Alias) {
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
312
|
+
const alias = specifier.specifier;
|
|
313
|
+
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
|
|
314
|
+
? { name: alias.alias.simpleName, member: alias.propertyName.element.simpleName }
|
|
315
|
+
: undefined;
|
|
316
|
+
}
|
|
317
|
+
function importBindings(jsImport) {
|
|
318
|
+
var _a, _b, _c, _d;
|
|
319
|
+
const moduleSpecifier = (_a = jsImport.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element;
|
|
320
|
+
const module = (moduleSpecifier === null || moduleSpecifier === void 0 ? void 0 : moduleSpecifier.kind) === java_1.J.Kind.Literal
|
|
321
|
+
? moduleNameOf(moduleSpecifier)
|
|
322
|
+
: undefined;
|
|
323
|
+
const importClause = jsImport.importClause;
|
|
324
|
+
if (!importClause) {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
const typeOnly = importClause.typeOnly;
|
|
328
|
+
const bindings = [];
|
|
329
|
+
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) {
|
|
330
|
+
bindings.push({ name: importClause.name.element.simpleName, module, member: undefined, typeOnly });
|
|
331
|
+
}
|
|
332
|
+
const namedBindings = importClause.namedBindings;
|
|
333
|
+
if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === java_1.J.Kind.Identifier) {
|
|
334
|
+
bindings.push({ name: namedBindings.simpleName, module, member: '*', typeOnly });
|
|
335
|
+
}
|
|
336
|
+
else if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_1.JS.Kind.Alias) {
|
|
337
|
+
const alias = namedBindings.alias;
|
|
338
|
+
if ((alias === null || alias === void 0 ? void 0 : alias.kind) === java_1.J.Kind.Identifier) {
|
|
339
|
+
bindings.push({ name: alias.simpleName, module, member: '*', typeOnly });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
else if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_1.JS.Kind.NamedImports) {
|
|
343
|
+
for (const elem of namedBindings.elements.elements) {
|
|
344
|
+
const bound = ((_d = elem.element) === null || _d === void 0 ? void 0 : _d.kind) === tree_1.JS.Kind.ImportSpecifier
|
|
345
|
+
? specifierBinding(elem.element)
|
|
346
|
+
: undefined;
|
|
347
|
+
if (bound) {
|
|
348
|
+
bindings.push(Object.assign(Object.assign({}, bound), { module, typeOnly }));
|
|
73
349
|
}
|
|
74
|
-
|
|
75
|
-
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return bindings;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Names the file binds, plus those pending `AddImport`s on the `afterVisit` queue have claimed. A
|
|
356
|
+
* queued `RemoveImport` does not free one: it removes only what the file leaves unused, and binding
|
|
357
|
+
* a name it keeps is an error, where an unnecessary suffix merely reads oddly.
|
|
358
|
+
*/
|
|
359
|
+
function takenNames(bindings, visitor) {
|
|
360
|
+
const taken = new Set();
|
|
361
|
+
for (const v of visitor.afterVisit || []) {
|
|
362
|
+
if (v instanceof AddImport && v.bindingName) {
|
|
363
|
+
taken.add(v.bindingName);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
for (const binding of bindings) {
|
|
367
|
+
taken.add(binding.name);
|
|
368
|
+
}
|
|
369
|
+
return taken;
|
|
370
|
+
}
|
|
371
|
+
function deconflict(derived, taken) {
|
|
372
|
+
if (!taken.has(derived)) {
|
|
373
|
+
return derived;
|
|
374
|
+
}
|
|
375
|
+
let suffix = 1;
|
|
376
|
+
while (taken.has(`${derived}_${suffix}`)) {
|
|
377
|
+
suffix++;
|
|
378
|
+
}
|
|
379
|
+
return `${derived}_${suffix}`;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* The parser lifts surrogate pairs out of `valueSource` into `unicodeEscapes` and, when it does,
|
|
383
|
+
* sets `value` to the quoted source (`parser.ts` `mapLiteral`), so neither field alone is the
|
|
384
|
+
* module name. Reuniting them and stripping the quotes yields it for either shape of literal.
|
|
385
|
+
*/
|
|
386
|
+
function moduleNameOf(module) {
|
|
387
|
+
var _a;
|
|
388
|
+
if (typeof module === 'string') {
|
|
389
|
+
return module;
|
|
390
|
+
}
|
|
391
|
+
const source = module.valueSource;
|
|
392
|
+
if (source === undefined) {
|
|
393
|
+
return String(module.value);
|
|
394
|
+
}
|
|
395
|
+
let restored = '';
|
|
396
|
+
let cut = 0;
|
|
397
|
+
for (const escape of (_a = module.unicodeEscapes) !== null && _a !== void 0 ? _a : []) {
|
|
398
|
+
restored += source.slice(cut, escape.valueSourceIndex) + String.fromCharCode(parseInt(escape.codePoint, 16));
|
|
399
|
+
cut = escape.valueSourceIndex;
|
|
400
|
+
}
|
|
401
|
+
restored += source.slice(cut);
|
|
402
|
+
const quote = restored.charAt(0);
|
|
403
|
+
return (quote === "'" || quote === '"') && restored.endsWith(quote) && restored.length > 1
|
|
404
|
+
? restored.slice(1, -1)
|
|
405
|
+
: restored;
|
|
406
|
+
}
|
|
407
|
+
function quoteOf(expression) {
|
|
408
|
+
if ((expression === null || expression === void 0 ? void 0 : expression.kind) !== java_1.J.Kind.Literal) {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
const source = expression.valueSource;
|
|
412
|
+
const quote = source === null || source === void 0 ? void 0 : source.charAt(0);
|
|
413
|
+
// JsxText carries raw text as its `valueSource`, so a leading quote alone does not make a
|
|
414
|
+
// literal quote-delimited; requiring a matching pair keeps prose out of the tally.
|
|
415
|
+
return (quote === "'" || quote === '"') && source.length > 1 && source.endsWith(quote)
|
|
416
|
+
? quote
|
|
417
|
+
: undefined;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Pick the quote character for a module specifier being added to `cu`, so that it agrees
|
|
421
|
+
* with the file it lands in. An existing specifier is the most direct precedent; a Prettier
|
|
422
|
+
* config states intent even where the file itself has not been formatted to match.
|
|
423
|
+
*/
|
|
424
|
+
async function detectQuote(cu) {
|
|
425
|
+
var _a, _b;
|
|
426
|
+
// Static imports are top-level, so the highest-ranked signal needs no traversal to find.
|
|
427
|
+
for (const statement of cu.statements) {
|
|
428
|
+
const element = statement.element;
|
|
429
|
+
const topLevelQuote = (element === null || element === void 0 ? void 0 : element.kind) === tree_1.JS.Kind.Import
|
|
430
|
+
? quoteOf((_a = element.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element)
|
|
431
|
+
: (element === null || element === void 0 ? void 0 : element.kind) === tree_1.JS.Kind.ExportDeclaration
|
|
432
|
+
? quoteOf((_b = element.moduleSpecifier) === null || _b === void 0 ? void 0 : _b.element)
|
|
433
|
+
: undefined;
|
|
434
|
+
if (topLevelQuote) {
|
|
435
|
+
return topLevelQuote;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
let importQuote;
|
|
439
|
+
let requireQuote;
|
|
440
|
+
let single = 0;
|
|
441
|
+
let double = 0;
|
|
442
|
+
await new class extends visitor_1.JavaScriptVisitor {
|
|
443
|
+
async visitImportDeclaration(jsImport, p) {
|
|
444
|
+
var _a;
|
|
445
|
+
importQuote !== null && importQuote !== void 0 ? importQuote : (importQuote = quoteOf((_a = jsImport.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element));
|
|
446
|
+
return super.visitImportDeclaration(jsImport, p);
|
|
447
|
+
}
|
|
448
|
+
async visitExportDeclaration(exportDeclaration, p) {
|
|
449
|
+
var _a;
|
|
450
|
+
importQuote !== null && importQuote !== void 0 ? importQuote : (importQuote = quoteOf((_a = exportDeclaration.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element));
|
|
451
|
+
return super.visitExportDeclaration(exportDeclaration, p);
|
|
452
|
+
}
|
|
453
|
+
async visitMethodInvocation(method, p) {
|
|
454
|
+
var _a, _b, _c;
|
|
455
|
+
if (!method.select && ((_a = method.name) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier &&
|
|
456
|
+
method.name.simpleName === 'require') {
|
|
457
|
+
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));
|
|
76
458
|
}
|
|
77
|
-
|
|
78
|
-
|
|
459
|
+
return super.visitMethodInvocation(method, p);
|
|
460
|
+
}
|
|
461
|
+
async visitJsxAttribute(attribute, p) {
|
|
462
|
+
// JSX attribute quoting answers to Prettier's `jsxSingleQuote`, so it is no evidence
|
|
463
|
+
// about the quote style of ordinary strings.
|
|
464
|
+
return attribute;
|
|
465
|
+
}
|
|
466
|
+
async visitLiteral(literal, p) {
|
|
467
|
+
const quote = quoteOf(literal);
|
|
468
|
+
if (quote === "'") {
|
|
469
|
+
single++;
|
|
79
470
|
}
|
|
80
|
-
if (
|
|
81
|
-
|
|
471
|
+
else if (quote === '"') {
|
|
472
|
+
double++;
|
|
82
473
|
}
|
|
474
|
+
return super.visitLiteral(literal, p);
|
|
83
475
|
}
|
|
84
|
-
|
|
476
|
+
}().visit(cu, undefined);
|
|
477
|
+
// Imports outrank requires; that ordering is what makes the import-only scan above sound.
|
|
478
|
+
const specifierQuote = importQuote !== null && importQuote !== void 0 ? importQuote : requireQuote;
|
|
479
|
+
if (specifierQuote) {
|
|
480
|
+
return specifierQuote;
|
|
481
|
+
}
|
|
482
|
+
const prettier = (0, style_1.getStyle)(style_1.StyleKind.PrettierStyle, cu);
|
|
483
|
+
if ((prettier === null || prettier === void 0 ? void 0 : prettier.kind) === style_1.StyleKind.PrettierStyle && !prettier.ignored) {
|
|
484
|
+
const singleQuote = prettier.config.singleQuote;
|
|
485
|
+
if (typeof singleQuote === 'boolean') {
|
|
486
|
+
return singleQuote ? "'" : '"';
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return double > single ? '"' : "'";
|
|
490
|
+
}
|
|
491
|
+
class AddImport extends visitor_1.JavaScriptVisitor {
|
|
492
|
+
constructor(options, bindingName) {
|
|
493
|
+
var _a, _b, _c;
|
|
494
|
+
super();
|
|
495
|
+
validate(options);
|
|
496
|
+
this.module = moduleNameOf(options.module);
|
|
497
|
+
this.moduleValueSource = typeof options.module === 'string' ? undefined : options.module.valueSource;
|
|
498
|
+
this.moduleUnicodeEscapes = typeof options.module === 'string' ? undefined : options.module.unicodeEscapes;
|
|
85
499
|
this.member = options.member;
|
|
86
500
|
this.alias = options.alias;
|
|
87
501
|
this.onlyIfReferenced = (_a = options.onlyIfReferenced) !== null && _a !== void 0 ? _a : true;
|
|
88
502
|
this.sideEffectOnly = (_b = options.sideEffectOnly) !== null && _b !== void 0 ? _b : false;
|
|
89
503
|
this.typeOnly = (_c = options.typeOnly) !== null && _c !== void 0 ? _c : false;
|
|
90
504
|
this.style = options.style;
|
|
505
|
+
this.quoteStyle = options.quoteStyle;
|
|
506
|
+
this.bindingName = this.sideEffectOnly ? undefined : bindingName !== null && bindingName !== void 0 ? bindingName : derivedName(options);
|
|
507
|
+
this.anyNameAnswers = anyNameAnswers(options);
|
|
91
508
|
}
|
|
92
509
|
/**
|
|
93
510
|
* Extract module name from a module specifier literal
|
|
94
511
|
*/
|
|
95
512
|
getModuleName(moduleSpecifier) {
|
|
96
|
-
var _a;
|
|
97
513
|
if (moduleSpecifier.kind !== java_1.J.Kind.Literal) {
|
|
98
514
|
return undefined;
|
|
99
515
|
}
|
|
100
|
-
return (
|
|
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';
|
|
516
|
+
return moduleNameOf(moduleSpecifier);
|
|
109
517
|
}
|
|
110
518
|
/**
|
|
111
519
|
* Determine the appropriate import style based on file type and existing imports
|
|
@@ -177,7 +585,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
177
585
|
const namedVar = varDecl.variables[0].element;
|
|
178
586
|
const initializer = (_a = namedVar === null || namedVar === void 0 ? void 0 : namedVar.initializer) === null || _a === void 0 ? void 0 : _a.element;
|
|
179
587
|
if ((initializer === null || initializer === void 0 ? void 0 : initializer.kind) === java_1.J.Kind.MethodInvocation &&
|
|
180
|
-
|
|
588
|
+
isRequireCall(initializer)) {
|
|
181
589
|
hasCommonJSRequires = true;
|
|
182
590
|
}
|
|
183
591
|
}
|
|
@@ -246,12 +654,8 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
246
654
|
if (varDecl.variables.length === 1) {
|
|
247
655
|
const namedVar = varDecl.variables[0].element;
|
|
248
656
|
const initializer = (_b = namedVar === null || namedVar === void 0 ? void 0 : namedVar.initializer) === null || _b === void 0 ? void 0 : _b.element;
|
|
249
|
-
if ((initializer ===
|
|
250
|
-
|
|
251
|
-
const moduleName = this.getModuleNameFromRequire(initializer);
|
|
252
|
-
if (moduleName === this.module) {
|
|
253
|
-
return ImportStyle.CommonJS;
|
|
254
|
-
}
|
|
657
|
+
if (requiredModule(initializer) === this.module) {
|
|
658
|
+
return ImportStyle.CommonJS;
|
|
255
659
|
}
|
|
256
660
|
}
|
|
257
661
|
}
|
|
@@ -382,11 +786,11 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
382
786
|
const namedImports = importClause.namedBindings;
|
|
383
787
|
const existingElements = namedImports.elements.elements;
|
|
384
788
|
// Find the correct insertion position (alphabetical, case-insensitive)
|
|
385
|
-
const newName =
|
|
789
|
+
const newName = this.bindingName.toLowerCase();
|
|
386
790
|
let insertIndex = existingElements.findIndex(elem => {
|
|
387
|
-
var _a;
|
|
791
|
+
var _a, _b, _c;
|
|
388
792
|
if (((_a = elem.element) === null || _a === void 0 ? void 0 : _a.kind) === tree_1.JS.Kind.ImportSpecifier) {
|
|
389
|
-
const name =
|
|
793
|
+
const name = (_c = (_b = specifierBinding(elem.element)) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : '';
|
|
390
794
|
return newName.localeCompare(name.toLowerCase()) < 0;
|
|
391
795
|
}
|
|
392
796
|
return false;
|
|
@@ -511,135 +915,40 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
511
915
|
* Check if the import matches what we're trying to add
|
|
512
916
|
*/
|
|
513
917
|
isMatchingImport(jsImport) {
|
|
514
|
-
var _a
|
|
515
|
-
// Check module specifier
|
|
918
|
+
var _a;
|
|
516
919
|
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) {
|
|
920
|
+
if (!moduleSpecifier || this.getModuleName(moduleSpecifier) !== this.module) {
|
|
522
921
|
return false;
|
|
523
922
|
}
|
|
923
|
+
// A side-effect import is the clause-less one, and no import carrying bindings stands in
|
|
924
|
+
// for it — nor it for them.
|
|
524
925
|
const importClause = jsImport.importClause;
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
// If we're trying to add a side-effect import and one already exists, it's a match
|
|
528
|
-
return this.sideEffectOnly;
|
|
529
|
-
}
|
|
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
|
-
}
|
|
926
|
+
if (!importClause || this.sideEffectOnly) {
|
|
927
|
+
return !importClause && this.sideEffectOnly;
|
|
591
928
|
}
|
|
592
|
-
return
|
|
929
|
+
return importBindings(jsImport).some(binding => this.answeredBy(binding));
|
|
930
|
+
}
|
|
931
|
+
/** Whether a binding the file already has serves this request. */
|
|
932
|
+
answeredBy(binding) {
|
|
933
|
+
return binding.module === this.module &&
|
|
934
|
+
binding.member === memberName(this.member) &&
|
|
935
|
+
binding.typeOnly === this.typeOnly &&
|
|
936
|
+
(this.anyNameAnswers || binding.name === this.bindingName);
|
|
593
937
|
}
|
|
594
938
|
/**
|
|
595
939
|
* Check if this is a matching CommonJS require statement
|
|
596
940
|
*/
|
|
597
941
|
isMatchingRequire(varDecl) {
|
|
598
|
-
var _a
|
|
942
|
+
var _a;
|
|
599
943
|
if (varDecl.variables.length !== 1) {
|
|
600
944
|
return false;
|
|
601
945
|
}
|
|
602
946
|
const namedVar = varDecl.variables[0].element;
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
}
|
|
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)) {
|
|
947
|
+
const module = requiredModule((_a = namedVar === null || namedVar === void 0 ? void 0 : namedVar.initializer) === null || _a === void 0 ? void 0 : _a.element);
|
|
948
|
+
if (module !== this.module) {
|
|
612
949
|
return false;
|
|
613
950
|
}
|
|
614
|
-
|
|
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;
|
|
951
|
+
return requireBindings(namedVar.name, module).some(binding => this.answeredBy(binding));
|
|
643
952
|
}
|
|
644
953
|
/**
|
|
645
954
|
* Extract the module name from a class type by traversing the owningClass chain
|
|
@@ -754,7 +1063,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
754
1063
|
}
|
|
755
1064
|
}
|
|
756
1065
|
// Step 2: Look for references that match
|
|
757
|
-
const targetName = this.
|
|
1066
|
+
const targetName = this.bindingName;
|
|
758
1067
|
const targetModule = this.module;
|
|
759
1068
|
let found = false;
|
|
760
1069
|
const self = this;
|
|
@@ -872,21 +1181,27 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
872
1181
|
* Create a new import statement
|
|
873
1182
|
*/
|
|
874
1183
|
async createImportStatement(compilationUnit, insertionIndex, p) {
|
|
1184
|
+
var _a;
|
|
875
1185
|
// Determine the appropriate prefix (spacing before the import)
|
|
876
1186
|
const prefix = this.determineImportPrefix(compilationUnit, insertionIndex);
|
|
877
1187
|
// Create the module specifier
|
|
878
1188
|
// For side-effect imports, use emptySpace since space comes from LeftPadded.before
|
|
879
1189
|
// For regular imports with import clause, use emptySpace since space comes from LeftPadded.before
|
|
880
1190
|
// However, the printer expects the space after 'from' in the literal's prefix
|
|
881
|
-
// Note: value
|
|
1191
|
+
// Note: value is the unquoted module name; valueSource and unicodeEscapes are its printed form
|
|
1192
|
+
let valueSource = this.moduleValueSource;
|
|
1193
|
+
if (valueSource === undefined) {
|
|
1194
|
+
const quote = (_a = this.quoteStyle) !== null && _a !== void 0 ? _a : await detectQuote(compilationUnit);
|
|
1195
|
+
valueSource = `${quote}${this.module}${quote}`;
|
|
1196
|
+
}
|
|
882
1197
|
const moduleSpecifier = {
|
|
883
1198
|
id: (0, uuid_1.randomId)(),
|
|
884
1199
|
kind: java_1.J.Kind.Literal,
|
|
885
1200
|
prefix: this.sideEffectOnly ? java_1.emptySpace : java_1.singleSpace,
|
|
886
1201
|
markers: markers_1.emptyMarkers,
|
|
887
1202
|
value: this.module,
|
|
888
|
-
valueSource
|
|
889
|
-
unicodeEscapes:
|
|
1203
|
+
valueSource,
|
|
1204
|
+
unicodeEscapes: this.moduleUnicodeEscapes,
|
|
890
1205
|
type: undefined
|
|
891
1206
|
};
|
|
892
1207
|
let importClause;
|
|
@@ -912,7 +1227,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
912
1227
|
prefix: java_1.singleSpace,
|
|
913
1228
|
markers: markers_1.emptyMarkers,
|
|
914
1229
|
annotations: [],
|
|
915
|
-
simpleName: this.
|
|
1230
|
+
simpleName: this.bindingName,
|
|
916
1231
|
type: undefined,
|
|
917
1232
|
fieldType: undefined
|
|
918
1233
|
};
|
|
@@ -943,7 +1258,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
943
1258
|
prefix: java_1.singleSpace,
|
|
944
1259
|
markers: markers_1.emptyMarkers,
|
|
945
1260
|
annotations: [],
|
|
946
|
-
simpleName: this.
|
|
1261
|
+
simpleName: this.bindingName,
|
|
947
1262
|
type: undefined,
|
|
948
1263
|
fieldType: undefined
|
|
949
1264
|
};
|
|
@@ -1010,7 +1325,8 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1010
1325
|
*/
|
|
1011
1326
|
createImportSpecifier() {
|
|
1012
1327
|
let specifier;
|
|
1013
|
-
|
|
1328
|
+
// An alias equal to the member says nothing `{member}` alone does not.
|
|
1329
|
+
if (this.bindingName !== this.member) {
|
|
1014
1330
|
// Aliased import: import { member as alias } from 'module'
|
|
1015
1331
|
const propertyName = {
|
|
1016
1332
|
id: (0, uuid_1.randomId)(),
|
|
@@ -1028,7 +1344,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1028
1344
|
prefix: java_1.singleSpace,
|
|
1029
1345
|
markers: markers_1.emptyMarkers,
|
|
1030
1346
|
annotations: [],
|
|
1031
|
-
simpleName: this.
|
|
1347
|
+
simpleName: this.bindingName,
|
|
1032
1348
|
type: undefined,
|
|
1033
1349
|
fieldType: undefined
|
|
1034
1350
|
};
|
|
@@ -1113,52 +1429,6 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1113
1429
|
// No imports found, insert at the beginning
|
|
1114
1430
|
return 0;
|
|
1115
1431
|
}
|
|
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;
|
|
1124
|
-
}
|
|
1125
|
-
const firstArg = args[0].element;
|
|
1126
|
-
if (!firstArg || firstArg.kind !== java_1.J.Kind.Literal || typeof firstArg.value !== 'string') {
|
|
1127
|
-
return undefined;
|
|
1128
|
-
}
|
|
1129
|
-
return (_b = firstArg.value) === null || _b === void 0 ? void 0 : _b.toString();
|
|
1130
|
-
}
|
|
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
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
else if ((spec === null || spec === void 0 ? void 0 : spec.kind) === java_1.J.Kind.Identifier) {
|
|
1144
|
-
return spec.simpleName;
|
|
1145
|
-
}
|
|
1146
|
-
return '';
|
|
1147
|
-
}
|
|
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
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
return undefined;
|
|
1161
|
-
}
|
|
1162
1432
|
}
|
|
1163
1433
|
exports.AddImport = AddImport;
|
|
1164
1434
|
//# sourceMappingURL=add-import.js.map
|