@openrewrite/rewrite 8.91.1 → 8.91.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/javascript/add-import.d.ts +68 -34
- package/dist/javascript/add-import.d.ts.map +1 -1
- package/dist/javascript/add-import.js +322 -160
- package/dist/javascript/add-import.js.map +1 -1
- package/dist/javascript/amd.d.ts +138 -0
- package/dist/javascript/amd.d.ts.map +1 -0
- package/dist/javascript/amd.js +768 -0
- package/dist/javascript/amd.js.map +1 -0
- package/dist/javascript/binding.d.ts +94 -0
- package/dist/javascript/binding.d.ts.map +1 -0
- package/dist/javascript/binding.js +335 -0
- package/dist/javascript/binding.js.map +1 -0
- package/dist/javascript/index.d.ts +8 -1
- package/dist/javascript/index.d.ts.map +1 -1
- package/dist/javascript/index.js +26 -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 +2 -0
- 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 +7 -1
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/recipes/change-import.d.ts +2 -4
- package/dist/javascript/recipes/change-import.d.ts.map +1 -1
- package/dist/javascript/recipes/change-import.js +8 -240
- 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/scope.d.ts +49 -0
- package/dist/javascript/scope.d.ts.map +1 -0
- package/dist/javascript/scope.js +324 -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 +0 -29
- package/dist/javascript/templating/bindings.js.map +1 -1
- package/dist/javascript/templating/comparator.d.ts +10 -0
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +43 -14
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/engine.d.ts +17 -4
- package/dist/javascript/templating/engine.d.ts.map +1 -1
- package/dist/javascript/templating/engine.js +43 -17
- 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 +48 -28
- package/dist/javascript/templating/template.js.map +1 -1
- package/dist/javascript/templating/types.d.ts +10 -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/rewrite-javascript-version.txt +1 -1
- package/package.json +2 -2
- package/src/javascript/add-import.ts +342 -198
- package/src/javascript/amd.ts +915 -0
- package/src/javascript/binding.ts +449 -0
- package/src/javascript/index.ts +12 -1
- package/src/javascript/parser-utils.ts +2 -0
- package/src/javascript/parser.ts +13 -0
- package/src/javascript/recipes/change-import.ts +10 -260
- package/src/javascript/remove-import.ts +20 -56
- package/src/javascript/scope.ts +338 -0
- package/src/javascript/templating/bindings.ts +0 -30
- package/src/javascript/templating/comparator.ts +52 -14
- package/src/javascript/templating/engine.ts +70 -28
- 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 +62 -29
- package/src/javascript/templating/types.ts +11 -18
- package/src/javascript/templating/utils.ts +4 -2
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RemoveAmdDependency = exports.RebindAmdDependency = exports.AddAmdDependency = exports.DEFAULT_AMD_CALLEES = void 0;
|
|
4
|
+
exports.amdBlockOf = amdBlockOf;
|
|
5
|
+
exports.present = present;
|
|
6
|
+
exports.elementsOf = elementsOf;
|
|
7
|
+
exports.parametersOf = parametersOf;
|
|
8
|
+
exports.dependencyNames = dependencyNames;
|
|
9
|
+
exports.parameterNames = parameterNames;
|
|
10
|
+
exports.identifierOf = identifierOf;
|
|
11
|
+
exports.withDependency = withDependency;
|
|
12
|
+
exports.withoutDependencyAt = withoutDependencyAt;
|
|
13
|
+
exports.withDependencyModuleAt = withDependencyModuleAt;
|
|
14
|
+
exports.calleesOf = calleesOf;
|
|
15
|
+
exports.enclosingAmdBlock = enclosingAmdBlock;
|
|
16
|
+
exports.lastSegment = lastSegment;
|
|
17
|
+
exports.derivedBindingName = derivedBindingName;
|
|
18
|
+
exports.bindAmd = bindAmd;
|
|
19
|
+
exports.bodyOf = bodyOf;
|
|
20
|
+
exports.references = references;
|
|
21
|
+
exports.namesUsed = namesUsed;
|
|
22
|
+
exports.removeNewlyUnusedAmdBindings = removeNewlyUnusedAmdBindings;
|
|
23
|
+
/*
|
|
24
|
+
* Copyright 2025 the original author or authors.
|
|
25
|
+
* <p>
|
|
26
|
+
* Licensed under the Moderne Source Available License (the "License");
|
|
27
|
+
* you may not use this file except in compliance with the License.
|
|
28
|
+
* You may obtain a copy of the License at
|
|
29
|
+
* <p>
|
|
30
|
+
* https://docs.moderne.io/licensing/moderne-source-available-license
|
|
31
|
+
* <p>
|
|
32
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
33
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
34
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
35
|
+
* See the License for the specific language governing permissions and
|
|
36
|
+
* limitations under the License.
|
|
37
|
+
*/
|
|
38
|
+
const markers_1 = require("../markers");
|
|
39
|
+
const uuid_1 = require("../uuid");
|
|
40
|
+
const java_1 = require("../java");
|
|
41
|
+
const tree_1 = require("./tree");
|
|
42
|
+
const scope_1 = require("./scope");
|
|
43
|
+
const visitor_1 = require("./visitor");
|
|
44
|
+
const execution_1 = require("../execution");
|
|
45
|
+
/** RequireJS and Dojo write `define`; UI5 namespaces it as `sap.ui.define`. */
|
|
46
|
+
exports.DEFAULT_AMD_CALLEES = ["define", "require"];
|
|
47
|
+
function amdBlockOf(call, callees = exports.DEFAULT_AMD_CALLEES) {
|
|
48
|
+
if (!isAmdCallee(call, callees)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
const args = call.arguments.elements;
|
|
52
|
+
const dependenciesIndex = args.findIndex(arg => arg.element.kind === java_1.J.Kind.NewArray);
|
|
53
|
+
const factoryIndex = dependenciesIndex >= 0 ?
|
|
54
|
+
dependenciesIndex + 1 :
|
|
55
|
+
args.findIndex(arg => asFactory(arg.element) !== undefined);
|
|
56
|
+
const factory = factoryIndex >= 0 && factoryIndex < args.length ?
|
|
57
|
+
asFactory(args[factoryIndex].element) :
|
|
58
|
+
undefined;
|
|
59
|
+
if (factory === undefined) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
const dependencies = dependenciesIndex >= 0 ?
|
|
63
|
+
args[dependenciesIndex].element :
|
|
64
|
+
noDependencies();
|
|
65
|
+
return { dependenciesIndex, dependencies, factoryIndex, factory };
|
|
66
|
+
}
|
|
67
|
+
/** Stands in for the array a block written without one would have had. */
|
|
68
|
+
function noDependencies() {
|
|
69
|
+
return {
|
|
70
|
+
kind: java_1.J.Kind.NewArray,
|
|
71
|
+
id: (0, uuid_1.randomId)(),
|
|
72
|
+
prefix: java_1.emptySpace,
|
|
73
|
+
markers: markers_1.emptyMarkers,
|
|
74
|
+
dimensions: [],
|
|
75
|
+
initializer: (0, java_1.emptyContainer)()
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A callee written without a dot (`"define"`) matches by simple name on any receiver, so it
|
|
80
|
+
* also matches `foo.define(...)`; a dotted callee (`"sap.ui.define"`) requires the whole path.
|
|
81
|
+
*/
|
|
82
|
+
function isAmdCallee(call, callees) {
|
|
83
|
+
// The name check first: this runs for every call in a file, and almost none are these.
|
|
84
|
+
const simpleName = call.name.simpleName;
|
|
85
|
+
return callees.some(callee => {
|
|
86
|
+
const last = callee.substring(callee.lastIndexOf(".") + 1);
|
|
87
|
+
return simpleName === last && (callee === last || namespaceOf(call) === callee);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** The dotted path a call is written under, `sap.ui.define` for `sap.ui.define(…)`. */
|
|
91
|
+
function namespaceOf(call) {
|
|
92
|
+
var _a;
|
|
93
|
+
const segments = [call.name.simpleName];
|
|
94
|
+
let node = (_a = call.select) === null || _a === void 0 ? void 0 : _a.element;
|
|
95
|
+
while (node !== undefined) {
|
|
96
|
+
if (node.kind === java_1.J.Kind.FieldAccess) {
|
|
97
|
+
const access = node;
|
|
98
|
+
segments.unshift(access.name.element.simpleName);
|
|
99
|
+
node = access.target;
|
|
100
|
+
}
|
|
101
|
+
else if ((0, java_1.isIdentifier)(node)) {
|
|
102
|
+
segments.unshift(node.simpleName);
|
|
103
|
+
return segments.join(".");
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return segments.join(".");
|
|
110
|
+
}
|
|
111
|
+
/** A factory is written either as a function expression or as an arrow function. */
|
|
112
|
+
function asFactory(argument) {
|
|
113
|
+
if (argument.kind === tree_1.JS.Kind.StatementExpression) {
|
|
114
|
+
const statement = argument.statement;
|
|
115
|
+
return statement.kind === java_1.J.Kind.MethodDeclaration ? statement : undefined;
|
|
116
|
+
}
|
|
117
|
+
return argument.kind === tree_1.JS.Kind.ArrowFunction ? argument.lambda : undefined;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The elements of a `[]` or `()`, which the parser fills with a single `J.Empty` when
|
|
121
|
+
* there are none.
|
|
122
|
+
*/
|
|
123
|
+
function present(elements) {
|
|
124
|
+
return elements.length === 1 && elements[0].element.kind === java_1.J.Kind.Empty ? [] : [...elements];
|
|
125
|
+
}
|
|
126
|
+
function elementsOf(block) {
|
|
127
|
+
var _a, _b;
|
|
128
|
+
return present((_b = (_a = block.dependencies.initializer) === null || _a === void 0 ? void 0 : _a.elements) !== null && _b !== void 0 ? _b : []);
|
|
129
|
+
}
|
|
130
|
+
function parametersOf(block) {
|
|
131
|
+
return present(block.factory.kind === java_1.J.Kind.MethodDeclaration ?
|
|
132
|
+
block.factory.parameters.elements :
|
|
133
|
+
normalizeArrowParameters(block.factory.parameters.parameters));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* An arrow function's parameter list isn't built the way every other comma-separated list in
|
|
137
|
+
* the tree is: a trailing comma is an extra `J.Empty` entry rather than a `TrailingComma`
|
|
138
|
+
* marker, and a parameter's own trailing whitespace sits on the identifier's declaration
|
|
139
|
+
* rather than on the list entry that holds it. Folding both into the shape the rest of this
|
|
140
|
+
* module already handles lets append/remove treat every kind of factory alike.
|
|
141
|
+
*/
|
|
142
|
+
function normalizeArrowParameters(raw) {
|
|
143
|
+
if (raw.length === 1 && raw[0].element.kind === java_1.J.Kind.Empty) {
|
|
144
|
+
return [...raw];
|
|
145
|
+
}
|
|
146
|
+
const trailingEmpty = raw.length > 1 && raw[raw.length - 1].element.kind === java_1.J.Kind.Empty ?
|
|
147
|
+
raw[raw.length - 1] : undefined;
|
|
148
|
+
const real = (trailingEmpty === undefined ? raw : raw.slice(0, -1)).map(foldDeclarationTrailingSpace);
|
|
149
|
+
if (trailingEmpty === undefined) {
|
|
150
|
+
return real;
|
|
151
|
+
}
|
|
152
|
+
const last = real[real.length - 1];
|
|
153
|
+
const marker = { kind: java_1.J.Markers.TrailingComma, id: (0, uuid_1.randomId)(), suffix: trailingEmpty.after };
|
|
154
|
+
return [...real.slice(0, -1), Object.assign(Object.assign({}, last), { markers: Object.assign(Object.assign({}, last.markers), { markers: [...last.markers.markers, marker] }) })];
|
|
155
|
+
}
|
|
156
|
+
/** Moves a VariableDeclarations parameter's trailing whitespace from the identifier's own declaration up to the list entry, when the entry doesn't already carry any. */
|
|
157
|
+
function foldDeclarationTrailingSpace(entry) {
|
|
158
|
+
if (entry.element.kind !== java_1.J.Kind.VariableDeclarations ||
|
|
159
|
+
entry.after.whitespace !== "" || entry.after.comments.length > 0) {
|
|
160
|
+
return entry;
|
|
161
|
+
}
|
|
162
|
+
const declaration = entry.element;
|
|
163
|
+
const variable = declaration.variables[0];
|
|
164
|
+
if (variable === undefined) {
|
|
165
|
+
return entry;
|
|
166
|
+
}
|
|
167
|
+
const folded = Object.assign(Object.assign({}, declaration), { variables: [Object.assign(Object.assign({}, variable), { after: java_1.emptySpace }), ...declaration.variables.slice(1)] });
|
|
168
|
+
return Object.assign(Object.assign({}, entry), { element: folded, after: variable.after });
|
|
169
|
+
}
|
|
170
|
+
function dependencyNames(block) {
|
|
171
|
+
return elementsOf(block).map(padded => {
|
|
172
|
+
const element = padded.element;
|
|
173
|
+
return (0, java_1.isLiteral)(element) && typeof element.value === "string" ? element.value : "";
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function parameterNames(block) {
|
|
177
|
+
return parametersOf(block).map(padded => { var _a; return (_a = identifierOf(padded.element)) === null || _a === void 0 ? void 0 : _a.simpleName; });
|
|
178
|
+
}
|
|
179
|
+
function identifierOf(parameter) {
|
|
180
|
+
var _a;
|
|
181
|
+
if (parameter.kind === java_1.J.Kind.VariableDeclarations) {
|
|
182
|
+
const name = (_a = parameter.variables[0]) === null || _a === void 0 ? void 0 : _a.element.name;
|
|
183
|
+
return name !== undefined && (0, java_1.isIdentifier)(name) ? name : undefined;
|
|
184
|
+
}
|
|
185
|
+
return (0, java_1.isIdentifier)(parameter) ? parameter : undefined;
|
|
186
|
+
}
|
|
187
|
+
const dependencySlot = {
|
|
188
|
+
prefixOf: element => element.prefix,
|
|
189
|
+
withPrefix: (element, prefix) => (Object.assign(Object.assign({}, element), { prefix }))
|
|
190
|
+
};
|
|
191
|
+
const parameterSlot = {
|
|
192
|
+
prefixOf: element => { var _a, _b; return (_b = (_a = identifierOf(element)) === null || _a === void 0 ? void 0 : _a.prefix) !== null && _b !== void 0 ? _b : element.prefix; },
|
|
193
|
+
withPrefix: (element, prefix) => {
|
|
194
|
+
if (element.kind !== java_1.J.Kind.VariableDeclarations) {
|
|
195
|
+
return Object.assign(Object.assign({}, element), { prefix });
|
|
196
|
+
}
|
|
197
|
+
const declaration = element;
|
|
198
|
+
const variable = declaration.variables[0];
|
|
199
|
+
if (variable === undefined) {
|
|
200
|
+
return element;
|
|
201
|
+
}
|
|
202
|
+
return Object.assign(Object.assign({}, declaration), { variables: [
|
|
203
|
+
Object.assign(Object.assign({}, variable), { element: Object.assign(Object.assign({}, variable.element), { name: Object.assign(Object.assign({}, variable.element.name), { prefix }) }) }),
|
|
204
|
+
...declaration.variables.slice(1)
|
|
205
|
+
] });
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* A trailing comma is a marker on the entry it follows, so it has to travel to whichever
|
|
210
|
+
* entry ends up last. Left where it was, it prints as a second comma and the array gains a
|
|
211
|
+
* hole, which shifts every dependency after it out of step with its parameter.
|
|
212
|
+
*/
|
|
213
|
+
function moveTrailingComma(from, to) {
|
|
214
|
+
const trailing = (0, markers_1.findMarker)(from, java_1.J.Markers.TrailingComma);
|
|
215
|
+
if (trailing === undefined) {
|
|
216
|
+
return { from, to };
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
from: Object.assign(Object.assign({}, from), { markers: Object.assign(Object.assign({}, from.markers), { markers: from.markers.markers.filter(m => m !== trailing) }) }),
|
|
220
|
+
to: Object.assign(Object.assign({}, to), { markers: Object.assign(Object.assign({}, to.markers), { markers: [...to.markers.markers, trailing] }) })
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* The whitespace that separates one entry from the next, taken from the entries already
|
|
225
|
+
* there so that a block listing its dependencies one per line keeps doing so.
|
|
226
|
+
*/
|
|
227
|
+
function separator(entries, slot) {
|
|
228
|
+
if (entries.length >= 2) {
|
|
229
|
+
return slot.prefixOf(entries[1].element);
|
|
230
|
+
}
|
|
231
|
+
const first = slot.prefixOf(entries[0].element);
|
|
232
|
+
return (0, java_1.spaceContainsNewline)(first) ? first : (0, java_1.space)(" ");
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Appends `element`. The trailing entry's padding holds the whitespace before the closing
|
|
236
|
+
* bracket, so it has to move along with the position.
|
|
237
|
+
*/
|
|
238
|
+
function appendEntry(entries, element, slot) {
|
|
239
|
+
const last = entries[entries.length - 1];
|
|
240
|
+
if (last === undefined) {
|
|
241
|
+
return [(0, java_1.rightPadded)(element, java_1.emptySpace)];
|
|
242
|
+
}
|
|
243
|
+
const positioned = slot.withPrefix(element, separator(entries, slot));
|
|
244
|
+
const trailing = splitTrailingComments(last.after);
|
|
245
|
+
const moved = moveTrailingComma(Object.assign(Object.assign({}, last), { after: trailing.comments }), (0, java_1.rightPadded)(positioned, trailing.whitespace));
|
|
246
|
+
return [...entries.slice(0, -1), moved.from, moved.to];
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Splits an entry's trailing padding when it stops being last. Plain whitespace only ever
|
|
250
|
+
* positioned the closing delimiter, so it travels to whichever entry becomes the new last. A
|
|
251
|
+
* comment documents the entry it follows and keeps its own trailing newline, so it stays put
|
|
252
|
+
* whole — a line comment with nothing left after it would otherwise swallow the comma the
|
|
253
|
+
* printer inserts right after this padding.
|
|
254
|
+
*/
|
|
255
|
+
function splitTrailingComments(after) {
|
|
256
|
+
return after.comments.length === 0 ?
|
|
257
|
+
{ comments: Object.assign(Object.assign({}, after), { whitespace: "" }), whitespace: (0, java_1.space)(after.whitespace) } :
|
|
258
|
+
{ comments: after, whitespace: java_1.emptySpace };
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* The plain-whitespace part of a leading prefix whose entry is about to be removed. A comment in
|
|
262
|
+
* that prefix documents the entry it precedes and is dropped along with it; the whitespace only
|
|
263
|
+
* ever positioned that entry after the opening bracket or a comma, so it's what travels to
|
|
264
|
+
* whichever entry takes its place.
|
|
265
|
+
*/
|
|
266
|
+
function leadingWhitespaceOf(prefix) {
|
|
267
|
+
return prefix.comments.length === 0 ? prefix.whitespace : "";
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Removes the entry at `index`. The first entry carries no separating whitespace and the
|
|
271
|
+
* last carries the whitespace before the closing bracket, so removing either hands its
|
|
272
|
+
* padding to whichever entry takes its place.
|
|
273
|
+
*/
|
|
274
|
+
function removeEntry(entries, index, slot) {
|
|
275
|
+
if (index < 0 || index >= entries.length) {
|
|
276
|
+
return [...entries];
|
|
277
|
+
}
|
|
278
|
+
const remaining = [...entries];
|
|
279
|
+
const [removed] = remaining.splice(index, 1);
|
|
280
|
+
if (remaining.length === 0) {
|
|
281
|
+
return remaining;
|
|
282
|
+
}
|
|
283
|
+
if (index === 0) {
|
|
284
|
+
// Spread the survivor's own prefix rather than replacing it outright, so a comment it
|
|
285
|
+
// already carries survives; only its plain whitespace is replaced.
|
|
286
|
+
const whitespace = leadingWhitespaceOf(slot.prefixOf(removed.element));
|
|
287
|
+
const survivor = remaining[0];
|
|
288
|
+
remaining[0] = Object.assign(Object.assign({}, survivor), { element: slot.withPrefix(survivor.element, Object.assign(Object.assign({}, slot.prefixOf(survivor.element)), { whitespace })) });
|
|
289
|
+
}
|
|
290
|
+
else if (index === remaining.length) {
|
|
291
|
+
const last = Object.assign(Object.assign({}, remaining[index - 1]), { after: removed.after });
|
|
292
|
+
remaining[index - 1] = moveTrailingComma(removed, last).to;
|
|
293
|
+
}
|
|
294
|
+
return remaining;
|
|
295
|
+
}
|
|
296
|
+
function emptyExpression() {
|
|
297
|
+
return { kind: java_1.J.Kind.Empty, id: (0, uuid_1.randomId)(), prefix: java_1.emptySpace, markers: markers_1.emptyMarkers };
|
|
298
|
+
}
|
|
299
|
+
function withElements(dependencies, elements) {
|
|
300
|
+
const initializer = dependencies.initializer;
|
|
301
|
+
return Object.assign(Object.assign({}, dependencies), { initializer: Object.assign(Object.assign({}, initializer), { elements: elements.length === 0 ? [(0, java_1.rightPadded)(emptyExpression(), java_1.emptySpace)] : elements }) });
|
|
302
|
+
}
|
|
303
|
+
function withParameters(factory, parameters) {
|
|
304
|
+
const elements = parameters.length === 0 ? [(0, java_1.rightPadded)(emptyExpression(), java_1.emptySpace)] : parameters;
|
|
305
|
+
if (factory.kind === java_1.J.Kind.MethodDeclaration) {
|
|
306
|
+
const method = factory;
|
|
307
|
+
return Object.assign(Object.assign({}, method), { parameters: Object.assign(Object.assign({}, method.parameters), { elements: elements }) });
|
|
308
|
+
}
|
|
309
|
+
const lambda = factory;
|
|
310
|
+
// Unparenthesized arrow parameters are only valid JavaScript for exactly one parameter.
|
|
311
|
+
if (lambda.parameters.parenthesized || parameters.length === 1) {
|
|
312
|
+
return Object.assign(Object.assign({}, lambda), { parameters: Object.assign(Object.assign({}, lambda.parameters), { parameters: elements }) });
|
|
313
|
+
}
|
|
314
|
+
return parenthesize(lambda, elements);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* A bare arrow parameter has no closing delimiter of its own — the space before `=>` sits on
|
|
318
|
+
* the parameter itself. Wrapping it in parens turns that into the space before the new `)`,
|
|
319
|
+
* so it has to move to `arrow` instead, where it will actually print before `=>` again.
|
|
320
|
+
*/
|
|
321
|
+
function parenthesize(lambda, elements) {
|
|
322
|
+
const last = elements[elements.length - 1];
|
|
323
|
+
const arrowSpace = last.after.whitespace === "" ? lambda.arrow : last.after;
|
|
324
|
+
return Object.assign(Object.assign({}, lambda), { parameters: Object.assign(Object.assign({}, lambda.parameters), { parenthesized: true, parameters: [...elements.slice(0, -1), Object.assign(Object.assign({}, last), { after: java_1.emptySpace })] }), arrow: arrowSpace });
|
|
325
|
+
}
|
|
326
|
+
function identifier(name) {
|
|
327
|
+
return {
|
|
328
|
+
kind: java_1.J.Kind.Identifier,
|
|
329
|
+
id: (0, uuid_1.randomId)(),
|
|
330
|
+
prefix: java_1.emptySpace,
|
|
331
|
+
markers: markers_1.emptyMarkers,
|
|
332
|
+
annotations: [],
|
|
333
|
+
simpleName: name,
|
|
334
|
+
type: undefined,
|
|
335
|
+
fieldType: undefined
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function dependencyLiteral(module, quote) {
|
|
339
|
+
return {
|
|
340
|
+
kind: java_1.J.Kind.Literal,
|
|
341
|
+
id: (0, uuid_1.randomId)(),
|
|
342
|
+
prefix: java_1.emptySpace,
|
|
343
|
+
markers: markers_1.emptyMarkers,
|
|
344
|
+
value: module,
|
|
345
|
+
valueSource: `${quote}${module}${quote}`
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
/** The quote the block's own dependencies use, so a new one matches them. */
|
|
349
|
+
function quoteOf(block) {
|
|
350
|
+
for (const padded of elementsOf(block)) {
|
|
351
|
+
const source = (0, java_1.isLiteral)(padded.element) ? padded.element.valueSource : undefined;
|
|
352
|
+
const quote = source === null || source === void 0 ? void 0 : source.charAt(0);
|
|
353
|
+
if (quote === '"' || quote === "'") {
|
|
354
|
+
return quote;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return '"';
|
|
358
|
+
}
|
|
359
|
+
function parameterDeclaration(name) {
|
|
360
|
+
return {
|
|
361
|
+
kind: java_1.J.Kind.VariableDeclarations,
|
|
362
|
+
id: (0, uuid_1.randomId)(),
|
|
363
|
+
prefix: java_1.emptySpace,
|
|
364
|
+
markers: markers_1.emptyMarkers,
|
|
365
|
+
leadingAnnotations: [],
|
|
366
|
+
modifiers: [],
|
|
367
|
+
variables: [(0, java_1.rightPadded)({
|
|
368
|
+
kind: java_1.J.Kind.NamedVariable,
|
|
369
|
+
id: (0, uuid_1.randomId)(),
|
|
370
|
+
prefix: java_1.emptySpace,
|
|
371
|
+
markers: markers_1.emptyMarkers,
|
|
372
|
+
name: identifier(name),
|
|
373
|
+
dimensionsAfterName: []
|
|
374
|
+
}, java_1.emptySpace)]
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Adds a dependency and its binding, keeping the array and the parameter list index-aligned.
|
|
379
|
+
* Refuses (returns `undefined`) unless the two are already the same length: fewer parameters
|
|
380
|
+
* than dependencies would invent a binding for one an author left unbound on purpose, and more
|
|
381
|
+
* would pair the new dependency with an existing surplus parameter instead of a fresh one —
|
|
382
|
+
* either way corrupting the pairing this module exists to protect.
|
|
383
|
+
*/
|
|
384
|
+
function withDependency(call, block, module, binding) {
|
|
385
|
+
if (parametersOf(block).length !== elementsOf(block).length) {
|
|
386
|
+
return undefined;
|
|
387
|
+
}
|
|
388
|
+
const dependencies = withElements(block.dependencies, appendEntry(elementsOf(block), dependencyLiteral(module, quoteOf(block)), dependencySlot));
|
|
389
|
+
const factory = withParameters(block.factory, appendEntry(parametersOf(block), parameterDeclaration(binding), parameterSlot));
|
|
390
|
+
return withParts(call, block, dependencies, factory);
|
|
391
|
+
}
|
|
392
|
+
function withoutDependencyAt(call, block, index) {
|
|
393
|
+
const dependencies = withElements(block.dependencies, removeEntry(elementsOf(block), index, dependencySlot));
|
|
394
|
+
const parameters = parametersOf(block);
|
|
395
|
+
const factory = index >= parameters.length ?
|
|
396
|
+
block.factory :
|
|
397
|
+
withParameters(block.factory, removeEntry(parameters, index, parameterSlot));
|
|
398
|
+
return withParts(call, block, dependencies, factory);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Swaps the module the dependency at `index` names, leaving its parameter and every other
|
|
402
|
+
* entry's position untouched — simpler than a removal and a fresh append, which would shift
|
|
403
|
+
* every dependency after it and re-litigate the parameter pairing this module exists to protect.
|
|
404
|
+
*/
|
|
405
|
+
function withDependencyModuleAt(call, block, index, module) {
|
|
406
|
+
const quote = quoteOf(block);
|
|
407
|
+
const elements = [...elementsOf(block)];
|
|
408
|
+
const entry = elements[index];
|
|
409
|
+
const literal = entry.element;
|
|
410
|
+
const updated = Object.assign(Object.assign({}, literal), { value: module, valueSource: `${quote}${module}${quote}` });
|
|
411
|
+
elements[index] = Object.assign(Object.assign({}, entry), { element: updated });
|
|
412
|
+
const dependencies = withElements(block.dependencies, elements);
|
|
413
|
+
return withParts(call, block, dependencies, block.factory);
|
|
414
|
+
}
|
|
415
|
+
function withParts(call, block, dependencies, factory) {
|
|
416
|
+
var _a, _b;
|
|
417
|
+
const args = [...call.arguments.elements];
|
|
418
|
+
let factoryIndex = block.factoryIndex;
|
|
419
|
+
if (block.dependenciesIndex >= 0) {
|
|
420
|
+
args[block.dependenciesIndex] = Object.assign(Object.assign({}, args[block.dependenciesIndex]), { element: dependencies });
|
|
421
|
+
}
|
|
422
|
+
else if (present((_b = (_a = dependencies.initializer) === null || _a === void 0 ? void 0 : _a.elements) !== null && _b !== void 0 ? _b : []).length > 0) {
|
|
423
|
+
// The array takes the position the factory held, so it inherits what led up to it.
|
|
424
|
+
const leading = args[factoryIndex].element;
|
|
425
|
+
args[factoryIndex] = Object.assign(Object.assign({}, args[factoryIndex]), { element: Object.assign(Object.assign({}, leading), { prefix: (0, java_1.space)(" ") }) });
|
|
426
|
+
args.splice(factoryIndex, 0, (0, java_1.rightPadded)(Object.assign(Object.assign({}, dependencies), { prefix: leading.prefix }), java_1.emptySpace));
|
|
427
|
+
factoryIndex += 1;
|
|
428
|
+
}
|
|
429
|
+
args[factoryIndex] = Object.assign(Object.assign({}, args[factoryIndex]), { element: restoreFactory(args[factoryIndex].element, factory) });
|
|
430
|
+
return Object.assign(Object.assign({}, call), { arguments: Object.assign(Object.assign({}, call.arguments), { elements: args }) });
|
|
431
|
+
}
|
|
432
|
+
/** Puts the rewritten factory back into whichever wrapper the original was written as. */
|
|
433
|
+
function restoreFactory(original, factory) {
|
|
434
|
+
if (original.kind === tree_1.JS.Kind.StatementExpression) {
|
|
435
|
+
const statementExpression = Object.assign(Object.assign({}, original), { statement: factory });
|
|
436
|
+
return statementExpression;
|
|
437
|
+
}
|
|
438
|
+
const arrowFunction = Object.assign(Object.assign({}, original), { lambda: factory });
|
|
439
|
+
return arrowFunction;
|
|
440
|
+
}
|
|
441
|
+
function calleesOf(options) {
|
|
442
|
+
const callee = options === null || options === void 0 ? void 0 : options.amdCallee;
|
|
443
|
+
return callee === undefined ? exports.DEFAULT_AMD_CALLEES : typeof callee === "string" ? [callee] : callee;
|
|
444
|
+
}
|
|
445
|
+
/** The nearest AMD block the cursor sits inside, which is the one a binding belongs to. */
|
|
446
|
+
function enclosingAmdBlock(visitor, options) {
|
|
447
|
+
const callees = calleesOf(options);
|
|
448
|
+
let cursor = (0, scope_1.cursorOf)(visitor);
|
|
449
|
+
while (cursor !== undefined) {
|
|
450
|
+
const value = cursor.value;
|
|
451
|
+
if ((value === null || value === void 0 ? void 0 : value.kind) === java_1.J.Kind.MethodInvocation) {
|
|
452
|
+
const block = amdBlockOf(value, callees);
|
|
453
|
+
if (block !== undefined) {
|
|
454
|
+
return { call: value, block };
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
cursor = cursor.parent;
|
|
458
|
+
}
|
|
459
|
+
return undefined;
|
|
460
|
+
}
|
|
461
|
+
/** The conventional local name for a module: its last path segment. */
|
|
462
|
+
function lastSegment(module) {
|
|
463
|
+
return module.substring(module.lastIndexOf("/") + 1);
|
|
464
|
+
}
|
|
465
|
+
// Always-reserved words, plus the strict-mode-only ones (module code is always strict) and
|
|
466
|
+
// `await`/`yield`, contextual elsewhere but a trap to bind regardless of where they're legal.
|
|
467
|
+
const RESERVED_WORDS = new Set([
|
|
468
|
+
"break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do",
|
|
469
|
+
"else", "enum", "export", "extends", "false", "finally", "for", "function", "if", "import",
|
|
470
|
+
"in", "instanceof", "new", "null", "return", "super", "switch", "this", "throw", "true", "try",
|
|
471
|
+
"typeof", "var", "void", "while", "with",
|
|
472
|
+
"await", "implements", "interface", "let", "package", "private", "protected", "public",
|
|
473
|
+
"static", "yield"
|
|
474
|
+
]);
|
|
475
|
+
/**
|
|
476
|
+
* `lastSegment(module)`, or `undefined` where that string cannot bind a name — a scoped
|
|
477
|
+
* package's `@scope/my-lib`, a subpath's `lodash.merge`, a `-`/`.`-bearing name, or a reserved
|
|
478
|
+
* word are all real module strings that are not legal identifiers. ASCII identifiers only —
|
|
479
|
+
* stricter than JavaScript itself, which allows Unicode, but refusing there is safe.
|
|
480
|
+
*/
|
|
481
|
+
function derivedBindingName(module) {
|
|
482
|
+
const segment = lastSegment(module);
|
|
483
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(segment) && !RESERVED_WORDS.has(segment) ? segment : undefined;
|
|
484
|
+
}
|
|
485
|
+
/** Queued reservations already targeting this block: the shared source for both dedup and deconfliction. */
|
|
486
|
+
function queuedFor(visitor, blockId) {
|
|
487
|
+
return visitor.afterVisit.filter((v) => v instanceof AddAmdDependency && v.blockId === blockId);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* A local binding for `module` on the AMD lane, creating one where the block does not already
|
|
491
|
+
* have it, or `undefined` where no safe binding exists.
|
|
492
|
+
*
|
|
493
|
+
* The name is decided from the cursor and returned at once; the edit that creates the binding
|
|
494
|
+
* is deferred onto `visitor.afterVisit`, as `bindImport`'s is.
|
|
495
|
+
*/
|
|
496
|
+
function bindAmd(visitor, amd, module, preferredName, callees, pinned = false) {
|
|
497
|
+
var _a;
|
|
498
|
+
const modules = dependencyNames(amd.block);
|
|
499
|
+
const bindings = parameterNames(amd.block);
|
|
500
|
+
const declared = module === "" ? -1 : modules.indexOf(module);
|
|
501
|
+
if (declared >= 0) {
|
|
502
|
+
return bindings[declared];
|
|
503
|
+
}
|
|
504
|
+
// Two calls naming the same module at the same block are the same request (ADR 0013),
|
|
505
|
+
// answered from whichever reservation is already queued rather than doubling the dependency.
|
|
506
|
+
const queued = queuedFor(visitor, amd.call.id);
|
|
507
|
+
const reserved = (_a = queued.find(v => v.module === module)) === null || _a === void 0 ? void 0 : _a.binding;
|
|
508
|
+
if (reserved !== undefined) {
|
|
509
|
+
return reserved;
|
|
510
|
+
}
|
|
511
|
+
// A parameter can only be appended at the end, so a block whose dependency and parameter
|
|
512
|
+
// counts already disagree would bind the new module against the wrong parameter either way.
|
|
513
|
+
if (bindings.length !== elementsOf(amd.block).length) {
|
|
514
|
+
return undefined;
|
|
515
|
+
}
|
|
516
|
+
if (preferredName === undefined && derivedBindingName(module) === undefined) {
|
|
517
|
+
return undefined;
|
|
518
|
+
}
|
|
519
|
+
// A parameter is in scope across the whole factory, and the queue hands this name to later
|
|
520
|
+
// requests at cursors inside it, so every name the factory declares is one it has to clear.
|
|
521
|
+
const taken = [...bindings, ...(0, scope_1.namesDeclaredWithin)(amd.block.factory), ...queued.map(v => v.binding)];
|
|
522
|
+
const requested = preferredName !== null && preferredName !== void 0 ? preferredName : derivedBindingName(module);
|
|
523
|
+
const binding = (0, scope_1.deconflict)(requested, candidate => taken.includes(candidate));
|
|
524
|
+
if (pinned && binding !== requested) {
|
|
525
|
+
// An alias is bound verbatim or not at all, since the caller may already have emitted
|
|
526
|
+
// code naming it, and a deconflicted spelling would leave that unbound.
|
|
527
|
+
return undefined;
|
|
528
|
+
}
|
|
529
|
+
visitor.afterVisit.push(new AddAmdDependency(amd.call.id, module, binding, callees));
|
|
530
|
+
return binding;
|
|
531
|
+
}
|
|
532
|
+
/** The factory's body: a `J.Block` for `function(){}` and most arrows, or the bare expression for `() => expr`. */
|
|
533
|
+
function bodyOf(block) {
|
|
534
|
+
return block.factory.kind === java_1.J.Kind.MethodDeclaration ?
|
|
535
|
+
block.factory.body :
|
|
536
|
+
block.factory.body;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Whether the factory body references `name`. This counts any identifier of that name,
|
|
540
|
+
* including a member in a field access, since a stray dependency is fine where a shadowed one
|
|
541
|
+
* is not. `missingBodyAnswer` covers a body-less factory: `false` for an addition (nothing to
|
|
542
|
+
* conflict with), `true` for a removal (nothing to prove the binding unused).
|
|
543
|
+
*/
|
|
544
|
+
async function references(block, name, missingBodyAnswer) {
|
|
545
|
+
const body = bodyOf(block);
|
|
546
|
+
if (body === undefined) {
|
|
547
|
+
return missingBodyAnswer;
|
|
548
|
+
}
|
|
549
|
+
let found = false;
|
|
550
|
+
const finder = new class extends visitor_1.JavaScriptVisitor {
|
|
551
|
+
async visitIdentifier(id, c) {
|
|
552
|
+
if (id.simpleName === name) {
|
|
553
|
+
found = true;
|
|
554
|
+
}
|
|
555
|
+
return super.visitIdentifier(id, c);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
await finder.visit(body, new execution_1.ExecutionContext());
|
|
559
|
+
return found;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Every name the factory body references, one walk for the whole block rather than one per
|
|
563
|
+
* name. `missingBodyAnswer` means what it means on `references`: the answer `.has` gives, for
|
|
564
|
+
* every name, when the factory has no body to walk.
|
|
565
|
+
*/
|
|
566
|
+
async function namesUsed(block, missingBodyAnswer) {
|
|
567
|
+
const body = bodyOf(block);
|
|
568
|
+
if (body === undefined) {
|
|
569
|
+
return { has: () => missingBodyAnswer };
|
|
570
|
+
}
|
|
571
|
+
const names = new Set();
|
|
572
|
+
const collector = new class extends visitor_1.JavaScriptVisitor {
|
|
573
|
+
async visitIdentifier(id, c) {
|
|
574
|
+
names.add(id.simpleName);
|
|
575
|
+
return super.visitIdentifier(id, c);
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
await collector.visit(body, new execution_1.ExecutionContext());
|
|
579
|
+
return names;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Applies the dependency a `maybeBind` call settled on. The block is found by id: its caller
|
|
583
|
+
* has already emitted a reference to the binding, so a block that cannot be found is an error
|
|
584
|
+
* rather than a skipped edit.
|
|
585
|
+
*/
|
|
586
|
+
class AddAmdDependency extends visitor_1.JavaScriptVisitor {
|
|
587
|
+
constructor(blockId, module, binding, callees) {
|
|
588
|
+
super();
|
|
589
|
+
this.blockId = blockId;
|
|
590
|
+
this.module = module;
|
|
591
|
+
this.binding = binding;
|
|
592
|
+
this.callees = callees;
|
|
593
|
+
this.applied = false;
|
|
594
|
+
}
|
|
595
|
+
async visitJsCompilationUnit(cu, p) {
|
|
596
|
+
const visited = await super.visitJsCompilationUnit(cu, p);
|
|
597
|
+
if (!this.applied) {
|
|
598
|
+
throw new Error(`No AMD block ${this.blockId} to declare '${this.module}' on, but '${this.binding}' was reported bound`);
|
|
599
|
+
}
|
|
600
|
+
return visited;
|
|
601
|
+
}
|
|
602
|
+
async visitMethodInvocation(m, p) {
|
|
603
|
+
const visited = await super.visitMethodInvocation(m, p);
|
|
604
|
+
if (visited.id !== this.blockId) {
|
|
605
|
+
return visited;
|
|
606
|
+
}
|
|
607
|
+
const block = amdBlockOf(visited, this.callees);
|
|
608
|
+
if (block === undefined) {
|
|
609
|
+
return visited;
|
|
610
|
+
}
|
|
611
|
+
this.applied = true;
|
|
612
|
+
if (!(await references(block, this.binding, false))) {
|
|
613
|
+
// Nothing referenced the binding, so the caller asked and then did not use the answer.
|
|
614
|
+
return visited;
|
|
615
|
+
}
|
|
616
|
+
// Another edit in the same visit can drop or add a factory parameter, reopening a count
|
|
617
|
+
// mismatch bindAmd's own check already cleared — an error here for the same reason a missing
|
|
618
|
+
// block is one.
|
|
619
|
+
const dependency = withDependency(visited, block, this.module, this.binding);
|
|
620
|
+
if (dependency === undefined) {
|
|
621
|
+
throw new Error(`AMD block ${this.blockId} no longer has matching dependency and parameter counts, ` +
|
|
622
|
+
`so '${this.module}' could not be declared for '${this.binding}', which was reported bound`);
|
|
623
|
+
}
|
|
624
|
+
return dependency;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
exports.AddAmdDependency = AddAmdDependency;
|
|
628
|
+
/**
|
|
629
|
+
* Applies a `maybeRebind` call's decision on the AMD lane: swaps the dependency's module in
|
|
630
|
+
* place, found by the block's tree id and the module it named when the caller asked.
|
|
631
|
+
*/
|
|
632
|
+
class RebindAmdDependency extends visitor_1.JavaScriptVisitor {
|
|
633
|
+
constructor(blockId, fromModule, toModule, callees) {
|
|
634
|
+
super();
|
|
635
|
+
this.blockId = blockId;
|
|
636
|
+
this.fromModule = fromModule;
|
|
637
|
+
this.toModule = toModule;
|
|
638
|
+
this.callees = callees;
|
|
639
|
+
this.applied = false;
|
|
640
|
+
}
|
|
641
|
+
async visitJsCompilationUnit(cu, p) {
|
|
642
|
+
const visited = await super.visitJsCompilationUnit(cu, p);
|
|
643
|
+
if (!this.applied) {
|
|
644
|
+
throw new Error(`No AMD block ${this.blockId} to rebind '${this.fromModule}' to '${this.toModule}' on`);
|
|
645
|
+
}
|
|
646
|
+
return visited;
|
|
647
|
+
}
|
|
648
|
+
async visitMethodInvocation(m, p) {
|
|
649
|
+
const visited = await super.visitMethodInvocation(m, p);
|
|
650
|
+
if (visited.id !== this.blockId) {
|
|
651
|
+
return visited;
|
|
652
|
+
}
|
|
653
|
+
const block = amdBlockOf(visited, this.callees);
|
|
654
|
+
if (block === undefined) {
|
|
655
|
+
return visited;
|
|
656
|
+
}
|
|
657
|
+
this.applied = true;
|
|
658
|
+
const index = this.fromModule === "" ? -1 : dependencyNames(block).indexOf(this.fromModule);
|
|
659
|
+
if (index < 0) {
|
|
660
|
+
throw new Error(`AMD block ${this.blockId} no longer has dependency '${this.fromModule}' to rebind to '${this.toModule}'`);
|
|
661
|
+
}
|
|
662
|
+
return withDependencyModuleAt(visited, block, index, this.toModule);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
exports.RebindAmdDependency = RebindAmdDependency;
|
|
666
|
+
/**
|
|
667
|
+
* The AMD counterpart to `RemoveImport`: drops a dependency the block's body no longer names,
|
|
668
|
+
* along with the parameter bound to it. A dependency the factory takes no parameter for is
|
|
669
|
+
* loaded for its side effects and stays, matching `bindAmd`'s own read of such a block. A
|
|
670
|
+
* `member`-scoped request is a no-op here — see `maybeUnbind`.
|
|
671
|
+
*/
|
|
672
|
+
class RemoveAmdDependency extends visitor_1.JavaScriptVisitor {
|
|
673
|
+
constructor(module, member, callees = exports.DEFAULT_AMD_CALLEES) {
|
|
674
|
+
super();
|
|
675
|
+
this.module = module;
|
|
676
|
+
this.member = member;
|
|
677
|
+
this.callees = callees;
|
|
678
|
+
}
|
|
679
|
+
async visitMethodInvocation(m, p) {
|
|
680
|
+
const visited = await super.visitMethodInvocation(m, p);
|
|
681
|
+
if (this.member !== undefined) {
|
|
682
|
+
return visited;
|
|
683
|
+
}
|
|
684
|
+
const block = amdBlockOf(visited, this.callees);
|
|
685
|
+
if (block === undefined) {
|
|
686
|
+
return visited;
|
|
687
|
+
}
|
|
688
|
+
const index = dependencyNames(block).indexOf(this.module);
|
|
689
|
+
if (index < 0) {
|
|
690
|
+
return visited;
|
|
691
|
+
}
|
|
692
|
+
const binding = parameterNames(block)[index];
|
|
693
|
+
if (binding === undefined || await references(block, binding, true)) {
|
|
694
|
+
return visited;
|
|
695
|
+
}
|
|
696
|
+
return withoutDependencyAt(visited, block, index);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
exports.RemoveAmdDependency = RemoveAmdDependency;
|
|
700
|
+
/**
|
|
701
|
+
* Drops AMD bindings a rewrite left unreferenced. One already unused beforehand stays: it is
|
|
702
|
+
* loaded for its side effects, so dropping it changes what the module loads. Needing the tree as
|
|
703
|
+
* it stood before the rewrite is why this takes both and does not defer. Blocks are matched by
|
|
704
|
+
* the call's id, so one a rewrite rebuilds rather than edits is left alone.
|
|
705
|
+
*/
|
|
706
|
+
async function removeNewlyUnusedAmdBindings(before, after, ctx, options) {
|
|
707
|
+
const callees = calleesOf(options);
|
|
708
|
+
const usedBeforeByBlock = new Map();
|
|
709
|
+
const collector = new class extends visitor_1.JavaScriptVisitor {
|
|
710
|
+
async visitMethodInvocation(m, c) {
|
|
711
|
+
const block = amdBlockOf(m, callees);
|
|
712
|
+
if (block !== undefined) {
|
|
713
|
+
usedBeforeByBlock.set(m.id, await usedBindings(block));
|
|
714
|
+
}
|
|
715
|
+
return super.visitMethodInvocation(m, c);
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
await collector.visit(before, ctx);
|
|
719
|
+
if (usedBeforeByBlock.size === 0) {
|
|
720
|
+
return after;
|
|
721
|
+
}
|
|
722
|
+
const sweeper = new class extends visitor_1.JavaScriptVisitor {
|
|
723
|
+
async visitMethodInvocation(m, c) {
|
|
724
|
+
let call = await super.visitMethodInvocation(m, c);
|
|
725
|
+
const usedBefore = usedBeforeByBlock.get(call.id);
|
|
726
|
+
let block = usedBefore === undefined ? undefined : amdBlockOf(call, callees);
|
|
727
|
+
if (usedBefore === undefined || block === undefined) {
|
|
728
|
+
return call;
|
|
729
|
+
}
|
|
730
|
+
// A block written `define(factory)` takes `require`, `exports` and `module` from the
|
|
731
|
+
// loader, so a parameter there is positional against arguments no dependency list names.
|
|
732
|
+
if (block.dependenciesIndex < 0 || parameterNames(block).length !== elementsOf(block).length) {
|
|
733
|
+
return call;
|
|
734
|
+
}
|
|
735
|
+
// withoutDependencyAt only edits the dependency array and parameter list, never the
|
|
736
|
+
// body, so which names it references can't change between removals.
|
|
737
|
+
const usedNow = await namesUsed(block, true);
|
|
738
|
+
for (;;) {
|
|
739
|
+
const bindings = parameterNames(block);
|
|
740
|
+
const goneIndex = bindings.findIndex(binding => binding !== undefined && usedBefore.has(binding) && !usedNow.has(binding));
|
|
741
|
+
if (goneIndex < 0) {
|
|
742
|
+
return call;
|
|
743
|
+
}
|
|
744
|
+
call = withoutDependencyAt(call, block, goneIndex);
|
|
745
|
+
// A removal shifts the indices of the dependencies after it, so the block is
|
|
746
|
+
// re-derived from `call` rather than reused with stale offsets.
|
|
747
|
+
const next = amdBlockOf(call, callees);
|
|
748
|
+
if (next === undefined) {
|
|
749
|
+
return call;
|
|
750
|
+
}
|
|
751
|
+
block = next;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
return await sweeper.visit(after, ctx);
|
|
756
|
+
}
|
|
757
|
+
/** The block's parameter names that its body actually references, from a single walk of it. */
|
|
758
|
+
async function usedBindings(block) {
|
|
759
|
+
const usedNames = await namesUsed(block, false);
|
|
760
|
+
const used = new Set();
|
|
761
|
+
for (const binding of parameterNames(block)) {
|
|
762
|
+
if (binding !== undefined && usedNames.has(binding)) {
|
|
763
|
+
used.add(binding);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return used;
|
|
767
|
+
}
|
|
768
|
+
//# sourceMappingURL=amd.js.map
|