@openrewrite/rewrite 8.91.1 → 8.91.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/execution.d.ts +8 -0
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +31 -0
- package/dist/execution.js.map +1 -1
- package/dist/javascript/add-import.d.ts +127 -38
- package/dist/javascript/add-import.d.ts.map +1 -1
- package/dist/javascript/add-import.js +684 -195
- package/dist/javascript/add-import.js.map +1 -1
- package/dist/javascript/amd.d.ts +148 -0
- package/dist/javascript/amd.d.ts.map +1 -0
- package/dist/javascript/amd.js +802 -0
- package/dist/javascript/amd.js.map +1 -0
- package/dist/javascript/binding.d.ts +102 -0
- package/dist/javascript/binding.d.ts.map +1 -0
- package/dist/javascript/binding.js +308 -0
- package/dist/javascript/binding.js.map +1 -0
- package/dist/javascript/comparator.d.ts +2 -0
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js +5 -1
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/format/format.d.ts +5 -2
- package/dist/javascript/format/format.d.ts.map +1 -1
- package/dist/javascript/format/format.js +51 -11
- package/dist/javascript/format/format.js.map +1 -1
- package/dist/javascript/format/minimum-viable-spacing-visitor.d.ts +19 -0
- package/dist/javascript/format/minimum-viable-spacing-visitor.d.ts.map +1 -1
- package/dist/javascript/format/minimum-viable-spacing-visitor.js +180 -7
- package/dist/javascript/format/minimum-viable-spacing-visitor.js.map +1 -1
- package/dist/javascript/format/prettier-format.d.ts +2 -1
- package/dist/javascript/format/prettier-format.d.ts.map +1 -1
- package/dist/javascript/format/prettier-format.js +33 -23
- package/dist/javascript/format/prettier-format.js.map +1 -1
- package/dist/javascript/index.d.ts +8 -1
- package/dist/javascript/index.d.ts.map +1 -1
- package/dist/javascript/index.js +46 -1
- package/dist/javascript/index.js.map +1 -1
- package/dist/javascript/parser-utils.d.ts.map +1 -1
- package/dist/javascript/parser-utils.js +7 -9
- package/dist/javascript/parser-utils.js.map +1 -1
- package/dist/javascript/parser.d.ts +7 -1
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +55 -4
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/print.d.ts +1 -0
- package/dist/javascript/print.d.ts.map +1 -1
- package/dist/javascript/print.js +9 -0
- package/dist/javascript/print.js.map +1 -1
- package/dist/javascript/recipes/change-import.d.ts +4 -3
- package/dist/javascript/recipes/change-import.d.ts.map +1 -1
- package/dist/javascript/recipes/change-import.js +12 -504
- package/dist/javascript/recipes/change-import.js.map +1 -1
- package/dist/javascript/remove-import.d.ts +2 -28
- package/dist/javascript/remove-import.d.ts.map +1 -1
- package/dist/javascript/remove-import.js +19 -57
- package/dist/javascript/remove-import.js.map +1 -1
- package/dist/javascript/rpc.js +2 -0
- package/dist/javascript/rpc.js.map +1 -1
- package/dist/javascript/scope.d.ts +82 -0
- package/dist/javascript/scope.d.ts.map +1 -0
- package/dist/javascript/scope.js +476 -0
- package/dist/javascript/scope.js.map +1 -0
- package/dist/javascript/templating/bindings.d.ts +0 -11
- package/dist/javascript/templating/bindings.d.ts.map +1 -1
- package/dist/javascript/templating/bindings.js +2 -49
- package/dist/javascript/templating/bindings.js.map +1 -1
- package/dist/javascript/templating/comparator.d.ts +12 -0
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +68 -14
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/engine.d.ts +22 -4
- package/dist/javascript/templating/engine.d.ts.map +1 -1
- package/dist/javascript/templating/engine.js +68 -24
- 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 +2 -2
- 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 +10 -5
- package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
- package/dist/javascript/templating/precedence.d.ts +12 -4
- package/dist/javascript/templating/precedence.d.ts.map +1 -1
- package/dist/javascript/templating/precedence.js +18 -11
- package/dist/javascript/templating/precedence.js.map +1 -1
- package/dist/javascript/templating/rewrite.d.ts.map +1 -1
- package/dist/javascript/templating/rewrite.js +7 -1
- package/dist/javascript/templating/rewrite.js.map +1 -1
- package/dist/javascript/templating/template.d.ts +7 -1
- package/dist/javascript/templating/template.d.ts.map +1 -1
- package/dist/javascript/templating/template.js +54 -28
- package/dist/javascript/templating/template.js.map +1 -1
- package/dist/javascript/templating/types.d.ts +15 -18
- package/dist/javascript/templating/types.d.ts.map +1 -1
- package/dist/javascript/templating/utils.d.ts +1 -1
- package/dist/javascript/templating/utils.d.ts.map +1 -1
- package/dist/javascript/templating/utils.js +3 -2
- package/dist/javascript/templating/utils.js.map +1 -1
- package/dist/javascript/tree.d.ts +1 -0
- package/dist/javascript/tree.d.ts.map +1 -1
- package/dist/javascript/tree.js.map +1 -1
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +14 -0
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/javascript/visitor.d.ts.map +1 -1
- package/dist/javascript/visitor.js +1 -0
- package/dist/javascript/visitor.js.map +1 -1
- package/dist/parser.d.ts +6 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +23 -1
- package/dist/parser.js.map +1 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +5 -2
- package/dist/print.js.map +1 -1
- package/dist/rewrite-javascript-version.txt +1 -1
- package/dist/rpc/request/parse-project.d.ts +11 -1
- package/dist/rpc/request/parse-project.d.ts.map +1 -1
- package/dist/rpc/request/parse-project.js +7 -2
- package/dist/rpc/request/parse-project.js.map +1 -1
- package/dist/rpc/request/parse.d.ts +13 -1
- package/dist/rpc/request/parse.d.ts.map +1 -1
- package/dist/rpc/request/parse.js +8 -2
- package/dist/rpc/request/parse.js.map +1 -1
- package/package.json +2 -2
- package/src/execution.ts +23 -0
- package/src/javascript/add-import.ts +753 -238
- package/src/javascript/amd.ts +954 -0
- package/src/javascript/binding.ts +435 -0
- package/src/javascript/comparator.ts +6 -1
- package/src/javascript/format/format.ts +56 -12
- package/src/javascript/format/minimum-viable-spacing-visitor.ts +196 -7
- package/src/javascript/format/prettier-format.ts +35 -25
- package/src/javascript/index.ts +19 -1
- package/src/javascript/parser-utils.ts +7 -11
- package/src/javascript/parser.ts +78 -6
- package/src/javascript/print.ts +10 -0
- package/src/javascript/recipes/change-import.ts +13 -593
- package/src/javascript/remove-import.ts +20 -56
- package/src/javascript/rpc.ts +2 -0
- package/src/javascript/scope.ts +517 -0
- package/src/javascript/templating/bindings.ts +2 -55
- package/src/javascript/templating/comparator.ts +81 -16
- package/src/javascript/templating/engine.ts +107 -38
- package/src/javascript/templating/index.ts +0 -1
- package/src/javascript/templating/pattern.ts +4 -2
- package/src/javascript/templating/placeholder-replacement.ts +16 -6
- package/src/javascript/templating/precedence.ts +21 -13
- package/src/javascript/templating/rewrite.ts +9 -1
- package/src/javascript/templating/template.ts +68 -29
- package/src/javascript/templating/types.ts +17 -18
- package/src/javascript/templating/utils.ts +4 -2
- package/src/javascript/tree.ts +1 -0
- package/src/javascript/type-mapping.ts +15 -0
- package/src/javascript/visitor.ts +1 -0
- package/src/parser.ts +24 -1
- package/src/print.ts +5 -2
- package/src/rpc/request/parse-project.ts +6 -2
- package/src/rpc/request/parse.ts +7 -2
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AddImport = exports.ImportStyle = void 0;
|
|
4
|
-
exports.
|
|
3
|
+
exports.RebindImport = exports.AddImport = exports.ImportStyle = void 0;
|
|
4
|
+
exports.bindImport = bindImport;
|
|
5
|
+
exports.memberName = memberName;
|
|
6
|
+
exports.moduleScopeBindings = moduleScopeBindings;
|
|
7
|
+
exports.hasEsmSyntax = hasEsmSyntax;
|
|
8
|
+
exports.isCommonJs = isCommonJs;
|
|
9
|
+
exports.requiredModuleOfDeclaration = requiredModuleOfDeclaration;
|
|
10
|
+
exports.requiredModuleOf = requiredModuleOf;
|
|
11
|
+
exports.takenNames = takenNames;
|
|
12
|
+
exports.nameTaken = nameTaken;
|
|
13
|
+
exports.moduleNameOf = moduleNameOf;
|
|
14
|
+
exports.existingImportBinding = existingImportBinding;
|
|
5
15
|
const visitor_1 = require("./visitor");
|
|
6
16
|
const java_1 = require("../java");
|
|
7
17
|
const tree_1 = require("./tree");
|
|
8
18
|
const uuid_1 = require("../uuid");
|
|
9
19
|
const markers_1 = require("../markers");
|
|
10
20
|
const style_1 = require("./style");
|
|
21
|
+
const scope_1 = require("./scope");
|
|
22
|
+
const mutative_1 = require("mutative");
|
|
23
|
+
const type_visitor_1 = require("../java/type-visitor");
|
|
24
|
+
const format_1 = require("./format");
|
|
25
|
+
const prettier_format_1 = require("./format/prettier-format");
|
|
11
26
|
var ImportStyle;
|
|
12
27
|
(function (ImportStyle) {
|
|
13
28
|
ImportStyle[ImportStyle["ES6Named"] = 0] = "ES6Named";
|
|
@@ -15,7 +30,13 @@ var ImportStyle;
|
|
|
15
30
|
ImportStyle[ImportStyle["ES6Default"] = 2] = "ES6Default";
|
|
16
31
|
ImportStyle[ImportStyle["CommonJS"] = 3] = "CommonJS"; // const x = require('module')
|
|
17
32
|
})(ImportStyle || (exports.ImportStyle = ImportStyle = {}));
|
|
18
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Ensures an import for `options.module` exists, queuing an `AddImport` edit where none already
|
|
35
|
+
* serves the request. `maybeBind`'s ESM/CommonJS lane is this function; its own JSDoc carries the
|
|
36
|
+
* return-value contract. `refuseCreate` answers `undefined` instead of queuing a new import,
|
|
37
|
+
* without affecting whether an existing binding answers the request first.
|
|
38
|
+
*/
|
|
39
|
+
function bindImport(visitor, options, refuseCreate) {
|
|
19
40
|
var _a, _b;
|
|
20
41
|
validate(options);
|
|
21
42
|
const module = moduleNameOf(options.module);
|
|
@@ -38,34 +59,48 @@ function maybeAddImport(visitor, options) {
|
|
|
38
59
|
}
|
|
39
60
|
}
|
|
40
61
|
if (sideEffectOnly) {
|
|
62
|
+
if (refuseCreate) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
41
65
|
visitor.afterVisit.push(new AddImport(options));
|
|
42
66
|
return undefined;
|
|
43
67
|
}
|
|
44
68
|
// A pinned alias is the whole answer, so the file has no say and nothing below needs to read it.
|
|
45
69
|
if (options.alias) {
|
|
70
|
+
if (refuseCreate) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
46
73
|
visitor.afterVisit.push(new AddImport(options, options.alias));
|
|
47
74
|
return options.alias;
|
|
48
75
|
}
|
|
49
76
|
const derived = derivedName(options);
|
|
50
|
-
const
|
|
77
|
+
const cursor = (0, scope_1.cursorOf)(visitor);
|
|
78
|
+
const cu = cursor && (0, scope_1.compilationUnitOf)(cursor);
|
|
51
79
|
if (!cu) {
|
|
80
|
+
if (refuseCreate) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
52
83
|
visitor.afterVisit.push(new AddImport(options, derived));
|
|
53
84
|
return derived;
|
|
54
85
|
}
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
// derive a suffixed name from the binding this call just added.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
for (const binding of bindings) {
|
|
86
|
+
// An import already serving this request answers it, under whatever name the file gave it —
|
|
87
|
+
// only a pinned alias, returned above, asks for a binding of its own. Queuing a second import
|
|
88
|
+
// would, on the next cycle, derive a suffixed name from the binding this call just added.
|
|
89
|
+
const scope = (0, scope_1.scopeOf)(cursor);
|
|
90
|
+
for (const binding of moduleScopeBindings(cu)) {
|
|
61
91
|
if (binding.module === module && binding.member === memberName(options.member) &&
|
|
62
|
-
binding.typeOnly === typeOnly &&
|
|
63
|
-
(options.preferredName !== undefined || anyNameAnswers(options) ||
|
|
64
|
-
binding.name === derived)) {
|
|
92
|
+
binding.typeOnly === typeOnly && scope.declaringScope(binding.name) === cu) {
|
|
65
93
|
return binding.name;
|
|
66
94
|
}
|
|
67
95
|
}
|
|
68
|
-
|
|
96
|
+
if (refuseCreate) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
// Only the module scope answers for a name, but any scope in the file occupies one. The queue
|
|
100
|
+
// gives every later request for this module the name chosen here, so it has to clear the scopes
|
|
101
|
+
// those references will sit in, which are not known yet.
|
|
102
|
+
const taken = takenNames((0, scope_1.namesDeclaredIn)(cu), visitor);
|
|
103
|
+
const name = (0, scope_1.deconflict)(derived, candidate => taken.has(candidate));
|
|
69
104
|
visitor.afterVisit.push(new AddImport(options, name));
|
|
70
105
|
return name;
|
|
71
106
|
}
|
|
@@ -115,108 +150,86 @@ function derivedName(options) {
|
|
|
115
150
|
function memberName(member) {
|
|
116
151
|
return member === 'default' ? undefined : member;
|
|
117
152
|
}
|
|
118
|
-
|
|
119
|
-
|
|
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;
|
|
153
|
+
/** What the file's imports and `require`s bind at module scope, and the module each name comes from. */
|
|
154
|
+
function moduleScopeBindings(cu) {
|
|
170
155
|
const bindings = [];
|
|
171
|
-
const declaredBy = (name) => {
|
|
172
|
-
for (const bound of patternNames(name)) {
|
|
173
|
-
bindings.push({ name: bound.name });
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
156
|
const declaredByVariables = (varDecl) => {
|
|
177
|
-
var _a, _b, _c
|
|
157
|
+
var _a, _b, _c;
|
|
178
158
|
for (const variable of varDecl.variables) {
|
|
179
159
|
const required = requiredModule((_b = (_a = variable.element) === null || _a === void 0 ? void 0 : _a.initializer) === null || _b === void 0 ? void 0 : _b.element);
|
|
180
160
|
if (required !== undefined) {
|
|
181
161
|
bindings.push(...requireBindings((_c = variable.element) === null || _c === void 0 ? void 0 : _c.name, required));
|
|
182
162
|
}
|
|
183
|
-
else {
|
|
184
|
-
declaredBy((_d = variable.element) === null || _d === void 0 ? void 0 : _d.name);
|
|
185
|
-
}
|
|
186
163
|
}
|
|
187
164
|
};
|
|
188
|
-
for (const stmt of statements) {
|
|
165
|
+
for (const stmt of cu.statements) {
|
|
189
166
|
const statement = stmt.element;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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;
|
|
167
|
+
if ((statement === null || statement === void 0 ? void 0 : statement.kind) === tree_1.JS.Kind.Import) {
|
|
168
|
+
bindings.push(...importBindings(statement));
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
for (const declaration of (0, scope_1.declarationsOf)(statement)) {
|
|
172
|
+
declaredByVariables(declaration);
|
|
216
173
|
}
|
|
217
174
|
}
|
|
218
175
|
return bindings;
|
|
219
176
|
}
|
|
177
|
+
/** Whether a top-level statement already marks the file as a module: an import or any export form. */
|
|
178
|
+
function hasEsmSyntax(cu) {
|
|
179
|
+
return cu.statements.some(stmt => {
|
|
180
|
+
var _a;
|
|
181
|
+
const element = stmt.element;
|
|
182
|
+
// `export {a, b}`, `export * from` and `export default` are their own statement kinds;
|
|
183
|
+
// `export class`/`function`/`const` instead carry `export` as a modifier on the
|
|
184
|
+
// declaration itself, the same way TypeScript's own AST models it.
|
|
185
|
+
const modifiers = element === null || element === void 0 ? void 0 : element.modifiers;
|
|
186
|
+
return (element === null || element === void 0 ? void 0 : element.kind) === tree_1.JS.Kind.Import ||
|
|
187
|
+
(element === null || element === void 0 ? void 0 : element.kind) === tree_1.JS.Kind.ExportDeclaration ||
|
|
188
|
+
(element === null || element === void 0 ? void 0 : element.kind) === tree_1.JS.Kind.ExportAssignment ||
|
|
189
|
+
((_a = modifiers === null || modifiers === void 0 ? void 0 : modifiers.some(m => m.keyword === "export")) !== null && _a !== void 0 ? _a : false);
|
|
190
|
+
}) || hasTopLevelAwait(cu);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Whether a statement holds an `await` outside any function of its own — legal only at module
|
|
194
|
+
* top level, unlike an `await` inside an `async function`, which says nothing about the file.
|
|
195
|
+
*/
|
|
196
|
+
function hasTopLevelAwait(cu) {
|
|
197
|
+
let found = false;
|
|
198
|
+
(0, scope_1.walk)(cu.statements, node => {
|
|
199
|
+
if (found) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
if (node.kind === tree_1.JS.Kind.Await) {
|
|
203
|
+
found = true;
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
return node.kind !== java_1.J.Kind.MethodDeclaration && node.kind !== java_1.J.Kind.Lambda;
|
|
207
|
+
});
|
|
208
|
+
return found;
|
|
209
|
+
}
|
|
210
|
+
/** Whether the file binds its modules with `require`, which decides whether a create is possible. */
|
|
211
|
+
function isCommonJs(cu) {
|
|
212
|
+
if (cu.sourcePath.endsWith(".cjs") || cu.sourcePath.endsWith(".cts")) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
// Node treats these as ES modules regardless of what they contain, the same way
|
|
216
|
+
// `determineImportStyle` reads them as ES6-preferring; `.js`/`.ts`/`.tsx` stay ambiguous and
|
|
217
|
+
// fall through to the statements below.
|
|
218
|
+
if (cu.sourcePath.endsWith(".mjs") || cu.sourcePath.endsWith(".mts")) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
if (hasEsmSyntax(cu)) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
return cu.statements.some(stmt => (0, scope_1.declarationsOf)(stmt.element).some(d => requiredModuleOfDeclaration(d) !== undefined));
|
|
225
|
+
}
|
|
226
|
+
/** The module a `const X = require('m')` declaration names, for the one variable it declares. */
|
|
227
|
+
function requiredModuleOfDeclaration(declaration) {
|
|
228
|
+
var _a, _b;
|
|
229
|
+
return declaration.variables.length === 1
|
|
230
|
+
? requiredModule((_b = (_a = declaration.variables[0].element) === null || _a === void 0 ? void 0 : _a.initializer) === null || _b === void 0 ? void 0 : _b.element)
|
|
231
|
+
: undefined;
|
|
232
|
+
}
|
|
220
233
|
/** The module a `require('...')` initializer names, `undefined` for an initializer that is anything else. */
|
|
221
234
|
function requiredModule(initializer) {
|
|
222
235
|
return (initializer === null || initializer === void 0 ? void 0 : initializer.kind) === java_1.J.Kind.MethodInvocation
|
|
@@ -243,59 +256,13 @@ function requiredModuleOf(methodInv) {
|
|
|
243
256
|
? moduleNameOf(argument)
|
|
244
257
|
: undefined;
|
|
245
258
|
}
|
|
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
259
|
/** A `require` binds a module the way an import does, so the pool records it the same way. */
|
|
293
260
|
function requireBindings(pattern, module) {
|
|
294
261
|
// A whole-module require binds no member, exactly as a default import does.
|
|
295
262
|
if ((pattern === null || pattern === void 0 ? void 0 : pattern.kind) === java_1.J.Kind.Identifier) {
|
|
296
263
|
return [{ name: pattern.simpleName, module, member: undefined, typeOnly: false }];
|
|
297
264
|
}
|
|
298
|
-
return
|
|
265
|
+
return (0, scope_1.bindingNames)(pattern).map(bound => bound.member === undefined
|
|
299
266
|
? { name: bound.name }
|
|
300
267
|
: { name: bound.name, module, member: bound.member, typeOnly: false });
|
|
301
268
|
}
|
|
@@ -352,31 +319,23 @@ function importBindings(jsImport) {
|
|
|
352
319
|
return bindings;
|
|
353
320
|
}
|
|
354
321
|
/**
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
322
|
+
* `names`, plus those pending `AddImport`s on the `afterVisit` queue have claimed. A queued
|
|
323
|
+
* `RemoveImport` does not free one: it removes only what the file leaves unused, and binding a name
|
|
324
|
+
* it keeps is an error, where an unnecessary suffix merely reads oddly.
|
|
358
325
|
*/
|
|
359
|
-
function takenNames(
|
|
360
|
-
const taken = new Set();
|
|
326
|
+
function takenNames(names, visitor) {
|
|
327
|
+
const taken = new Set(names);
|
|
361
328
|
for (const v of visitor.afterVisit || []) {
|
|
362
329
|
if (v instanceof AddImport && v.bindingName) {
|
|
363
330
|
taken.add(v.bindingName);
|
|
364
331
|
}
|
|
365
332
|
}
|
|
366
|
-
for (const binding of bindings) {
|
|
367
|
-
taken.add(binding.name);
|
|
368
|
-
}
|
|
369
333
|
return taken;
|
|
370
334
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
let suffix = 1;
|
|
376
|
-
while (taken.has(`${derived}_${suffix}`)) {
|
|
377
|
-
suffix++;
|
|
378
|
-
}
|
|
379
|
-
return `${derived}_${suffix}`;
|
|
335
|
+
/** As {@link takenNames}, for a caller asking about one name rather than probing repeatedly. */
|
|
336
|
+
function nameTaken(name, names, visitor) {
|
|
337
|
+
return names.has(name) ||
|
|
338
|
+
(visitor.afterVisit || []).some(v => v instanceof AddImport && v.bindingName === name);
|
|
380
339
|
}
|
|
381
340
|
/**
|
|
382
341
|
* The parser lifts surrogate pairs out of `valueSource` into `unicodeEscapes` and, when it does,
|
|
@@ -479,8 +438,8 @@ async function detectQuote(cu) {
|
|
|
479
438
|
if (specifierQuote) {
|
|
480
439
|
return specifierQuote;
|
|
481
440
|
}
|
|
482
|
-
const prettier = (0,
|
|
483
|
-
if (
|
|
441
|
+
const prettier = (0, prettier_format_1.getPrettierStyle)(cu);
|
|
442
|
+
if (prettier && !prettier.ignored) {
|
|
484
443
|
const singleQuote = prettier.config.singleQuote;
|
|
485
444
|
if (typeof singleQuote === 'boolean') {
|
|
486
445
|
return singleQuote ? "'" : '"';
|
|
@@ -488,6 +447,32 @@ async function detectQuote(cu) {
|
|
|
488
447
|
}
|
|
489
448
|
return double > single ? '"' : "'";
|
|
490
449
|
}
|
|
450
|
+
/**
|
|
451
|
+
* Lays out the import statement `AddImport` wrote, so that the file's own style — a Prettier
|
|
452
|
+
* configuration's brace spacing and print width, say — reaches it.
|
|
453
|
+
*/
|
|
454
|
+
async function formatImport(cu, id, p) {
|
|
455
|
+
return await new class extends visitor_1.JavaScriptVisitor {
|
|
456
|
+
async visitImportDeclaration(jsImport, p) {
|
|
457
|
+
return jsImport.id === id ? await (0, format_1.autoFormat)(jsImport, p, undefined, this.cursor.parent) : jsImport;
|
|
458
|
+
}
|
|
459
|
+
}().visit(cu, p);
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* As {@link formatImport}, for an import that gained specifiers. Short of a Prettier configuration
|
|
463
|
+
* deciding the whole line, the statement's own text says more about its spacing than a
|
|
464
|
+
* repository-wide style does, and the merge reads it directly.
|
|
465
|
+
*/
|
|
466
|
+
async function formatMergedImport(cu, id, p) {
|
|
467
|
+
var _a;
|
|
468
|
+
return ((_a = (0, prettier_format_1.getPrettierStyle)(cu)) === null || _a === void 0 ? void 0 : _a.ignored) === false ? formatImport(cu, id, p) : cu;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Adds the import {@link AddImportOptions} describes, under the name derived there. An import
|
|
472
|
+
* already in the file answers where it binds that same name, or where the request expressed no
|
|
473
|
+
* preference at all. Reuse under some other name belongs to {@link bindImport}, whose caller
|
|
474
|
+
* learns which name came back; a recipe driving this visitor is stuck with the one it derived.
|
|
475
|
+
*/
|
|
491
476
|
class AddImport extends visitor_1.JavaScriptVisitor {
|
|
492
477
|
constructor(options, bindingName) {
|
|
493
478
|
var _a, _b, _c;
|
|
@@ -689,17 +674,18 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
689
674
|
return mergedCu;
|
|
690
675
|
}
|
|
691
676
|
}
|
|
692
|
-
//
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
677
|
+
// TODO: create a `require` here. Until then the request goes unserved, since `import` would
|
|
678
|
+
// make a file that binds its modules with `require` an ES module and change how everything
|
|
679
|
+
// in it loads — `maybeBind` refuses it for the same reason. An explicit ES6 `style`
|
|
680
|
+
// overrides, which is how a caller converting the file to ESM asks for one.
|
|
681
|
+
if (importStyle === ImportStyle.CommonJS && isCommonJs(compilationUnit)) {
|
|
682
|
+
return compilationUnit;
|
|
697
683
|
}
|
|
698
684
|
// Add ES6 import (handles ES6Named, ES6Namespace, ES6Default)
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
685
|
+
// Find the position to insert the import
|
|
686
|
+
const insertionIndex = this.findImportInsertionIndex(compilationUnit);
|
|
687
|
+
const newImport = await this.createImportStatement(compilationUnit, insertionIndex, p);
|
|
688
|
+
const withImport = await this.produceJavaScript(compilationUnit, p, async (draft) => {
|
|
703
689
|
// Insert the import at the appropriate position
|
|
704
690
|
// Create semicolon marker for the import statement
|
|
705
691
|
const semicolonMarkers = (0, markers_1.markers)({
|
|
@@ -746,6 +732,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
746
732
|
}
|
|
747
733
|
}
|
|
748
734
|
});
|
|
735
|
+
return formatImport(withImport, newImport.id, p);
|
|
749
736
|
}
|
|
750
737
|
/**
|
|
751
738
|
* Try to merge the new member into an existing import from the same module
|
|
@@ -781,7 +768,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
781
768
|
continue;
|
|
782
769
|
}
|
|
783
770
|
// We found a matching import with named bindings - merge into it
|
|
784
|
-
return this.produceJavaScript(compilationUnit, p, async (draft) => {
|
|
771
|
+
return formatMergedImport(await this.produceJavaScript(compilationUnit, p, async (draft) => {
|
|
785
772
|
var _a, _b, _c;
|
|
786
773
|
const namedImports = importClause.namedBindings;
|
|
787
774
|
const existingElements = namedImports.elements.elements;
|
|
@@ -841,12 +828,12 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
841
828
|
});
|
|
842
829
|
// Replace the statement in the compilation unit
|
|
843
830
|
draft.statements = compilationUnit.statements.map((s, idx) => idx === i ? Object.assign(Object.assign({}, s), { element: updatedImport }) : s);
|
|
844
|
-
});
|
|
831
|
+
}), jsImport.id, p);
|
|
845
832
|
}
|
|
846
833
|
// Case 2: Default import without named bindings - add named bindings
|
|
847
834
|
// Transform: import React from 'react' -> import React, { useState } from 'react'
|
|
848
835
|
if (importClause.name && !importClause.namedBindings) {
|
|
849
|
-
return this.produceJavaScript(compilationUnit, p, async (draft) => {
|
|
836
|
+
return formatMergedImport(await this.produceJavaScript(compilationUnit, p, async (draft) => {
|
|
850
837
|
const newSpecifier = this.createImportSpecifier();
|
|
851
838
|
// Get the spaces style for brace spacing
|
|
852
839
|
const spacesStyle = (0, style_1.getStyle)(style_1.StyleKind.SpacesStyle, compilationUnit);
|
|
@@ -882,7 +869,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
882
869
|
});
|
|
883
870
|
// Replace the statement in the compilation unit
|
|
884
871
|
draft.statements = compilationUnit.statements.map((s, idx) => idx === i ? Object.assign(Object.assign({}, s), { element: updatedImport }) : s);
|
|
885
|
-
});
|
|
872
|
+
}), jsImport.id, p);
|
|
886
873
|
}
|
|
887
874
|
}
|
|
888
875
|
}
|
|
@@ -1184,10 +1171,8 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1184
1171
|
var _a;
|
|
1185
1172
|
// Determine the appropriate prefix (spacing before the import)
|
|
1186
1173
|
const prefix = this.determineImportPrefix(compilationUnit, insertionIndex);
|
|
1187
|
-
//
|
|
1188
|
-
//
|
|
1189
|
-
// For regular imports with import clause, use emptySpace since space comes from LeftPadded.before
|
|
1190
|
-
// However, the printer expects the space after 'from' in the literal's prefix
|
|
1174
|
+
// Whitespace throughout follows the parser's placement, so that formatting the result is a
|
|
1175
|
+
// no-op where the file's style already agrees with it.
|
|
1191
1176
|
// Note: value is the unquoted module name; valueSource and unicodeEscapes are its printed form
|
|
1192
1177
|
let valueSource = this.moduleValueSource;
|
|
1193
1178
|
if (valueSource === undefined) {
|
|
@@ -1197,7 +1182,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1197
1182
|
const moduleSpecifier = {
|
|
1198
1183
|
id: (0, uuid_1.randomId)(),
|
|
1199
1184
|
kind: java_1.J.Kind.Literal,
|
|
1200
|
-
prefix:
|
|
1185
|
+
prefix: java_1.singleSpace,
|
|
1201
1186
|
markers: markers_1.emptyMarkers,
|
|
1202
1187
|
value: this.module,
|
|
1203
1188
|
valueSource,
|
|
@@ -1234,7 +1219,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1234
1219
|
const namespaceBinding = {
|
|
1235
1220
|
id: (0, uuid_1.randomId)(),
|
|
1236
1221
|
kind: tree_1.JS.Kind.Alias,
|
|
1237
|
-
prefix: java_1.singleSpace,
|
|
1222
|
+
prefix: this.typeOnly ? java_1.singleSpace : java_1.emptySpace,
|
|
1238
1223
|
markers: markers_1.emptyMarkers,
|
|
1239
1224
|
propertyName: (0, java_1.rightPadded)(propertyName, java_1.singleSpace),
|
|
1240
1225
|
alias: aliasIdentifier
|
|
@@ -1242,7 +1227,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1242
1227
|
importClause = {
|
|
1243
1228
|
id: (0, uuid_1.randomId)(),
|
|
1244
1229
|
kind: tree_1.JS.Kind.ImportClause,
|
|
1245
|
-
prefix:
|
|
1230
|
+
prefix: java_1.singleSpace,
|
|
1246
1231
|
markers: markers_1.emptyMarkers,
|
|
1247
1232
|
typeOnly: this.typeOnly,
|
|
1248
1233
|
name: undefined,
|
|
@@ -1255,7 +1240,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1255
1240
|
const defaultName = {
|
|
1256
1241
|
id: (0, uuid_1.randomId)(),
|
|
1257
1242
|
kind: java_1.J.Kind.Identifier,
|
|
1258
|
-
prefix: java_1.singleSpace,
|
|
1243
|
+
prefix: this.typeOnly ? java_1.singleSpace : java_1.emptySpace,
|
|
1259
1244
|
markers: markers_1.emptyMarkers,
|
|
1260
1245
|
annotations: [],
|
|
1261
1246
|
simpleName: this.bindingName,
|
|
@@ -1265,10 +1250,10 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1265
1250
|
importClause = {
|
|
1266
1251
|
id: (0, uuid_1.randomId)(),
|
|
1267
1252
|
kind: tree_1.JS.Kind.ImportClause,
|
|
1268
|
-
prefix:
|
|
1253
|
+
prefix: java_1.singleSpace,
|
|
1269
1254
|
markers: markers_1.emptyMarkers,
|
|
1270
1255
|
typeOnly: this.typeOnly,
|
|
1271
|
-
name: (0, java_1.rightPadded)(defaultName, java_1.
|
|
1256
|
+
name: (0, java_1.rightPadded)(defaultName, java_1.singleSpace),
|
|
1272
1257
|
namedBindings: undefined
|
|
1273
1258
|
};
|
|
1274
1259
|
}
|
|
@@ -1284,11 +1269,11 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1284
1269
|
const namedImports = {
|
|
1285
1270
|
id: (0, uuid_1.randomId)(),
|
|
1286
1271
|
kind: tree_1.JS.Kind.NamedImports,
|
|
1287
|
-
prefix: java_1.
|
|
1272
|
+
prefix: java_1.emptySpace,
|
|
1288
1273
|
markers: markers_1.emptyMarkers,
|
|
1289
1274
|
elements: {
|
|
1290
1275
|
kind: java_1.J.Kind.Container,
|
|
1291
|
-
before: java_1.emptySpace,
|
|
1276
|
+
before: this.typeOnly ? java_1.singleSpace : java_1.emptySpace,
|
|
1292
1277
|
elements: [(0, java_1.rightPadded)(importSpecWithSpacing, braceSpace)],
|
|
1293
1278
|
markers: markers_1.emptyMarkers
|
|
1294
1279
|
}
|
|
@@ -1296,7 +1281,7 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1296
1281
|
importClause = {
|
|
1297
1282
|
id: (0, uuid_1.randomId)(),
|
|
1298
1283
|
kind: tree_1.JS.Kind.ImportClause,
|
|
1299
|
-
prefix:
|
|
1284
|
+
prefix: java_1.singleSpace,
|
|
1300
1285
|
markers: markers_1.emptyMarkers,
|
|
1301
1286
|
typeOnly: this.typeOnly,
|
|
1302
1287
|
name: undefined,
|
|
@@ -1312,7 +1297,9 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1312
1297
|
importClause,
|
|
1313
1298
|
moduleSpecifier: {
|
|
1314
1299
|
kind: java_1.J.Kind.LeftPadded,
|
|
1315
|
-
before
|
|
1300
|
+
// Bindings end at `}`, so the space before `from` sits here; a default import's name
|
|
1301
|
+
// and a side-effect import's literal carry their own
|
|
1302
|
+
before: (importClause === null || importClause === void 0 ? void 0 : importClause.namedBindings) ? java_1.singleSpace : java_1.emptySpace,
|
|
1316
1303
|
element: moduleSpecifier,
|
|
1317
1304
|
markers: markers_1.emptyMarkers
|
|
1318
1305
|
},
|
|
@@ -1431,4 +1418,506 @@ class AddImport extends visitor_1.JavaScriptVisitor {
|
|
|
1431
1418
|
}
|
|
1432
1419
|
}
|
|
1433
1420
|
exports.AddImport = AddImport;
|
|
1421
|
+
/**
|
|
1422
|
+
* The local name `jsImport` binds `member` of `module` to, or `undefined` where it binds
|
|
1423
|
+
* something else. `'default'` and an absent `member` both mean the default import, matching
|
|
1424
|
+
* {@link memberName}.
|
|
1425
|
+
*/
|
|
1426
|
+
function importBinds(jsImport, module, member) {
|
|
1427
|
+
var _a, _b;
|
|
1428
|
+
const specifier = (_a = jsImport.moduleSpecifier) === null || _a === void 0 ? void 0 : _a.element;
|
|
1429
|
+
if ((specifier === null || specifier === void 0 ? void 0 : specifier.kind) !== java_1.J.Kind.Literal || specifier.value !== module) {
|
|
1430
|
+
return undefined;
|
|
1431
|
+
}
|
|
1432
|
+
const importClause = jsImport.importClause;
|
|
1433
|
+
if (!importClause) {
|
|
1434
|
+
return undefined;
|
|
1435
|
+
}
|
|
1436
|
+
const key = memberName(member);
|
|
1437
|
+
if (key === undefined) {
|
|
1438
|
+
const nameElem = (_b = importClause.name) === null || _b === void 0 ? void 0 : _b.element;
|
|
1439
|
+
return nameElem && (0, java_1.isIdentifier)(nameElem) ? nameElem.simpleName : undefined;
|
|
1440
|
+
}
|
|
1441
|
+
if (key === '*') {
|
|
1442
|
+
const namedBindings = importClause.namedBindings;
|
|
1443
|
+
return (namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_1.JS.Kind.Alias && (0, java_1.isIdentifier)(namedBindings.alias)
|
|
1444
|
+
? namedBindings.alias.simpleName
|
|
1445
|
+
: undefined;
|
|
1446
|
+
}
|
|
1447
|
+
const namedBindings = importClause.namedBindings;
|
|
1448
|
+
if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) !== tree_1.JS.Kind.NamedImports) {
|
|
1449
|
+
return undefined;
|
|
1450
|
+
}
|
|
1451
|
+
for (const elem of namedBindings.elements.elements) {
|
|
1452
|
+
const specifierNode = elem.element.specifier;
|
|
1453
|
+
if (!namedSpecifierImports(specifierNode, key)) {
|
|
1454
|
+
continue;
|
|
1455
|
+
}
|
|
1456
|
+
if ((0, java_1.isIdentifier)(specifierNode)) {
|
|
1457
|
+
return specifierNode.simpleName;
|
|
1458
|
+
}
|
|
1459
|
+
const alias = specifierNode.alias;
|
|
1460
|
+
if ((0, java_1.isIdentifier)(alias)) {
|
|
1461
|
+
return alias.simpleName;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
return undefined;
|
|
1465
|
+
}
|
|
1466
|
+
/** Whether `specifier` imports the member `key`, under whatever local name it binds it to. */
|
|
1467
|
+
function namedSpecifierImports(specifier, key) {
|
|
1468
|
+
if ((0, java_1.isIdentifier)(specifier)) {
|
|
1469
|
+
return specifier.simpleName === key;
|
|
1470
|
+
}
|
|
1471
|
+
if (specifier.kind === tree_1.JS.Kind.Alias) {
|
|
1472
|
+
const propertyName = specifier.propertyName.element;
|
|
1473
|
+
return (0, java_1.isIdentifier)(propertyName) && propertyName.simpleName === key;
|
|
1474
|
+
}
|
|
1475
|
+
return false;
|
|
1476
|
+
}
|
|
1477
|
+
/** Whether the named specifier binding `key` carries its own inline `type`, as in `{type a, b}`. */
|
|
1478
|
+
function namedSpecifierIsTypeOnly(imp, key) {
|
|
1479
|
+
var _a;
|
|
1480
|
+
const namedBindings = (_a = imp.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings;
|
|
1481
|
+
if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) !== tree_1.JS.Kind.NamedImports) {
|
|
1482
|
+
return false;
|
|
1483
|
+
}
|
|
1484
|
+
for (const elem of namedBindings.elements.elements) {
|
|
1485
|
+
if (namedSpecifierImports(elem.element.specifier, key)) {
|
|
1486
|
+
return elem.element.importType.element;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
return false;
|
|
1490
|
+
}
|
|
1491
|
+
/** How many named specifiers `jsImport` carries, `0` for a default, namespace or side-effect import. */
|
|
1492
|
+
function namedImportCount(jsImport) {
|
|
1493
|
+
var _a;
|
|
1494
|
+
const namedBindings = (_a = jsImport.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings;
|
|
1495
|
+
return (namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_1.JS.Kind.NamedImports ? namedBindings.elements.elements.length : 0;
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* Whether `jsImport`'s clause binds exactly one thing — a default, a namespace, or one named
|
|
1499
|
+
* specifier. That is the only shape a module-only move can rewrite in place: whichever binding
|
|
1500
|
+
* `importBinds` already matched is this one, so nothing else the clause carries goes along with it.
|
|
1501
|
+
*/
|
|
1502
|
+
function isOnlyMember(jsImport) {
|
|
1503
|
+
var _a;
|
|
1504
|
+
const importClause = jsImport.importClause;
|
|
1505
|
+
if (!importClause) {
|
|
1506
|
+
return false;
|
|
1507
|
+
}
|
|
1508
|
+
const hasDefault = importClause.name !== undefined;
|
|
1509
|
+
const hasNamespace = ((_a = importClause.namedBindings) === null || _a === void 0 ? void 0 : _a.kind) === tree_1.JS.Kind.Alias;
|
|
1510
|
+
return (hasDefault ? 1 : 0) + (hasNamespace ? 1 : 0) + namedImportCount(jsImport) === 1;
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* The existing binding for `member` of `module`, read from `cu`'s own import statements — what
|
|
1514
|
+
* `maybeRebind` reads before committing to a `RebindImport` edit.
|
|
1515
|
+
*/
|
|
1516
|
+
function existingImportBinding(cu, module, member) {
|
|
1517
|
+
for (const stmt of cu.statements) {
|
|
1518
|
+
const element = stmt.element;
|
|
1519
|
+
if ((element === null || element === void 0 ? void 0 : element.kind) !== tree_1.JS.Kind.Import) {
|
|
1520
|
+
continue;
|
|
1521
|
+
}
|
|
1522
|
+
const localName = importBinds(element, module, member);
|
|
1523
|
+
if (localName !== undefined) {
|
|
1524
|
+
return {
|
|
1525
|
+
localName,
|
|
1526
|
+
onlyMemberOfStatement: isOnlyMember(element),
|
|
1527
|
+
aliased: localName !== memberName(member)
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
return undefined;
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* The `member as local` specifier, for an import binding `member` under `local` or an export
|
|
1535
|
+
* publishing `local`'s binding under that name. `local` itself becomes the alias, keeping the type
|
|
1536
|
+
* attribution it holds, and the alias takes its prefix: that whitespace separates the specifier
|
|
1537
|
+
* from a `type` keyword before it.
|
|
1538
|
+
*/
|
|
1539
|
+
function aliasing(local, member) {
|
|
1540
|
+
const propertyName = {
|
|
1541
|
+
id: (0, uuid_1.randomId)(),
|
|
1542
|
+
kind: java_1.J.Kind.Identifier,
|
|
1543
|
+
prefix: java_1.emptySpace,
|
|
1544
|
+
markers: markers_1.emptyMarkers,
|
|
1545
|
+
annotations: [],
|
|
1546
|
+
simpleName: member,
|
|
1547
|
+
type: undefined,
|
|
1548
|
+
fieldType: undefined
|
|
1549
|
+
};
|
|
1550
|
+
return {
|
|
1551
|
+
id: (0, uuid_1.randomId)(),
|
|
1552
|
+
kind: tree_1.JS.Kind.Alias,
|
|
1553
|
+
prefix: local.prefix,
|
|
1554
|
+
markers: markers_1.emptyMarkers,
|
|
1555
|
+
propertyName: (0, java_1.rightPadded)(propertyName, java_1.singleSpace),
|
|
1556
|
+
alias: Object.assign(Object.assign({}, local), { prefix: java_1.singleSpace })
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Drops the binding for `member` from `jsImport`'s clause — the default, the namespace alias, or
|
|
1561
|
+
* one entry of the named list, whichever `member` names — keeping everything else the clause
|
|
1562
|
+
* binds. `ElementRemovalFormatter` carries the dropped binding's prefix onto whatever prints
|
|
1563
|
+
* next, the same way `RemoveImport` keeps formatting sane when trimming a list.
|
|
1564
|
+
*/
|
|
1565
|
+
function removeBinding(jsImport, member) {
|
|
1566
|
+
var _a;
|
|
1567
|
+
const importClause = jsImport.importClause;
|
|
1568
|
+
if (!importClause) {
|
|
1569
|
+
return jsImport;
|
|
1570
|
+
}
|
|
1571
|
+
const key = memberName(member);
|
|
1572
|
+
if (key === undefined) {
|
|
1573
|
+
if (!importClause.name) {
|
|
1574
|
+
return jsImport;
|
|
1575
|
+
}
|
|
1576
|
+
const namedBindings = importClause.namedBindings;
|
|
1577
|
+
if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_1.JS.Kind.NamedImports) {
|
|
1578
|
+
// `NamedImports` keeps the space before its own `{` on the container's `before`,
|
|
1579
|
+
// not on its own prefix, so the removed default's prefix has to land there instead.
|
|
1580
|
+
const namedImports = namedBindings;
|
|
1581
|
+
const updated = Object.assign(Object.assign({}, namedImports), { elements: Object.assign(Object.assign({}, namedImports.elements), { before: importClause.name.element.prefix }) });
|
|
1582
|
+
return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { name: undefined, namedBindings: updated }) });
|
|
1583
|
+
}
|
|
1584
|
+
if (namedBindings) {
|
|
1585
|
+
const formatter = new java_1.ElementRemovalFormatter();
|
|
1586
|
+
formatter.markRemoved(importClause.name.element);
|
|
1587
|
+
return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { name: undefined, namedBindings: formatter.processKept(namedBindings) }) });
|
|
1588
|
+
}
|
|
1589
|
+
return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { name: undefined }) });
|
|
1590
|
+
}
|
|
1591
|
+
if (key === '*') {
|
|
1592
|
+
return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { namedBindings: undefined }) });
|
|
1593
|
+
}
|
|
1594
|
+
if (((_a = importClause.namedBindings) === null || _a === void 0 ? void 0 : _a.kind) !== tree_1.JS.Kind.NamedImports) {
|
|
1595
|
+
return jsImport;
|
|
1596
|
+
}
|
|
1597
|
+
const namedImports = importClause.namedBindings;
|
|
1598
|
+
const formatter = new java_1.ElementRemovalFormatter();
|
|
1599
|
+
const kept = [];
|
|
1600
|
+
for (const entry of namedImports.elements.elements) {
|
|
1601
|
+
if (namedSpecifierImports(entry.element.specifier, key)) {
|
|
1602
|
+
formatter.markRemoved(entry.element);
|
|
1603
|
+
}
|
|
1604
|
+
else {
|
|
1605
|
+
kept.push(Object.assign(Object.assign({}, entry), { element: formatter.processKept(entry.element) }));
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
if (kept.length === 0) {
|
|
1609
|
+
// An emptied brace list still prints, as `import D, {} from "m"`, so it goes with its
|
|
1610
|
+
// last member; the caller drops the whole statement when no default remains either.
|
|
1611
|
+
return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { namedBindings: undefined }) });
|
|
1612
|
+
}
|
|
1613
|
+
const updatedNamedImports = Object.assign(Object.assign({}, namedImports), { elements: Object.assign(Object.assign({}, namedImports.elements), { elements: kept }) });
|
|
1614
|
+
return Object.assign(Object.assign({}, jsImport), { importClause: Object.assign(Object.assign({}, importClause), { namedBindings: updatedNamedImports }) });
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Moves the binding `from` names to `to`, binding it under `boundName`. In place when the
|
|
1618
|
+
* statement that carries it binds nothing else — module and member specifier rewritten there
|
|
1619
|
+
* directly; otherwise the old specifier drops and {@link bindImport} queues the replacement.
|
|
1620
|
+
* A `boundName` of its own renames the binding, and the file's references to it follow.
|
|
1621
|
+
*
|
|
1622
|
+
* Not built on `RemoveImport`/`maybeUnbind`: those only drop a binding once nothing references
|
|
1623
|
+
* it, but a rebind moves one that is still in use — removal here has to be unconditional.
|
|
1624
|
+
*/
|
|
1625
|
+
class RebindImport extends visitor_1.JavaScriptVisitor {
|
|
1626
|
+
constructor(from, to, localName, boundName) {
|
|
1627
|
+
super();
|
|
1628
|
+
this.from = from;
|
|
1629
|
+
this.to = to;
|
|
1630
|
+
this.localName = localName;
|
|
1631
|
+
this.boundName = boundName;
|
|
1632
|
+
this.transformedInPlace = false;
|
|
1633
|
+
this.typeOnly = false;
|
|
1634
|
+
this.movedTypes = new MovedTypes(this.from, this.to);
|
|
1635
|
+
/** Identifiers settled as references to the binding, so a parent need not settle it again. */
|
|
1636
|
+
this.references = new Set();
|
|
1637
|
+
}
|
|
1638
|
+
get renaming() {
|
|
1639
|
+
return this.boundName !== this.localName;
|
|
1640
|
+
}
|
|
1641
|
+
async visitJsCompilationUnit(cu, p) {
|
|
1642
|
+
const visited = await super.visitJsCompilationUnit(cu, p);
|
|
1643
|
+
if (!this.transformedInPlace) {
|
|
1644
|
+
bindImport(this, {
|
|
1645
|
+
module: this.to.module,
|
|
1646
|
+
member: this.to.member,
|
|
1647
|
+
alias: this.boundName,
|
|
1648
|
+
typeOnly: this.typeOnly,
|
|
1649
|
+
onlyIfReferenced: false
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
return visited;
|
|
1653
|
+
}
|
|
1654
|
+
async visitImportDeclaration(jsImport, p) {
|
|
1655
|
+
var _a, _b;
|
|
1656
|
+
const imp = await super.visitImportDeclaration(jsImport, p);
|
|
1657
|
+
const key = memberName(this.from.member);
|
|
1658
|
+
// One call moves one binding, and no two imports bind the same local name, so the name
|
|
1659
|
+
// read from the matched statement is what picks it back out of the file.
|
|
1660
|
+
if (importBinds(imp, this.from.module, this.from.member) !== this.localName) {
|
|
1661
|
+
return imp;
|
|
1662
|
+
}
|
|
1663
|
+
// A moved named specifier's own inline `type` marks it type-only even where the clause
|
|
1664
|
+
// it's leaving is not — the replacement needs the same answer to stay type-safe.
|
|
1665
|
+
this.typeOnly = ((_b = (_a = imp.importClause) === null || _a === void 0 ? void 0 : _a.typeOnly) !== null && _b !== void 0 ? _b : false) ||
|
|
1666
|
+
(key !== undefined && key !== '*' && namedSpecifierIsTypeOnly(imp, key));
|
|
1667
|
+
if (!isOnlyMember(imp)) {
|
|
1668
|
+
return removeBinding(imp, this.from.member);
|
|
1669
|
+
}
|
|
1670
|
+
this.transformedInPlace = true;
|
|
1671
|
+
return (0, mutative_1.create)(imp, draft => {
|
|
1672
|
+
var _a;
|
|
1673
|
+
const literal = draft.moduleSpecifier.element;
|
|
1674
|
+
literal.value = this.to.module;
|
|
1675
|
+
const originalSource = literal.valueSource || `"${this.from.module}"`;
|
|
1676
|
+
const quoteChar = originalSource.startsWith("'") ? "'" : '"';
|
|
1677
|
+
literal.valueSource = `${quoteChar}${this.to.module}${quoteChar}`;
|
|
1678
|
+
// A default or namespace import carries its local name on the clause itself; a named
|
|
1679
|
+
// one states the member alongside it, in the specifier.
|
|
1680
|
+
if (key === undefined || key === '*') {
|
|
1681
|
+
if (this.boundName !== this.localName) {
|
|
1682
|
+
renameClauseBinding(draft.importClause, key, this.boundName);
|
|
1683
|
+
}
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
rewriteNamedSpecifier(draft.importClause, key, (_a = memberName(this.to.member)) !== null && _a !== void 0 ? _a : key, this.boundName);
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
async visitIdentifier(identifier, p) {
|
|
1690
|
+
if (!this.referencesBinding(identifier)) {
|
|
1691
|
+
return super.visitIdentifier(identifier, p);
|
|
1692
|
+
}
|
|
1693
|
+
this.references.add(identifier.id);
|
|
1694
|
+
// The name follows the binding, and so does what the name is attributed to; an aliased
|
|
1695
|
+
// binding keeps its name and still resolves somewhere new.
|
|
1696
|
+
const type = await this.movedTypes.visit(identifier.type, undefined);
|
|
1697
|
+
const fieldType = await this.movedTypes.visit(identifier.fieldType, undefined);
|
|
1698
|
+
return !this.renaming && type === identifier.type && fieldType === identifier.fieldType
|
|
1699
|
+
? identifier
|
|
1700
|
+
: Object.assign(Object.assign({}, identifier), { simpleName: this.boundName, type, fieldType });
|
|
1701
|
+
}
|
|
1702
|
+
async visitMethodInvocation(method, p) {
|
|
1703
|
+
const m = await super.visitMethodInvocation(method, p);
|
|
1704
|
+
if (!this.referencesMovedBinding(m.name)) {
|
|
1705
|
+
return m;
|
|
1706
|
+
}
|
|
1707
|
+
const methodType = await this.movedTypes.visit(m.methodType, undefined);
|
|
1708
|
+
return methodType === m.methodType ? m : Object.assign(Object.assign({}, m), { methodType });
|
|
1709
|
+
}
|
|
1710
|
+
async visitNewClass(newClass, p) {
|
|
1711
|
+
const nc = await super.visitNewClass(newClass, p);
|
|
1712
|
+
if (!this.referencesMovedBinding(nc.class)) {
|
|
1713
|
+
return nc;
|
|
1714
|
+
}
|
|
1715
|
+
const type = await this.movedTypes.visit(nc.type, undefined);
|
|
1716
|
+
const methodType = await this.movedTypes.visit(nc.methodType, undefined);
|
|
1717
|
+
const constructorType = await this.movedTypes.visit(nc.constructorType, undefined);
|
|
1718
|
+
return type === nc.type && methodType === nc.methodType && constructorType === nc.constructorType
|
|
1719
|
+
? nc
|
|
1720
|
+
: Object.assign(Object.assign({}, nc), { type, methodType, constructorType });
|
|
1721
|
+
}
|
|
1722
|
+
async visitFunctionCall(functionCall, p) {
|
|
1723
|
+
var _a;
|
|
1724
|
+
const fc = await super.visitFunctionCall(functionCall, p);
|
|
1725
|
+
if (!this.referencesMovedBinding((_a = fc.function) === null || _a === void 0 ? void 0 : _a.element)) {
|
|
1726
|
+
return fc;
|
|
1727
|
+
}
|
|
1728
|
+
const methodType = await this.movedTypes.visit(fc.methodType, undefined);
|
|
1729
|
+
return methodType === fc.methodType ? fc : Object.assign(Object.assign({}, fc), { methodType });
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1732
|
+
* Whether `callee` is the identifier `visitIdentifier` settled as a reference to the binding.
|
|
1733
|
+
* A rewrite keeps the identifier's id, so the one it settled is the one still standing here.
|
|
1734
|
+
*/
|
|
1735
|
+
referencesMovedBinding(callee) {
|
|
1736
|
+
return (callee === null || callee === void 0 ? void 0 : callee.kind) === java_1.J.Kind.Identifier && this.references.has(callee.id);
|
|
1737
|
+
}
|
|
1738
|
+
/**
|
|
1739
|
+
* A decorator and the other type-name positions reach the tree through this hook, which the
|
|
1740
|
+
* base visitor holds closed; a reference in one of them is a reference like any other.
|
|
1741
|
+
*/
|
|
1742
|
+
async visitTypeName(nameTree, p) {
|
|
1743
|
+
return await this.visit(nameTree, p);
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* An export specifier's own name is the module's public surface, which its consumers read and
|
|
1747
|
+
* the rename leaves alone. See CLAUDE.md: Which name a rebind binds.
|
|
1748
|
+
*/
|
|
1749
|
+
async visitExportSpecifier(exportSpecifier, p) {
|
|
1750
|
+
if (!this.renaming) {
|
|
1751
|
+
return super.visitExportSpecifier(exportSpecifier, p);
|
|
1752
|
+
}
|
|
1753
|
+
// `export {a} from "m"` names `m`'s member rather than anything this file binds.
|
|
1754
|
+
const from = this.cursor.firstEnclosing((v) => (v === null || v === void 0 ? void 0 : v.kind) === tree_1.JS.Kind.ExportDeclaration);
|
|
1755
|
+
if ((from === null || from === void 0 ? void 0 : from.moduleSpecifier) !== undefined) {
|
|
1756
|
+
return exportSpecifier;
|
|
1757
|
+
}
|
|
1758
|
+
const specifier = exportSpecifier.specifier;
|
|
1759
|
+
if (specifier.kind === java_1.J.Kind.Identifier && specifier.simpleName === this.localName) {
|
|
1760
|
+
return Object.assign(Object.assign({}, exportSpecifier), { specifier: aliasing(specifier, this.boundName) });
|
|
1761
|
+
}
|
|
1762
|
+
if (specifier.kind === tree_1.JS.Kind.Alias) {
|
|
1763
|
+
const alias = specifier;
|
|
1764
|
+
const propertyName = alias.propertyName.element;
|
|
1765
|
+
return propertyName.kind === java_1.J.Kind.Identifier && propertyName.simpleName === this.localName
|
|
1766
|
+
? Object.assign(Object.assign({}, exportSpecifier), { specifier: Object.assign(Object.assign({}, alias), { propertyName: Object.assign(Object.assign({}, alias.propertyName), { element: Object.assign(Object.assign({}, propertyName), { simpleName: this.boundName }) }) }) })
|
|
1767
|
+
: exportSpecifier;
|
|
1768
|
+
}
|
|
1769
|
+
return super.visitExportSpecifier(exportSpecifier, p);
|
|
1770
|
+
}
|
|
1771
|
+
/**
|
|
1772
|
+
* Whether the identifier at the cursor stands for the moved binding: the right name, in a
|
|
1773
|
+
* position that references rather than declares, reaching the module scope that binds it.
|
|
1774
|
+
*/
|
|
1775
|
+
referencesBinding(identifier) {
|
|
1776
|
+
var _a;
|
|
1777
|
+
return identifier.simpleName === this.localName &&
|
|
1778
|
+
// The specifier binding the name is the one edit that is not a reference to it.
|
|
1779
|
+
!this.cursor.firstEnclosing((v) => (v === null || v === void 0 ? void 0 : v.kind) === tree_1.JS.Kind.Import) &&
|
|
1780
|
+
(0, scope_1.isValueReference)(this.cursor, identifier) &&
|
|
1781
|
+
((_a = (0, scope_1.scopeOf)(this.cursor).declaringScope(this.localName)) === null || _a === void 0 ? void 0 : _a.kind) === tree_1.JS.Kind.CompilationUnit;
|
|
1782
|
+
}
|
|
1783
|
+
/** A shorthand property's name slot is also the reference to the binding. */
|
|
1784
|
+
async visitPropertyAssignment(propertyAssignment, p) {
|
|
1785
|
+
var _a;
|
|
1786
|
+
const name = propertyAssignment.name.element;
|
|
1787
|
+
if (this.renaming && propertyAssignment.initializer === undefined &&
|
|
1788
|
+
name.kind === java_1.J.Kind.Identifier && name.simpleName === this.localName &&
|
|
1789
|
+
((_a = (0, scope_1.scopeOf)(this.cursor).declaringScope(this.localName)) === null || _a === void 0 ? void 0 : _a.kind) === tree_1.JS.Kind.CompilationUnit) {
|
|
1790
|
+
// The key names a property rather than the binding, so it carries no attribution,
|
|
1791
|
+
// the same way `aliasing` builds a property name that stands for nothing.
|
|
1792
|
+
return Object.assign(Object.assign({}, propertyAssignment), { name: Object.assign(Object.assign({}, propertyAssignment.name), { element: Object.assign(Object.assign({}, name), { type: undefined, fieldType: undefined }) }), assigmentToken: tree_1.JS.PropertyAssignment.Token.Colon, initializer: Object.assign(Object.assign({}, name), { prefix: java_1.singleSpace, simpleName: this.boundName }) });
|
|
1793
|
+
}
|
|
1794
|
+
return super.visitPropertyAssignment(propertyAssignment, p);
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
exports.RebindImport = RebindImport;
|
|
1798
|
+
/**
|
|
1799
|
+
* Rewrites the attribution a move invalidates, onto the module and member it moved to. Applied at
|
|
1800
|
+
* a reference to the moved binding. See CLAUDE.md: What a rebind's attribution follows.
|
|
1801
|
+
*/
|
|
1802
|
+
class MovedTypes extends type_visitor_1.TypeVisitor {
|
|
1803
|
+
constructor(from, to) {
|
|
1804
|
+
super();
|
|
1805
|
+
this.onPath = new Set();
|
|
1806
|
+
this.answered = new Map();
|
|
1807
|
+
// Where no member is named the two keys coincide, and both name the module.
|
|
1808
|
+
this.renamed = new Map([[from.module, to.module], [qualifiedName(from), qualifiedName(to)]]);
|
|
1809
|
+
this.fromMember = declaredMember(from);
|
|
1810
|
+
this.toMember = declaredMember(to);
|
|
1811
|
+
this.toModule = to.module;
|
|
1812
|
+
}
|
|
1813
|
+
/**
|
|
1814
|
+
* A type reached while it is still being visited is a cycle — a class holds a method whose
|
|
1815
|
+
* declaring type is that class — and answers with itself, which is what ends the walk. Every
|
|
1816
|
+
* reference to a binding shares one type, so a completed walk is remembered for the next.
|
|
1817
|
+
*/
|
|
1818
|
+
async visit(type, p) {
|
|
1819
|
+
if (type === undefined || this.onPath.has(type)) {
|
|
1820
|
+
return type;
|
|
1821
|
+
}
|
|
1822
|
+
if (this.answered.has(type)) {
|
|
1823
|
+
return this.answered.get(type);
|
|
1824
|
+
}
|
|
1825
|
+
this.onPath.add(type);
|
|
1826
|
+
try {
|
|
1827
|
+
const answer = await super.visit(type, p);
|
|
1828
|
+
// Only the type a walk entered at is remembered: one reached inside it may have met
|
|
1829
|
+
// a back edge, which answers with the type itself and so stands for the path it was on.
|
|
1830
|
+
if (this.onPath.size === 1) {
|
|
1831
|
+
this.answered.set(type, answer);
|
|
1832
|
+
}
|
|
1833
|
+
return answer;
|
|
1834
|
+
}
|
|
1835
|
+
finally {
|
|
1836
|
+
this.onPath.delete(type);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
async visitClass(aClass, p) {
|
|
1840
|
+
const visited = await super.visitClass(aClass, p);
|
|
1841
|
+
const moved = this.renamed.get(visited.fullyQualifiedName);
|
|
1842
|
+
return moved === undefined || moved === visited.fullyQualifiedName
|
|
1843
|
+
? visited
|
|
1844
|
+
: Object.assign(Object.assign({}, visited), { fullyQualifiedName: moved });
|
|
1845
|
+
}
|
|
1846
|
+
async visitMethod(method, p) {
|
|
1847
|
+
const visited = await super.visitMethod(method, p);
|
|
1848
|
+
return this.declaresMovedMember(visited.name, visited.declaringType)
|
|
1849
|
+
? Object.assign(Object.assign({}, visited), { name: this.toMember })
|
|
1850
|
+
: visited;
|
|
1851
|
+
}
|
|
1852
|
+
async visitVariable(variable, p) {
|
|
1853
|
+
const visited = await super.visitVariable(variable, p);
|
|
1854
|
+
return this.declaresMovedMember(visited.name, visited.owner)
|
|
1855
|
+
? Object.assign(Object.assign({}, visited), { name: this.toMember })
|
|
1856
|
+
: visited;
|
|
1857
|
+
}
|
|
1858
|
+
/**
|
|
1859
|
+
* Whether `name`, declared on `owner`, is the member that moved. `owner` has already followed
|
|
1860
|
+
* the move by the time this runs, so it is the module moved *to* that it has to name. A
|
|
1861
|
+
* default or namespace binding declares no member, so there is no name for one to take.
|
|
1862
|
+
*/
|
|
1863
|
+
declaresMovedMember(name, owner) {
|
|
1864
|
+
return this.toMember !== undefined && name === this.fromMember && this.toMember !== this.fromMember &&
|
|
1865
|
+
owner !== undefined && java_1.Type.isFullyQualified(owner) &&
|
|
1866
|
+
java_1.Type.FullyQualified.getFullyQualifiedName(owner) === this.toModule;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
/** A member's qualified name, or the module's own where no member is named. */
|
|
1870
|
+
function qualifiedName(binding) {
|
|
1871
|
+
const key = memberName(binding.member);
|
|
1872
|
+
return key === undefined || key === '*' ? binding.module : `${binding.module}.${key}`;
|
|
1873
|
+
}
|
|
1874
|
+
/** The member the module declares, where the binding names one: a whole module declares none. */
|
|
1875
|
+
function declaredMember(binding) {
|
|
1876
|
+
const key = memberName(binding.member);
|
|
1877
|
+
return key === undefined || key === '*' ? undefined : key;
|
|
1878
|
+
}
|
|
1879
|
+
/** Renames the identifier a default (`key` undefined) or namespace clause binds its module under. */
|
|
1880
|
+
function renameClauseBinding(importClause, key, boundName) {
|
|
1881
|
+
var _a, _b;
|
|
1882
|
+
const bound = key === undefined
|
|
1883
|
+
? (_a = importClause === null || importClause === void 0 ? void 0 : importClause.name) === null || _a === void 0 ? void 0 : _a.element
|
|
1884
|
+
: (_b = importClause === null || importClause === void 0 ? void 0 : importClause.namedBindings) === null || _b === void 0 ? void 0 : _b.alias;
|
|
1885
|
+
if ((bound === null || bound === void 0 ? void 0 : bound.kind) === java_1.J.Kind.Identifier) {
|
|
1886
|
+
bound.simpleName = boundName;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
/**
|
|
1890
|
+
* Rewrites the specifier importing `key` to import `member` under `boundName` — a bare `{member}`
|
|
1891
|
+
* where the two agree, since an alias saying the same thing is noise the source never had.
|
|
1892
|
+
*/
|
|
1893
|
+
function rewriteNamedSpecifier(importClause, key, member, boundName) {
|
|
1894
|
+
var _a, _b;
|
|
1895
|
+
if (((_a = importClause === null || importClause === void 0 ? void 0 : importClause.namedBindings) === null || _a === void 0 ? void 0 : _a.kind) !== tree_1.JS.Kind.NamedImports) {
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
for (const elem of importClause.namedBindings.elements.elements) {
|
|
1899
|
+
const specifier = elem.element;
|
|
1900
|
+
const node = specifier.specifier;
|
|
1901
|
+
if (!namedSpecifierImports(node, key)) {
|
|
1902
|
+
continue;
|
|
1903
|
+
}
|
|
1904
|
+
const alias = node.kind === tree_1.JS.Kind.Alias ? node : undefined;
|
|
1905
|
+
const local = ((_b = alias === null || alias === void 0 ? void 0 : alias.alias) !== null && _b !== void 0 ? _b : node);
|
|
1906
|
+
if (local.kind !== java_1.J.Kind.Identifier) {
|
|
1907
|
+
continue;
|
|
1908
|
+
}
|
|
1909
|
+
if (boundName === member) {
|
|
1910
|
+
// An alias node holds the specifier's leading whitespace, so the bare identifier
|
|
1911
|
+
// standing in for it takes that prefix.
|
|
1912
|
+
specifier.specifier = Object.assign(Object.assign({}, local), { prefix: (alias !== null && alias !== void 0 ? alias : local).prefix, simpleName: member });
|
|
1913
|
+
}
|
|
1914
|
+
else if (alias) {
|
|
1915
|
+
alias.propertyName.element.simpleName = member;
|
|
1916
|
+
local.simpleName = boundName;
|
|
1917
|
+
}
|
|
1918
|
+
else {
|
|
1919
|
+
specifier.specifier = aliasing(Object.assign(Object.assign({}, local), { simpleName: boundName }), member);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1434
1923
|
//# sourceMappingURL=add-import.js.map
|