@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
|
@@ -236,7 +236,8 @@ export class Pattern {
|
|
|
236
236
|
// A capture's type reaches the parse as a declaration, so it shapes the tree the same
|
|
237
237
|
// way an explicit context statement does and belongs in the key alongside one.
|
|
238
238
|
[...contextStatements, ...TemplateEngine.capturePreamble(this.captures)],
|
|
239
|
-
this._options.dependencies || {}
|
|
239
|
+
this._options.dependencies || {},
|
|
240
|
+
this._options.types
|
|
240
241
|
);
|
|
241
242
|
|
|
242
243
|
// Level 2: Global cache (fast path - shared with Template)
|
|
@@ -247,7 +248,8 @@ export class Pattern {
|
|
|
247
248
|
this.templateParts,
|
|
248
249
|
this.captures,
|
|
249
250
|
contextStatements,
|
|
250
|
-
this._options.dependencies || {}
|
|
251
|
+
this._options.dependencies || {},
|
|
252
|
+
this._options.types
|
|
251
253
|
);
|
|
252
254
|
globalAstCache.set(cacheKey, tree);
|
|
253
255
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {Cursor, isTree} from '../..';
|
|
16
|
+
import {Cursor, isTree, Markers} from '../..';
|
|
17
17
|
import {J} from '../../java';
|
|
18
18
|
import {JS} from '..';
|
|
19
19
|
import {JavaScriptVisitor} from '../visitor';
|
|
@@ -42,7 +42,7 @@ export class PlaceholderReplacementVisitor extends JavaScriptVisitor<any> {
|
|
|
42
42
|
const replacement = this.replacePlaceholder(tree);
|
|
43
43
|
if (replacement !== tree) {
|
|
44
44
|
// `this.cursor` is still the enclosing template node: `super.visit()` has not pushed this one
|
|
45
|
-
return maybeParenthesize(enclosingTree(parent ?? this.cursor), tree.id, replacement,
|
|
45
|
+
return maybeParenthesize(enclosingTree(parent ?? this.cursor), tree.id, replacement, tree.markers) as R;
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -181,6 +181,16 @@ export class PlaceholderReplacementVisitor extends JavaScriptVisitor<any> {
|
|
|
181
181
|
sourcePrefix : this.mergePrefix(sourcePrefix, templatePrefix);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
/** As `mergePrefix`, for markers: everything the value was matched with, plus the kinds only the slot wrote. */
|
|
185
|
+
private mergeMarkers(sourceMarkers: Markers, templateMarkers: Markers): Markers {
|
|
186
|
+
const added = templateMarkers.markers.filter(
|
|
187
|
+
template => !sourceMarkers.markers.some(source => source.kind === template.kind));
|
|
188
|
+
return added.length === 0 ? sourceMarkers : {
|
|
189
|
+
...sourceMarkers,
|
|
190
|
+
markers: [...sourceMarkers.markers, ...added]
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
184
194
|
/**
|
|
185
195
|
* Expands variadic placeholders in a list of elements.
|
|
186
196
|
*
|
|
@@ -386,7 +396,7 @@ export class PlaceholderReplacementVisitor extends JavaScriptVisitor<any> {
|
|
|
386
396
|
if (isTree(propertyValue)) {
|
|
387
397
|
const propValueAsJ = propertyValue as J;
|
|
388
398
|
return produce(propValueAsJ, draft => {
|
|
389
|
-
draft.markers = placeholder.markers;
|
|
399
|
+
draft.markers = this.mergeMarkers(propValueAsJ.markers, placeholder.markers);
|
|
390
400
|
draft.prefix = this.mergePrefix(propValueAsJ.prefix, placeholder.prefix);
|
|
391
401
|
});
|
|
392
402
|
}
|
|
@@ -421,7 +431,7 @@ export class PlaceholderReplacementVisitor extends JavaScriptVisitor<any> {
|
|
|
421
431
|
const matchedNode = this.values.get(name);
|
|
422
432
|
if (matchedNode && !Array.isArray(matchedNode)) {
|
|
423
433
|
return produce(matchedNode, draft => {
|
|
424
|
-
draft.markers = placeholder.markers;
|
|
434
|
+
draft.markers = this.mergeMarkers(matchedNode.markers, placeholder.markers);
|
|
425
435
|
draft.prefix = this.mergePrefix(matchedNode.prefix, placeholder.prefix);
|
|
426
436
|
});
|
|
427
437
|
}
|
|
@@ -438,7 +448,7 @@ export class PlaceholderReplacementVisitor extends JavaScriptVisitor<any> {
|
|
|
438
448
|
// Extract the element from the J.RightPadded wrapper
|
|
439
449
|
const element = param.value.element as J;
|
|
440
450
|
return produce(element, draft => {
|
|
441
|
-
draft.markers = placeholder.markers;
|
|
451
|
+
draft.markers = this.mergeMarkers(element.markers, placeholder.markers);
|
|
442
452
|
draft.prefix = this.mergePrefix(element.prefix, placeholder.prefix);
|
|
443
453
|
});
|
|
444
454
|
}
|
|
@@ -458,7 +468,7 @@ export class PlaceholderReplacementVisitor extends JavaScriptVisitor<any> {
|
|
|
458
468
|
if (isTree(param.value)) {
|
|
459
469
|
// Return the AST node, preserving comments from the source
|
|
460
470
|
return produce(param.value as J, draft => {
|
|
461
|
-
draft.markers = placeholder.markers;
|
|
471
|
+
draft.markers = this.mergeMarkers(param.value.markers, placeholder.markers);
|
|
462
472
|
draft.prefix = this.mergePrefix(param.value.prefix, placeholder.prefix);
|
|
463
473
|
});
|
|
464
474
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import {isTree} from '../..';
|
|
17
17
|
import {emptySpace, J} from '../../java';
|
|
18
|
-
import {emptyMarkers, Marker, markers} from '../../markers';
|
|
18
|
+
import {emptyMarkers, Marker, markers, Markers} from '../../markers';
|
|
19
19
|
import {randomId} from '../../uuid';
|
|
20
20
|
import {JS} from '..';
|
|
21
21
|
|
|
@@ -319,9 +319,12 @@ export function requiredPrecedence(parent: J, childId: string): number | undefin
|
|
|
319
319
|
return slotConstraints(parent, childId)?.precedence;
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
/**
|
|
322
|
+
/**
|
|
323
|
+
* Parenthesizes `expression` if the slot of `parent` holding `childId` would otherwise reparse it.
|
|
324
|
+
* `slotMarkers` are the markers the slot contributed; {@link parenthesize} says what they decide.
|
|
325
|
+
*/
|
|
323
326
|
export function maybeParenthesize(parent: J | undefined, childId: string, expression: J,
|
|
324
|
-
|
|
327
|
+
slotMarkers?: Markers): J {
|
|
325
328
|
if (!parent) {
|
|
326
329
|
return expression;
|
|
327
330
|
}
|
|
@@ -334,10 +337,10 @@ export function maybeParenthesize(parent: J | undefined, childId: string, expres
|
|
|
334
337
|
// A statement wrapper is transparent here: the parentheses belong around the expression
|
|
335
338
|
if (expression.kind === JS.Kind.ExpressionStatement) {
|
|
336
339
|
const inner = (expression as JS.ExpressionStatement).expression;
|
|
337
|
-
const wrapped = wrapIfNeeded(parent, childId, constraints, inner,
|
|
340
|
+
const wrapped = wrapIfNeeded(parent, childId, constraints, inner, slotMarkers);
|
|
338
341
|
return wrapped === inner ? expression : {...expression, expression: wrapped} as JS.ExpressionStatement;
|
|
339
342
|
}
|
|
340
|
-
return wrapIfNeeded(parent, childId, constraints, expression,
|
|
343
|
+
return wrapIfNeeded(parent, childId, constraints, expression, slotMarkers);
|
|
341
344
|
}
|
|
342
345
|
|
|
343
346
|
/** The nearest enclosing LST node in a cursor path, skipping the padding wrappers visitors push. */
|
|
@@ -352,18 +355,23 @@ export function enclosingTree(cursor: { value: any, parent?: any } | undefined):
|
|
|
352
355
|
return undefined;
|
|
353
356
|
}
|
|
354
357
|
|
|
355
|
-
/**
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
358
|
+
/**
|
|
359
|
+
* Wraps in `J.Parentheses`, moving the prefix out so the surrounding whitespace survives. A trailing marker
|
|
360
|
+
* `slotMarkers` also carries belongs to the slot around the expression, so it moves out too: `${x}!` with `x`
|
|
361
|
+
* bound to `a + b` gives `(a + b)!`, while a bare `${x}` bound to `a!` gives `(a!)`.
|
|
362
|
+
*/
|
|
363
|
+
export function parenthesize(expression: J, slotMarkers?: Markers): J.Parentheses<J> {
|
|
364
|
+
const hoisted = expression.markers.markers.filter(
|
|
365
|
+
m => isTrailingMarker(m) && slotMarkers?.markers.some(slot => slot.kind === m.kind));
|
|
366
|
+
const inner = hoisted.length === 0 ? expression : {
|
|
359
367
|
...expression,
|
|
360
|
-
markers: markers(...expression.markers.markers.filter(m => !
|
|
368
|
+
markers: markers(...expression.markers.markers.filter(m => !hoisted.includes(m)))
|
|
361
369
|
};
|
|
362
370
|
return {
|
|
363
371
|
kind: J.Kind.Parentheses,
|
|
364
372
|
id: randomId(),
|
|
365
373
|
prefix: expression.prefix,
|
|
366
|
-
markers:
|
|
374
|
+
markers: hoisted.length === 0 ? emptyMarkers : markers(...hoisted),
|
|
367
375
|
tree: {
|
|
368
376
|
kind: J.Kind.RightPadded,
|
|
369
377
|
element: {...inner, prefix: emptySpace},
|
|
@@ -374,7 +382,7 @@ export function parenthesize(expression: J, slotOwnsTrailingMarkers: boolean = f
|
|
|
374
382
|
}
|
|
375
383
|
|
|
376
384
|
function wrapIfNeeded(parent: J, childId: string, constraints: SlotConstraints, expression: J,
|
|
377
|
-
|
|
385
|
+
slotMarkers: Markers | undefined): J {
|
|
378
386
|
if (precedenceOf(expression) < constraints.precedence ||
|
|
379
387
|
(constraints.noCallShape && isCallShaped(expression)) ||
|
|
380
388
|
(constraints.noOptionalChain && hasOptionalChain(expression)) ||
|
|
@@ -383,7 +391,7 @@ function wrapIfNeeded(parent: J, childId: string, constraints: SlotConstraints,
|
|
|
383
391
|
(constraints.followedByDot && isDotAdjacentNumber(expression)) ||
|
|
384
392
|
mixesNullishWithLogical(parent, expression) ||
|
|
385
393
|
wouldFuseSigns(parent, childId, expression)) {
|
|
386
|
-
return parenthesize(expression,
|
|
394
|
+
return parenthesize(expression, slotMarkers);
|
|
387
395
|
}
|
|
388
396
|
return expression;
|
|
389
397
|
}
|
|
@@ -58,7 +58,15 @@ class RewriteRuleImpl implements RewriteRule {
|
|
|
58
58
|
let result: J | undefined;
|
|
59
59
|
|
|
60
60
|
const template = typeof this.after === 'function' ? this.after(match) : this.after;
|
|
61
|
-
const bindings = options?.bindings ??
|
|
61
|
+
const bindings = options?.bindings ??
|
|
62
|
+
(options?.visitor ? await template.resolveBindings(options.visitor) : undefined);
|
|
63
|
+
// Applying without them would splice the context's own names in unbound, which
|
|
64
|
+
// reads as a working edit and is not one.
|
|
65
|
+
if (bindings === undefined && await template.bindsModules()) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
"Template binds modules in its context, so applying it needs their local names. " +
|
|
68
|
+
"Pass {visitor: this} to tryOn, or bindings you resolved yourself.");
|
|
69
|
+
}
|
|
62
70
|
result = await template.apply(node, cursor,
|
|
63
71
|
{ values: match, format: this.format, bindings: bindings || undefined });
|
|
64
72
|
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
import {Cursor, Tree} from '../..';
|
|
17
17
|
import {J} from '../../java';
|
|
18
18
|
import {ApplyOptions, Parameter, TemplateOptions, TemplateParameter} from './types';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
19
|
+
import {maybeBind} from '../binding';
|
|
20
|
+
import {ContextBinding} from './engine';
|
|
21
21
|
import {JavaScriptVisitor} from '../visitor';
|
|
22
22
|
import {MatchResult} from './pattern';
|
|
23
23
|
import {generateCacheKey, globalAstCache, WRAPPERS_MAP_SYMBOL} from './utils';
|
|
@@ -177,6 +177,7 @@ export class TemplateBuilder {
|
|
|
177
177
|
export class Template {
|
|
178
178
|
private options: TemplateOptions = {};
|
|
179
179
|
private _cachedTemplate?: J;
|
|
180
|
+
private _contextBindings?: Promise<ContextBinding[]>;
|
|
180
181
|
|
|
181
182
|
/**
|
|
182
183
|
* Creates a new template.
|
|
@@ -224,6 +225,7 @@ export class Template {
|
|
|
224
225
|
this.options = {...this.options, ...options};
|
|
225
226
|
// Invalidate cache when configuration changes
|
|
226
227
|
this._cachedTemplate = undefined;
|
|
228
|
+
this._contextBindings = undefined;
|
|
227
229
|
return this;
|
|
228
230
|
}
|
|
229
231
|
|
|
@@ -240,20 +242,16 @@ export class Template {
|
|
|
240
242
|
* @returns The cached or newly computed template tree
|
|
241
243
|
* @internal
|
|
242
244
|
*/
|
|
243
|
-
private async getTemplateTree(): Promise<
|
|
245
|
+
private async getTemplateTree(): Promise<J> {
|
|
244
246
|
// Level 1: Instance cache (fastest path)
|
|
245
247
|
if (this._cachedTemplate) {
|
|
246
|
-
return this._cachedTemplate
|
|
248
|
+
return this._cachedTemplate;
|
|
247
249
|
}
|
|
248
250
|
|
|
249
251
|
// Generate cache key for global lookup
|
|
250
252
|
// For raw() parameters, we need to include their code values in the key
|
|
251
253
|
// since they're spliced at construction time, not application time
|
|
252
|
-
const contextStatements = [
|
|
253
|
-
...(this.options.context || this.options.imports || []),
|
|
254
|
-
...Object.entries(this.options.bindings ?? {})
|
|
255
|
-
.map(([name, b]) => bindingContextStatement(name, b, this.options.dependencies ?? {}))
|
|
256
|
-
];
|
|
254
|
+
const contextStatements = this.options.context || this.options.imports || [];
|
|
257
255
|
const parametersKey = this.parameters.map((p, i) => {
|
|
258
256
|
const value = p.value;
|
|
259
257
|
// Include raw code values in the cache key using the symbol
|
|
@@ -267,14 +265,15 @@ export class Template {
|
|
|
267
265
|
parametersKey,
|
|
268
266
|
// As in Pattern.getAstPattern: a parameter's type reaches the parse as a declaration
|
|
269
267
|
[...contextStatements, ...TemplateEngine.parameterPreamble(this.parameters)],
|
|
270
|
-
this.options.dependencies || {}
|
|
268
|
+
this.options.dependencies || {},
|
|
269
|
+
this.options.types
|
|
271
270
|
);
|
|
272
271
|
|
|
273
272
|
// Level 2: Global cache (fast path - shared with Pattern)
|
|
274
273
|
const cached = globalAstCache.get(cacheKey);
|
|
275
274
|
if (cached) {
|
|
276
|
-
this._cachedTemplate = cached
|
|
277
|
-
return cached
|
|
275
|
+
this._cachedTemplate = cached;
|
|
276
|
+
return cached;
|
|
278
277
|
}
|
|
279
278
|
|
|
280
279
|
// Level 3: Compute via TemplateEngine (slow path)
|
|
@@ -282,8 +281,9 @@ export class Template {
|
|
|
282
281
|
this.templateParts,
|
|
283
282
|
this.parameters,
|
|
284
283
|
contextStatements,
|
|
285
|
-
this.options.dependencies || {}
|
|
286
|
-
|
|
284
|
+
this.options.dependencies || {},
|
|
285
|
+
this.options.types
|
|
286
|
+
);
|
|
287
287
|
|
|
288
288
|
// Cache in both levels
|
|
289
289
|
globalAstCache.set(cacheKey, result);
|
|
@@ -298,18 +298,47 @@ export class Template {
|
|
|
298
298
|
* cannot resolve is bound whether or not the template goes on to reference it, so call this
|
|
299
299
|
* where the template is known to apply — {@link RewriteRule.tryOn} does, once a pattern matched.
|
|
300
300
|
*/
|
|
301
|
-
resolveBindings(visitor: JavaScriptVisitor<any>): Record<string, string
|
|
301
|
+
async resolveBindings(visitor: JavaScriptVisitor<any>): Promise<Record<string, string>> {
|
|
302
302
|
const resolved: Record<string, string> = {};
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
//
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
|
|
303
|
+
for (const binding of await this.contextBindings()) {
|
|
304
|
+
// Recognising the reference the template splices in takes attribution, so a module the
|
|
305
|
+
// workspace could not resolve is bound whether or not the template turns out to use it.
|
|
306
|
+
const onlyIfReferenced = binding.attributed;
|
|
307
|
+
const bound = maybeBind(visitor, {
|
|
308
|
+
module: binding.module!,
|
|
309
|
+
member: binding.member,
|
|
310
|
+
typeOnly: binding.typeOnly,
|
|
311
|
+
preferredName: binding.name,
|
|
312
|
+
onlyIfReferenced
|
|
313
|
+
});
|
|
314
|
+
// An unresolved binding is left out rather than recorded as `undefined`, so `apply()`'s
|
|
315
|
+
// own "applied without a local name" check catches it, same as a caller-omitted one.
|
|
316
|
+
if (bound !== undefined) {
|
|
317
|
+
resolved[binding.name] = bound;
|
|
318
|
+
}
|
|
309
319
|
}
|
|
310
320
|
return resolved;
|
|
311
321
|
}
|
|
312
322
|
|
|
323
|
+
/** Whether the context binds a module, which applying this template therefore has to resolve. */
|
|
324
|
+
async bindsModules(): Promise<boolean> {
|
|
325
|
+
return (await this.contextBindings()).length > 0;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** What this template's context statements bind, which is what it needs bound in the target file. */
|
|
329
|
+
private contextBindings(): Promise<ContextBinding[]> {
|
|
330
|
+
return this._contextBindings ??= this.deriveContextBindings();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private deriveContextBindings(): Promise<ContextBinding[]> {
|
|
334
|
+
return TemplateEngine.getContextBindings(
|
|
335
|
+
this.templateParts, this.parameters,
|
|
336
|
+
this.options.context || this.options.imports || [],
|
|
337
|
+
this.options.dependencies || {},
|
|
338
|
+
this.options.types
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
313
342
|
/**
|
|
314
343
|
* Applies this template and returns the resulting tree.
|
|
315
344
|
*
|
|
@@ -372,17 +401,21 @@ export class Template {
|
|
|
372
401
|
}
|
|
373
402
|
}
|
|
374
403
|
|
|
375
|
-
const declared = this.options.bindings ?? {};
|
|
376
404
|
const renames: Record<string, string> = {};
|
|
377
405
|
const modules: Record<string, string> = {};
|
|
378
|
-
for
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
406
|
+
// Supplying names is what asks for the context's modules to be bound in the file being
|
|
407
|
+
// edited; without them a context import only types the template.
|
|
408
|
+
if (options?.bindings !== undefined) {
|
|
409
|
+
for (const binding of await this.contextBindings()) {
|
|
410
|
+
const bound = options.bindings[binding.name];
|
|
411
|
+
if (bound === undefined) {
|
|
412
|
+
throw new Error(`Template binds '${binding.module}' in its context, but no local name was ` +
|
|
413
|
+
`given for '${binding.name}'. Either pass bindings from resolveBindings(visitor), or — if it ` +
|
|
414
|
+
`already did — binding was refused, which an AMD block or a file requiring its modules can do.`);
|
|
415
|
+
}
|
|
416
|
+
renames[binding.name] = bound;
|
|
417
|
+
modules[binding.name] = binding.module!;
|
|
383
418
|
}
|
|
384
|
-
renames[name] = bound;
|
|
385
|
-
modules[name] = binding.module;
|
|
386
419
|
}
|
|
387
420
|
|
|
388
421
|
// Use instance-level cache to get the template tree
|
|
@@ -18,7 +18,6 @@ import {J, Type} from '../../java';
|
|
|
18
18
|
import type {Pattern} from "./pattern";
|
|
19
19
|
import type {Template} from "./template";
|
|
20
20
|
import type {CaptureValue, RawCode} from "./capture";
|
|
21
|
-
import type {AddImportOptions} from "../add-import";
|
|
22
21
|
import type {JavaScriptVisitor} from "../visitor";
|
|
23
22
|
|
|
24
23
|
/**
|
|
@@ -350,6 +349,12 @@ export interface PatternOptions {
|
|
|
350
349
|
*/
|
|
351
350
|
dependencies?: Record<string, string>;
|
|
352
351
|
|
|
352
|
+
/**
|
|
353
|
+
* Type packages to load whose declarations nothing imports, as {@link TemplateOptions.types}.
|
|
354
|
+
* A pattern matches on attribution, so a module typed only ambiently needs this to match.
|
|
355
|
+
*/
|
|
356
|
+
types?: string[];
|
|
357
|
+
|
|
353
358
|
/**
|
|
354
359
|
* When true, allows patterns without type annotations to match code with type annotations.
|
|
355
360
|
* This enables more flexible pattern matching during development or when full type attribution
|
|
@@ -485,27 +490,15 @@ export interface TemplateOptions {
|
|
|
485
490
|
dependencies?: Record<string, string>;
|
|
486
491
|
|
|
487
492
|
/**
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
493
|
+
* Type packages to load whose declarations nothing imports. TypeScript reads `@types/*` on its
|
|
494
|
+
* own and everything else only when named here, so a package declaring its modules ambiently —
|
|
495
|
+
* rather than at a path matching the specifier — resolves only with this set.
|
|
491
496
|
*
|
|
492
|
-
* @example
|
|
493
|
-
* ```typescript
|
|
494
|
-
* template`Theming.setTheme(${capture('theme')})`
|
|
495
|
-
* .configure({bindings: {Theming: {module: 'sap/ui/core/Theming', member: 'default'}}})
|
|
496
|
-
* ```
|
|
497
|
+
* @example `{dependencies: {'@sapui5/types': '^1.120.0'}, types: ['@sapui5/types']}`
|
|
497
498
|
*/
|
|
498
|
-
|
|
499
|
+
types?: string[];
|
|
499
500
|
}
|
|
500
501
|
|
|
501
|
-
/**
|
|
502
|
-
* A module a template's code depends on. The local name comes from the key it is declared under,
|
|
503
|
-
* and {@link Template.resolveBindings} settles the rest. `member` and `typeOnly` shape the import
|
|
504
|
-
* it creates, so a caller passing {@link ApplyOptions.bindings} of its own reads neither.
|
|
505
|
-
*/
|
|
506
|
-
export type ModuleBinding = Omit<AddImportOptions,
|
|
507
|
-
'module' | 'alias' | 'preferredName' | 'onlyIfReferenced' | 'sideEffectOnly'> & { module: string };
|
|
508
|
-
|
|
509
502
|
/**
|
|
510
503
|
* Options for template application.
|
|
511
504
|
*/
|
|
@@ -107,13 +107,15 @@ export function generateCacheKey(
|
|
|
107
107
|
templateParts: string[] | TemplateStringsArray,
|
|
108
108
|
itemsKey: string,
|
|
109
109
|
contextStatements: string[],
|
|
110
|
-
dependencies: Record<string, string
|
|
110
|
+
dependencies: Record<string, string>,
|
|
111
|
+
types?: string[]
|
|
111
112
|
): string {
|
|
112
113
|
return [
|
|
113
114
|
Array.from(templateParts).join('|'),
|
|
114
115
|
itemsKey,
|
|
115
116
|
contextStatements.join(';'),
|
|
116
|
-
JSON.stringify(dependencies)
|
|
117
|
+
JSON.stringify(dependencies),
|
|
118
|
+
JSON.stringify(types ?? null)
|
|
117
119
|
].join('::');
|
|
118
120
|
}
|
|
119
121
|
|