@ethlete/cdk 4.70.0 → 5.0.0-next.0
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/CHANGELOG.md +36 -0
- package/fesm2022/ethlete-cdk.mjs +11055 -12514
- package/fesm2022/ethlete-cdk.mjs.map +1 -1
- package/generators/generators.json +9 -0
- package/generators/migrate-to-v5/cdk-menu.js +259 -0
- package/generators/migrate-to-v5/color-themes.js +221 -0
- package/generators/migrate-to-v5/combobox.js +186 -0
- package/generators/migrate-to-v5/dialog-bottom-sheet.js +1039 -0
- package/generators/migrate-to-v5/et-let.js +514 -0
- package/generators/migrate-to-v5/is-active-element.js +201 -0
- package/generators/migrate-to-v5/migration.js +53 -0
- package/generators/migrate-to-v5/overlay-positions.js +868 -0
- package/generators/migrate-to-v5/schema.json +49 -0
- package/package.json +20 -14
- package/src/lib/styles/cdk.css +1 -1
- package/{index.d.ts → types/ethlete-cdk.d.ts} +2248 -3617
|
@@ -0,0 +1,868 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
|
+
import { formatFiles, getProjects, logger, visitNotIgnoredFiles } from '@nx/devkit';
|
|
3
|
+
import * as ts from 'typescript';
|
|
4
|
+
const STRATEGY_MAP = {
|
|
5
|
+
dialog: 'dialogOverlayStrategy',
|
|
6
|
+
bottomSheet: 'bottomSheetOverlayStrategy',
|
|
7
|
+
leftSheet: 'leftSheetOverlayStrategy',
|
|
8
|
+
rightSheet: 'rightSheetOverlayStrategy',
|
|
9
|
+
topSheet: 'topSheetOverlayStrategy',
|
|
10
|
+
fullScreenDialog: 'fullScreenDialogOverlayStrategy',
|
|
11
|
+
anchoredDialog: 'anchoredDialogOverlayStrategy',
|
|
12
|
+
};
|
|
13
|
+
const STRATEGY_INJECT_MAP = {
|
|
14
|
+
dialog: 'injectDialogStrategy',
|
|
15
|
+
bottomSheet: 'injectBottomSheetStrategy',
|
|
16
|
+
leftSheet: 'injectLeftSheetStrategy',
|
|
17
|
+
rightSheet: 'injectRightSheetStrategy',
|
|
18
|
+
topSheet: 'injectTopSheetStrategy',
|
|
19
|
+
fullScreenDialog: 'injectFullscreenDialogStrategy',
|
|
20
|
+
anchoredDialog: 'injectAnchoredDialogStrategy',
|
|
21
|
+
};
|
|
22
|
+
const TRANSFORMING_PRESET_STRATEGIES = {
|
|
23
|
+
transformingBottomSheetToDialog: 'transformingBottomSheetToDialogOverlayStrategy',
|
|
24
|
+
transformingFullScreenDialogToRightSheet: 'transformingFullScreenDialogToRightSheetOverlayStrategy',
|
|
25
|
+
transformingFullScreenDialogToDialog: 'transformingFullScreenDialogToDialogOverlayStrategy',
|
|
26
|
+
};
|
|
27
|
+
export default async function migrateOverlayPositions(tree) {
|
|
28
|
+
logger.log('Starting overlay positions migration...');
|
|
29
|
+
const projects = getProjects(tree);
|
|
30
|
+
let filesChanged = 0;
|
|
31
|
+
for (const [, project] of projects) {
|
|
32
|
+
visitNotIgnoredFiles(tree, project.root, (filePath) => {
|
|
33
|
+
if (!shouldProcessFile(filePath, tree)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const content = tree.read(filePath, 'utf-8');
|
|
37
|
+
const newContent = processFile(filePath, content);
|
|
38
|
+
if (newContent !== content) {
|
|
39
|
+
tree.write(filePath, newContent);
|
|
40
|
+
filesChanged++;
|
|
41
|
+
logger.log(`✓ Migrated ${filePath}`);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (filesChanged > 0) {
|
|
46
|
+
logger.log(`✓ Successfully migrated ${filesChanged} file(s)`);
|
|
47
|
+
await formatFiles(tree);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
logger.log('No files needed migration');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function shouldProcessFile(filePath, tree) {
|
|
54
|
+
if (!filePath.endsWith('.ts') || filePath.endsWith('.spec.ts')) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const content = tree.read(filePath, 'utf-8');
|
|
58
|
+
if (!content)
|
|
59
|
+
return false;
|
|
60
|
+
// Quick check if file might need migration
|
|
61
|
+
return (content.includes('OverlayService') ||
|
|
62
|
+
content.includes('positions') ||
|
|
63
|
+
content.includes('OverlayBreakpointConfigEntry'));
|
|
64
|
+
}
|
|
65
|
+
function processFile(filePath, content) {
|
|
66
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
67
|
+
const changes = [];
|
|
68
|
+
const importsToAdd = new Set();
|
|
69
|
+
const importsToRemove = new Set();
|
|
70
|
+
// Track which OverlayService imports are from @ethlete/cdk
|
|
71
|
+
const ethleteOverlayServiceNames = new Set();
|
|
72
|
+
// First pass: identify OverlayService imports from @ethlete/cdk
|
|
73
|
+
function identifyEthleteOverlayService(node) {
|
|
74
|
+
if (ts.isImportDeclaration(node)) {
|
|
75
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
76
|
+
if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === '@ethlete/cdk') {
|
|
77
|
+
if (node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
|
|
78
|
+
node.importClause.namedBindings.elements.forEach((element) => {
|
|
79
|
+
if (element.name.text === 'OverlayService' || element.propertyName?.text === 'OverlayService') {
|
|
80
|
+
// Store the local name (could be aliased)
|
|
81
|
+
ethleteOverlayServiceNames.add(element.name.text);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
ts.forEachChild(node, identifyEthleteOverlayService);
|
|
88
|
+
}
|
|
89
|
+
identifyEthleteOverlayService(sourceFile);
|
|
90
|
+
// Helper to check if a type reference is for ethlete OverlayService
|
|
91
|
+
function isEthleteOverlayServiceType(node) {
|
|
92
|
+
if (ts.isIdentifier(node.typeName)) {
|
|
93
|
+
return ethleteOverlayServiceNames.has(node.typeName.text);
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
// Helper to check if inject() call is for ethlete OverlayService
|
|
98
|
+
function isEthleteOverlayServiceInject(node) {
|
|
99
|
+
if (ts.isIdentifier(node.expression) && node.expression.text === 'inject' && node.arguments.length === 1) {
|
|
100
|
+
const arg = node.arguments[0];
|
|
101
|
+
if (arg && ts.isIdentifier(arg)) {
|
|
102
|
+
return ethleteOverlayServiceNames.has(arg.text);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
// Visit the AST to find what needs to change
|
|
108
|
+
function visit(node) {
|
|
109
|
+
// Handle constructor injection of OverlayService - MUST BE FIRST
|
|
110
|
+
if (ts.isConstructorDeclaration(node)) {
|
|
111
|
+
const overlayServiceParams = [];
|
|
112
|
+
// Find all OverlayService parameters from @ethlete/cdk
|
|
113
|
+
node.parameters.forEach((param) => {
|
|
114
|
+
if (param.type && ts.isTypeReferenceNode(param.type) && isEthleteOverlayServiceType(param.type)) {
|
|
115
|
+
overlayServiceParams.push(param);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
if (overlayServiceParams.length > 0) {
|
|
119
|
+
// Find the class declaration
|
|
120
|
+
let classDecl;
|
|
121
|
+
let current = node.parent;
|
|
122
|
+
while (current) {
|
|
123
|
+
if (ts.isClassDeclaration(current)) {
|
|
124
|
+
classDecl = current;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
current = current.parent;
|
|
128
|
+
}
|
|
129
|
+
if (classDecl) {
|
|
130
|
+
const hasOnlyOverlayServiceParams = node.parameters.length === overlayServiceParams.length;
|
|
131
|
+
const hasNoBody = !node.body || node.body.statements.length === 0;
|
|
132
|
+
if (hasOnlyOverlayServiceParams && hasNoBody) {
|
|
133
|
+
// We're going to replace the entire constructor with field declarations
|
|
134
|
+
// Get the full range of the constructor including leading whitespace
|
|
135
|
+
const constructorFullStart = node.getFullStart();
|
|
136
|
+
const constructorEnd = node.getEnd();
|
|
137
|
+
// Find the end including trailing newline
|
|
138
|
+
const fullText = sourceFile.getFullText();
|
|
139
|
+
let endPos = constructorEnd;
|
|
140
|
+
while (endPos < fullText.length && (fullText[endPos] === ' ' || fullText[endPos] === '\t')) {
|
|
141
|
+
endPos++;
|
|
142
|
+
}
|
|
143
|
+
if (endPos < fullText.length && fullText[endPos] === '\n') {
|
|
144
|
+
endPos++;
|
|
145
|
+
}
|
|
146
|
+
// Get the indentation by looking at the constructor line
|
|
147
|
+
const constructorLineStart = fullText.lastIndexOf('\n', constructorFullStart) + 1;
|
|
148
|
+
const indent = fullText.substring(constructorLineStart, constructorFullStart);
|
|
149
|
+
// Build replacement with field declarations
|
|
150
|
+
const fieldDeclarations = overlayServiceParams
|
|
151
|
+
.map((param) => {
|
|
152
|
+
const paramName = param.name.getText(sourceFile);
|
|
153
|
+
const modifiers = param.modifiers?.map((m) => m.getText(sourceFile)).join(' ') || 'private';
|
|
154
|
+
return `${indent}${modifiers} ${paramName} = injectOverlayManager();`;
|
|
155
|
+
})
|
|
156
|
+
.join('\n');
|
|
157
|
+
// Replace the entire constructor with the field declarations
|
|
158
|
+
changes.push({
|
|
159
|
+
start: constructorFullStart,
|
|
160
|
+
end: endPos,
|
|
161
|
+
replacement: fieldDeclarations + '\n',
|
|
162
|
+
});
|
|
163
|
+
overlayServiceParams.forEach(() => {
|
|
164
|
+
importsToAdd.add('injectOverlayManager');
|
|
165
|
+
importsToRemove.add('OverlayService');
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
// Handle inject(OverlayService) - only if from @ethlete/cdk
|
|
173
|
+
if (ts.isCallExpression(node) && isEthleteOverlayServiceInject(node)) {
|
|
174
|
+
changes.push({
|
|
175
|
+
start: node.getStart(sourceFile),
|
|
176
|
+
end: node.getEnd(),
|
|
177
|
+
replacement: 'injectOverlayManager()',
|
|
178
|
+
});
|
|
179
|
+
importsToAdd.add('injectOverlayManager');
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
// Handle positions: property FIRST (before checking for method calls)
|
|
183
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && node.name.text === 'positions') {
|
|
184
|
+
// Check if this is a builder pattern: (builder) => ...
|
|
185
|
+
if (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) {
|
|
186
|
+
const func = node.initializer;
|
|
187
|
+
// Check if it has a parameter (the builder parameter)
|
|
188
|
+
if (func.parameters.length === 1) {
|
|
189
|
+
const builderParam = func.parameters[0];
|
|
190
|
+
const builderParamName = builderParam.name.getText(sourceFile);
|
|
191
|
+
// Check if the function body uses DEFAULTS
|
|
192
|
+
if (func.body && nodeContainsDefaults(func.body)) {
|
|
193
|
+
// Get the function body (array or block)
|
|
194
|
+
let bodyCode;
|
|
195
|
+
if (ts.isBlock(func.body)) {
|
|
196
|
+
// Function with block: (builder) => { return [...] }
|
|
197
|
+
bodyCode = func.body.getText(sourceFile);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// Arrow function with expression: (builder) => [...]
|
|
201
|
+
bodyCode = func.body.getText(sourceFile);
|
|
202
|
+
}
|
|
203
|
+
// Transform the body - replace builder.DEFAULTS.X with strategy calls
|
|
204
|
+
const { code: transformedBody, usesMergeConfigs, strategiesUsed, } = transformBuilderBodyForDefaults(bodyCode, builderParamName, sourceFile);
|
|
205
|
+
if (usesMergeConfigs) {
|
|
206
|
+
importsToAdd.add('mergeOverlayBreakpointConfigs');
|
|
207
|
+
}
|
|
208
|
+
// Build the factory function with inject statements
|
|
209
|
+
const injectStatements = Array.from(strategiesUsed)
|
|
210
|
+
.map((inject) => {
|
|
211
|
+
const varName = getStrategyVariableName(inject);
|
|
212
|
+
return `const ${varName} = ${inject}();`;
|
|
213
|
+
})
|
|
214
|
+
.join('\n ');
|
|
215
|
+
const factoryCode = `() => {
|
|
216
|
+
${injectStatements}
|
|
217
|
+
return ${transformedBody};
|
|
218
|
+
}`;
|
|
219
|
+
changes.push({
|
|
220
|
+
start: node.name.getStart(sourceFile),
|
|
221
|
+
end: node.initializer.getEnd(),
|
|
222
|
+
replacement: `strategies: ${factoryCode}`,
|
|
223
|
+
});
|
|
224
|
+
// Add all strategy inject functions to imports
|
|
225
|
+
strategiesUsed.forEach((inject) => importsToAdd.add(inject));
|
|
226
|
+
return false; // Don't visit children
|
|
227
|
+
}
|
|
228
|
+
// Not using DEFAULTS, just transform the builder pattern normally
|
|
229
|
+
const transformedValue = transformBuilderPattern(node.initializer.getText(sourceFile), importsToAdd);
|
|
230
|
+
if (transformedValue !== node.initializer.getText(sourceFile)) {
|
|
231
|
+
changes.push({
|
|
232
|
+
start: node.name.getStart(sourceFile),
|
|
233
|
+
end: node.initializer.getEnd(),
|
|
234
|
+
replacement: `strategies: ${transformedValue}`,
|
|
235
|
+
});
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Handle DEFAULTS case (not in builder pattern)
|
|
241
|
+
if (nodeContainsDefaults(node.initializer)) {
|
|
242
|
+
const strategyInjects = new Set();
|
|
243
|
+
trackDefaultsUsageInNode(node.initializer, strategyInjects);
|
|
244
|
+
const factoryCode = generateFactoryFunctionCode(node.initializer, sourceFile, strategyInjects, importsToAdd);
|
|
245
|
+
changes.push({
|
|
246
|
+
start: node.name.getStart(sourceFile),
|
|
247
|
+
end: node.initializer.getEnd(),
|
|
248
|
+
replacement: `strategies: ${factoryCode}`,
|
|
249
|
+
});
|
|
250
|
+
strategyInjects.forEach((inject) => importsToAdd.add(inject));
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
// Check if value directly uses overlay positions API
|
|
254
|
+
const directlyUsesPositionsApi = node.initializer.getText(sourceFile).includes('.positions.') ||
|
|
255
|
+
node.initializer.getText(sourceFile).includes('builder.');
|
|
256
|
+
if (directlyUsesPositionsApi) {
|
|
257
|
+
const originalValue = node.initializer.getText(sourceFile);
|
|
258
|
+
const transformedValue = transformPositionCalls(originalValue, importsToAdd);
|
|
259
|
+
if (transformedValue !== originalValue) {
|
|
260
|
+
changes.push({
|
|
261
|
+
start: node.name.getStart(sourceFile),
|
|
262
|
+
end: node.initializer.getEnd(),
|
|
263
|
+
replacement: `strategies: ${transformedValue}`,
|
|
264
|
+
});
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// For any other positions: property inside overlay calls, just rename it
|
|
269
|
+
if (isInsideOverlayHandlerCall(node) || isInsideOverlayOpenCall(node)) {
|
|
270
|
+
changes.push({
|
|
271
|
+
start: node.name.getStart(sourceFile),
|
|
272
|
+
end: node.name.getEnd(),
|
|
273
|
+
replacement: 'strategies',
|
|
274
|
+
});
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Handle class property declarations with DEFAULTS
|
|
279
|
+
if (ts.isPropertyDeclaration(node) && node.initializer) {
|
|
280
|
+
// Check if the type is OverlayBreakpointConfigEntry[]
|
|
281
|
+
if (node.type && isOverlayBreakpointConfigEntryArrayType(node.type) && nodeContainsDefaults(node.initializer)) {
|
|
282
|
+
const strategyInjects = new Set();
|
|
283
|
+
trackDefaultsUsageInNode(node.initializer, strategyInjects);
|
|
284
|
+
// Update the type to () => OverlayStrategyBreakpoint[]
|
|
285
|
+
if (ts.isArrayTypeNode(node.type)) {
|
|
286
|
+
changes.push({
|
|
287
|
+
start: node.type.getStart(sourceFile),
|
|
288
|
+
end: node.type.getEnd(),
|
|
289
|
+
replacement: '() => OverlayStrategyBreakpoint[]',
|
|
290
|
+
});
|
|
291
|
+
importsToAdd.add('OverlayStrategyBreakpoint');
|
|
292
|
+
}
|
|
293
|
+
// Wrap the initializer in a factory function
|
|
294
|
+
const factoryCode = generateFactoryFunctionCode(node.initializer, sourceFile, strategyInjects, importsToAdd);
|
|
295
|
+
changes.push({
|
|
296
|
+
start: node.initializer.getStart(sourceFile),
|
|
297
|
+
end: node.initializer.getEnd(),
|
|
298
|
+
replacement: factoryCode,
|
|
299
|
+
});
|
|
300
|
+
strategyInjects.forEach((inject) => importsToAdd.add(inject));
|
|
301
|
+
return false; // Don't visit children
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Handle overlayService.positions.method() calls (only if not already handled above)
|
|
305
|
+
if (isPositionMethodCall(node)) {
|
|
306
|
+
// Check if this node is inside a return statement that contains DEFAULTS
|
|
307
|
+
let current = node.parent;
|
|
308
|
+
let insideDefaultsReturn = false;
|
|
309
|
+
while (current) {
|
|
310
|
+
if (ts.isReturnStatement(current) && current.expression) {
|
|
311
|
+
if (nodeContainsDefaults(current.expression)) {
|
|
312
|
+
insideDefaultsReturn = true;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
current = current.parent;
|
|
317
|
+
}
|
|
318
|
+
// Skip processing if we're inside a DEFAULTS return statement
|
|
319
|
+
// (it will be handled by the factory function transformation)
|
|
320
|
+
if (insideDefaultsReturn) {
|
|
321
|
+
return false; // Don't process this node or its children
|
|
322
|
+
}
|
|
323
|
+
const callExpr = node;
|
|
324
|
+
const propAccess = callExpr.expression;
|
|
325
|
+
const methodName = propAccess.name.text;
|
|
326
|
+
let replacement;
|
|
327
|
+
if (methodName in TRANSFORMING_PRESET_STRATEGIES) {
|
|
328
|
+
const strategyFn = TRANSFORMING_PRESET_STRATEGIES[methodName];
|
|
329
|
+
replacement = `${strategyFn}(${callExpr.arguments.map((arg) => arg.getText(sourceFile)).join(', ')})`;
|
|
330
|
+
importsToAdd.add(strategyFn);
|
|
331
|
+
}
|
|
332
|
+
else if (methodName === 'mergeConfigs') {
|
|
333
|
+
replacement = `mergeOverlayBreakpointConfigs(${callExpr.arguments.map((arg) => arg.getText(sourceFile)).join(', ')})`;
|
|
334
|
+
importsToAdd.add('mergeOverlayBreakpointConfigs');
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
const strategyFn = STRATEGY_MAP[methodName];
|
|
338
|
+
if (strategyFn) {
|
|
339
|
+
replacement = `${strategyFn}(${callExpr.arguments.map((arg) => arg.getText(sourceFile)).join(', ')})`;
|
|
340
|
+
importsToAdd.add(strategyFn);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
changes.push({
|
|
347
|
+
start: callExpr.getStart(sourceFile),
|
|
348
|
+
end: callExpr.getEnd(),
|
|
349
|
+
replacement,
|
|
350
|
+
});
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
// Handle OverlayBreakpointConfigEntry type
|
|
354
|
+
if (isOverlayBreakpointConfigEntryType(node)) {
|
|
355
|
+
const typeNode = node;
|
|
356
|
+
let parentFunc;
|
|
357
|
+
let current = node.parent;
|
|
358
|
+
while (current) {
|
|
359
|
+
if (ts.isMethodDeclaration(current) || ts.isFunctionDeclaration(current)) {
|
|
360
|
+
parentFunc = current;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
current = current.parent;
|
|
364
|
+
}
|
|
365
|
+
if (parentFunc?.body && nodeContainsDefaults(parentFunc.body)) {
|
|
366
|
+
// Skip - will be handled by function return type logic
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
changes.push({
|
|
370
|
+
start: typeNode.getStart(sourceFile),
|
|
371
|
+
end: typeNode.getEnd(),
|
|
372
|
+
replacement: 'OverlayStrategyBreakpoint',
|
|
373
|
+
});
|
|
374
|
+
importsToAdd.add('OverlayStrategyBreakpoint');
|
|
375
|
+
}
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
// Handle function return types
|
|
379
|
+
// In the visit function, find this section and update it:
|
|
380
|
+
if ((ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) && node.body && node.type) {
|
|
381
|
+
if (nodeContainsDefaults(node.body) && isOverlayBreakpointConfigEntryArrayType(node.type)) {
|
|
382
|
+
const strategyInjects = new Set();
|
|
383
|
+
trackDefaultsUsageInNode(node.body, strategyInjects);
|
|
384
|
+
const returnStatements = [];
|
|
385
|
+
function findReturns(n) {
|
|
386
|
+
if (ts.isReturnStatement(n) && n.expression) {
|
|
387
|
+
returnStatements.push(n);
|
|
388
|
+
}
|
|
389
|
+
ts.forEachChild(n, findReturns);
|
|
390
|
+
}
|
|
391
|
+
findReturns(node.body);
|
|
392
|
+
if (returnStatements.length > 0) {
|
|
393
|
+
if (ts.isArrayTypeNode(node.type)) {
|
|
394
|
+
changes.push({
|
|
395
|
+
start: node.type.getStart(sourceFile),
|
|
396
|
+
end: node.type.getEnd(),
|
|
397
|
+
replacement: '() => OverlayStrategyBreakpoint[]',
|
|
398
|
+
});
|
|
399
|
+
importsToAdd.add('OverlayStrategyBreakpoint');
|
|
400
|
+
}
|
|
401
|
+
returnStatements.forEach((ret) => {
|
|
402
|
+
if (ret.expression) {
|
|
403
|
+
const factoryCode = generateFactoryFunctionCode(ret.expression, sourceFile, strategyInjects, importsToAdd);
|
|
404
|
+
// We need to be very precise about what we're replacing
|
|
405
|
+
// Replace ONLY from "return" to the end of the expression (not including semicolon)
|
|
406
|
+
const returnKeywordStart = ret.getStart(sourceFile); // Start of 'return' keyword
|
|
407
|
+
const expressionEnd = ret.expression.getEnd(); // End of the array expression
|
|
408
|
+
changes.push({
|
|
409
|
+
start: returnKeywordStart,
|
|
410
|
+
end: expressionEnd,
|
|
411
|
+
replacement: `return ${factoryCode}`,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
strategyInjects.forEach((inject) => importsToAdd.add(inject));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
// Handle imports
|
|
421
|
+
if (ts.isImportDeclaration(node)) {
|
|
422
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
423
|
+
if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === '@ethlete/cdk') {
|
|
424
|
+
if (node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
|
|
425
|
+
node.importClause.namedBindings.elements.forEach((element) => {
|
|
426
|
+
if (element.name.text === 'OverlayService') {
|
|
427
|
+
importsToRemove.add('OverlayService');
|
|
428
|
+
importsToAdd.add('injectOverlayManager');
|
|
429
|
+
}
|
|
430
|
+
if (element.name.text === 'OverlayBreakpointConfigEntry') {
|
|
431
|
+
importsToRemove.add('OverlayBreakpointConfigEntry');
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
function visitNode(node) {
|
|
441
|
+
const shouldContinue = visit(node);
|
|
442
|
+
if (shouldContinue) {
|
|
443
|
+
ts.forEachChild(node, visitNode);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
visitNode(sourceFile);
|
|
447
|
+
// Apply changes in reverse order (from end to start) to maintain positions
|
|
448
|
+
changes.sort((a, b) => b.start - a.start);
|
|
449
|
+
for (let i = 0; i < changes.length - 1; i++) {
|
|
450
|
+
const current = changes[i];
|
|
451
|
+
const next = changes[i + 1];
|
|
452
|
+
if (current.start < next.end) {
|
|
453
|
+
logger.error(`Overlapping changes detected!`);
|
|
454
|
+
logger.error(`Change 1: ${current.start}-${current.end} = "${current.replacement}"`);
|
|
455
|
+
logger.error(`Change 2: ${next.start}-${next.end} = "${next.replacement}"`);
|
|
456
|
+
throw new Error('Overlapping changes detected - this will corrupt the file');
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
let result = content;
|
|
460
|
+
for (const change of changes) {
|
|
461
|
+
result = result.substring(0, change.start) + change.replacement + result.substring(change.end);
|
|
462
|
+
}
|
|
463
|
+
// Update imports
|
|
464
|
+
result = updateImportsInContent(result, importsToAdd, importsToRemove);
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
function transformNodeTextForDefaults(node, sourceFile) {
|
|
468
|
+
let text = node.getText(sourceFile);
|
|
469
|
+
let usesMergeConfigs = false;
|
|
470
|
+
// Check if mergeConfigs exists BEFORE any transformations
|
|
471
|
+
if (text.includes('.positions.mergeConfigs(')) {
|
|
472
|
+
usesMergeConfigs = true;
|
|
473
|
+
}
|
|
474
|
+
// Replace config: with strategy:
|
|
475
|
+
text = text.replace(/\bconfig:/g, 'strategy:');
|
|
476
|
+
// Replace DEFAULTS references with strategy.build() calls
|
|
477
|
+
Object.entries(STRATEGY_INJECT_MAP).forEach(([strategyName, injectFn]) => {
|
|
478
|
+
const varName = getStrategyVariableName(injectFn);
|
|
479
|
+
// Replace: overlayService.positions.DEFAULTS.dialog
|
|
480
|
+
// With: dialogStrategy.build()
|
|
481
|
+
const defaultsPattern = new RegExp(`(?:this\\.)?\\w+\\.positions\\.DEFAULTS\\.${strategyName}`, 'g');
|
|
482
|
+
text = text.replace(defaultsPattern, `${varName}.build()`);
|
|
483
|
+
});
|
|
484
|
+
// Now handle mergeConfigs - do this AFTER replacing DEFAULTS
|
|
485
|
+
Object.entries(STRATEGY_INJECT_MAP).forEach(([strategyName, injectFn]) => {
|
|
486
|
+
const varName = getStrategyVariableName(injectFn);
|
|
487
|
+
// Pattern: overlayService.positions.mergeConfigs(dialogStrategy.build(), ...args)
|
|
488
|
+
// We want to extract just the args after the first argument and comma
|
|
489
|
+
const pattern = new RegExp(`(?:this\\.)?\\w+\\.positions\\.mergeConfigs\\(\\s*${varName}\\.build\\(\\)\\s*,\\s*`, 'g');
|
|
490
|
+
let match;
|
|
491
|
+
const replacements = [];
|
|
492
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
493
|
+
const matchStart = match.index;
|
|
494
|
+
const afterComma = match.index + match[0].length;
|
|
495
|
+
// Find the closing paren of mergeConfigs by counting parentheses
|
|
496
|
+
let parenCount = 1;
|
|
497
|
+
let pos = afterComma;
|
|
498
|
+
while (pos < text.length && parenCount > 0) {
|
|
499
|
+
if (text[pos] === '(')
|
|
500
|
+
parenCount++;
|
|
501
|
+
if (text[pos] === ')')
|
|
502
|
+
parenCount--;
|
|
503
|
+
pos++;
|
|
504
|
+
}
|
|
505
|
+
// Extract the arguments (everything between afterComma and the closing paren)
|
|
506
|
+
const closingParenPos = pos - 1;
|
|
507
|
+
const args = text.substring(afterComma, closingParenPos).trim();
|
|
508
|
+
// Remove trailing comma if present
|
|
509
|
+
const cleanedArgs = args.replace(/,\s*$/, '');
|
|
510
|
+
// Build replacement
|
|
511
|
+
const replacement = `${varName}.build(mergeOverlayBreakpointConfigs(${cleanedArgs}))`;
|
|
512
|
+
replacements.push({
|
|
513
|
+
start: matchStart,
|
|
514
|
+
end: pos,
|
|
515
|
+
replacement,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
// Apply replacements in reverse order to maintain positions
|
|
519
|
+
replacements.reverse().forEach(({ start, end, replacement }) => {
|
|
520
|
+
text = text.substring(0, start) + replacement + text.substring(end);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
// Replace any remaining overlayService.positions.mergeConfigs calls
|
|
524
|
+
text = text.replace(/(?:this\.)?\w+\.positions\.mergeConfigs\(/g, 'mergeOverlayBreakpointConfigs(');
|
|
525
|
+
return { code: text, usesMergeConfigs };
|
|
526
|
+
}
|
|
527
|
+
function isInsideOverlayOpenCall(node) {
|
|
528
|
+
let current = node;
|
|
529
|
+
while (current) {
|
|
530
|
+
if (ts.isCallExpression(current)) {
|
|
531
|
+
const expr = current.expression;
|
|
532
|
+
// Match any .open call (not just overlayService)
|
|
533
|
+
if (ts.isPropertyAccessExpression(expr) && expr.name.text === 'open') {
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
current = current.parent;
|
|
538
|
+
}
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
function generateFactoryFunctionCode(node, sourceFile, strategyInjects, importsToAdd) {
|
|
542
|
+
const { code: transformedCode, usesMergeConfigs } = transformNodeTextForDefaults(node, sourceFile);
|
|
543
|
+
if (usesMergeConfigs) {
|
|
544
|
+
importsToAdd.add('mergeOverlayBreakpointConfigs');
|
|
545
|
+
}
|
|
546
|
+
const injectStatements = Array.from(strategyInjects)
|
|
547
|
+
.map((inject) => {
|
|
548
|
+
const varName = getStrategyVariableName(inject);
|
|
549
|
+
return `const ${varName} = ${inject}();`;
|
|
550
|
+
})
|
|
551
|
+
.join('\n ');
|
|
552
|
+
// Build the factory function with proper indentation and closing braces
|
|
553
|
+
return `() => {
|
|
554
|
+
${injectStatements}
|
|
555
|
+
return ${transformedCode};
|
|
556
|
+
}`;
|
|
557
|
+
}
|
|
558
|
+
function trackDefaultsUsageInNode(node, strategyInjects) {
|
|
559
|
+
function visit(n) {
|
|
560
|
+
if (ts.isPropertyAccessExpression(n) &&
|
|
561
|
+
ts.isPropertyAccessExpression(n.expression) &&
|
|
562
|
+
ts.isPropertyAccessExpression(n.expression.expression) &&
|
|
563
|
+
n.expression.expression.name.text === 'positions' &&
|
|
564
|
+
n.expression.name.text === 'DEFAULTS') {
|
|
565
|
+
const strategyName = n.name.text;
|
|
566
|
+
const injectFn = STRATEGY_INJECT_MAP[strategyName];
|
|
567
|
+
if (injectFn) {
|
|
568
|
+
strategyInjects.add(injectFn);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
ts.forEachChild(n, visit);
|
|
572
|
+
}
|
|
573
|
+
visit(node);
|
|
574
|
+
}
|
|
575
|
+
function updateImportsInContent(content, importsToAdd, importsToRemove) {
|
|
576
|
+
const importRegex = /import\s*{\s*([^}]+)\s*}\s*from\s*['"]@ethlete\/cdk['"]/;
|
|
577
|
+
const match = content.match(importRegex);
|
|
578
|
+
if (!match) {
|
|
579
|
+
if (importsToAdd.size > 0) {
|
|
580
|
+
const newImport = `import { ${Array.from(importsToAdd).sort().join(', ')} } from '@ethlete/cdk';\n`;
|
|
581
|
+
return newImport + content;
|
|
582
|
+
}
|
|
583
|
+
return content;
|
|
584
|
+
}
|
|
585
|
+
const existingImports = match[1]
|
|
586
|
+
.split(',')
|
|
587
|
+
.map((i) => i.trim())
|
|
588
|
+
.filter((i) => {
|
|
589
|
+
if (!i)
|
|
590
|
+
return false;
|
|
591
|
+
// Remove old imports
|
|
592
|
+
if (importsToRemove.has(i))
|
|
593
|
+
return false;
|
|
594
|
+
// Remove transforming presets if we're adding the OverlayStrategy versions
|
|
595
|
+
if (i in TRANSFORMING_PRESET_STRATEGIES) {
|
|
596
|
+
const strategyVersion = TRANSFORMING_PRESET_STRATEGIES[i];
|
|
597
|
+
if (importsToAdd.has(strategyVersion)) {
|
|
598
|
+
return false; // Remove the old version
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return true;
|
|
602
|
+
});
|
|
603
|
+
const allImports = [...new Set([...existingImports, ...Array.from(importsToAdd)])].sort();
|
|
604
|
+
if (allImports.length === 0) {
|
|
605
|
+
return content.replace(importRegex, '');
|
|
606
|
+
}
|
|
607
|
+
const newImportStatement = `import { ${allImports.join(', ')} } from '@ethlete/cdk'`;
|
|
608
|
+
return content.replace(importRegex, newImportStatement);
|
|
609
|
+
}
|
|
610
|
+
// Type guards
|
|
611
|
+
function isInjectOverlayServiceCall(node) {
|
|
612
|
+
return (ts.isCallExpression(node) &&
|
|
613
|
+
ts.isIdentifier(node.expression) &&
|
|
614
|
+
node.expression.text === 'inject' &&
|
|
615
|
+
node.arguments.length === 1 &&
|
|
616
|
+
ts.isIdentifier(node.arguments[0]) &&
|
|
617
|
+
node.arguments[0].text === 'OverlayService');
|
|
618
|
+
}
|
|
619
|
+
function isPositionMethodCall(node) {
|
|
620
|
+
return (ts.isCallExpression(node) &&
|
|
621
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
622
|
+
ts.isPropertyAccessExpression(node.expression.expression) &&
|
|
623
|
+
node.expression.expression.name.text === 'positions');
|
|
624
|
+
}
|
|
625
|
+
function isOverlayBreakpointConfigEntryType(node) {
|
|
626
|
+
return (ts.isTypeReferenceNode(node) &&
|
|
627
|
+
ts.isIdentifier(node.typeName) &&
|
|
628
|
+
node.typeName.text === 'OverlayBreakpointConfigEntry');
|
|
629
|
+
}
|
|
630
|
+
// Helper functions
|
|
631
|
+
function checkIfOverlayRelated(node) {
|
|
632
|
+
// First check: Are we inside createOverlayHandler call?
|
|
633
|
+
if (isInsideOverlayHandlerCall(node)) {
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
// Check for overlay-related patterns that need migration using AST
|
|
637
|
+
let isOverlayRelated = false;
|
|
638
|
+
function visit(n) {
|
|
639
|
+
if (isOverlayRelated)
|
|
640
|
+
return;
|
|
641
|
+
if (isPositionMethodCall(n)) {
|
|
642
|
+
isOverlayRelated = true;
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
if (isDefaultsUsage(n)) {
|
|
646
|
+
isOverlayRelated = true;
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (ts.isCallExpression(n)) {
|
|
650
|
+
const callText = n.getText();
|
|
651
|
+
if (callText.includes('getConfig') || callText.includes('getPositions') || callText.includes('getStrategies')) {
|
|
652
|
+
isOverlayRelated = true;
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
ts.forEachChild(n, visit);
|
|
657
|
+
}
|
|
658
|
+
visit(node);
|
|
659
|
+
return isOverlayRelated;
|
|
660
|
+
}
|
|
661
|
+
function isDefaultsUsage(node) {
|
|
662
|
+
return (ts.isPropertyAccessExpression(node) &&
|
|
663
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
664
|
+
node.expression.name.text === 'DEFAULTS');
|
|
665
|
+
}
|
|
666
|
+
// Remove TRANSFORMING_PRESET_STRATEGIES constant - we don't need it
|
|
667
|
+
// The transforming presets keep their original names
|
|
668
|
+
// Update the transformPositionCalls function
|
|
669
|
+
// The TRANSFORMING_PRESET_STRATEGIES constant is correct:
|
|
670
|
+
// But in transformPositionCalls, we're using the wrong approach for builder patterns
|
|
671
|
+
// The builder pattern should ALSO use the strategy name with suffix
|
|
672
|
+
function transformPositionCalls(text, importsToAdd) {
|
|
673
|
+
let result = text;
|
|
674
|
+
// Handle arrow functions with builder pattern: (paramName) => paramName.method()
|
|
675
|
+
const builderArrowMatch = result.match(/\((\w+)\)\s*=>\s*\1\.(\w+)\(/);
|
|
676
|
+
if (builderArrowMatch) {
|
|
677
|
+
const paramName = builderArrowMatch[1];
|
|
678
|
+
const methodName = builderArrowMatch[2];
|
|
679
|
+
let strategyFn;
|
|
680
|
+
// Check if it's a transforming preset
|
|
681
|
+
if (methodName in TRANSFORMING_PRESET_STRATEGIES) {
|
|
682
|
+
strategyFn = TRANSFORMING_PRESET_STRATEGIES[methodName];
|
|
683
|
+
importsToAdd.add(strategyFn);
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
// Regular strategy methods get mapped to their strategy functions
|
|
687
|
+
strategyFn = STRATEGY_MAP[methodName];
|
|
688
|
+
if (strategyFn) {
|
|
689
|
+
importsToAdd.add(strategyFn);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
if (strategyFn) {
|
|
693
|
+
const regex = new RegExp(`\\(${paramName}\\)\\s*=>\\s*${paramName}\\.\\w+\\(`, 'g');
|
|
694
|
+
result = result.replace(regex, `${strategyFn}(`);
|
|
695
|
+
return result;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
// Handle regular method calls: overlayService.positions.method()
|
|
699
|
+
Object.entries(STRATEGY_MAP).forEach(([method, strategy]) => {
|
|
700
|
+
const pattern = new RegExp(`(?:this\\.)?\\w+\\.positions\\.${method}\\(`, 'g');
|
|
701
|
+
result = result.replace(pattern, `${strategy}(`);
|
|
702
|
+
if (result.includes(strategy)) {
|
|
703
|
+
importsToAdd.add(strategy);
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
// Handle transforming presets - use the OverlayStrategy suffix
|
|
707
|
+
Object.entries(TRANSFORMING_PRESET_STRATEGIES).forEach(([preset, strategy]) => {
|
|
708
|
+
const pattern = new RegExp(`(?:this\\.)?\\w+\\.positions\\.${preset}\\(`, 'g');
|
|
709
|
+
result = result.replace(pattern, `${strategy}(`);
|
|
710
|
+
if (result.includes(strategy)) {
|
|
711
|
+
importsToAdd.add(strategy);
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
// Handle mergeConfigs
|
|
715
|
+
result = result.replace(/(?:this\.)?\w+\.positions\.mergeConfigs\(/g, 'mergeOverlayBreakpointConfigs(');
|
|
716
|
+
if (result.includes('mergeOverlayBreakpointConfigs')) {
|
|
717
|
+
importsToAdd.add('mergeOverlayBreakpointConfigs');
|
|
718
|
+
}
|
|
719
|
+
return result;
|
|
720
|
+
}
|
|
721
|
+
function nodeContainsDefaults(node) {
|
|
722
|
+
let found = false;
|
|
723
|
+
function visit(n) {
|
|
724
|
+
if (found)
|
|
725
|
+
return;
|
|
726
|
+
if (isDefaultsUsage(n)) {
|
|
727
|
+
found = true;
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
ts.forEachChild(n, visit);
|
|
731
|
+
}
|
|
732
|
+
visit(node);
|
|
733
|
+
return found;
|
|
734
|
+
}
|
|
735
|
+
function getStrategyVariableName(injectFn) {
|
|
736
|
+
const strategyName = injectFn.replace('inject', '').replace('Strategy', 'Strategy');
|
|
737
|
+
return strategyName.charAt(0).toLowerCase() + strategyName.slice(1);
|
|
738
|
+
}
|
|
739
|
+
function isOverlayBreakpointConfigEntryArrayType(type) {
|
|
740
|
+
return (ts.isArrayTypeNode(type) &&
|
|
741
|
+
ts.isTypeReferenceNode(type.elementType) &&
|
|
742
|
+
ts.isIdentifier(type.elementType.typeName) &&
|
|
743
|
+
type.elementType.typeName.text === 'OverlayBreakpointConfigEntry');
|
|
744
|
+
}
|
|
745
|
+
function isInsideOverlayHandlerCall(node) {
|
|
746
|
+
let current = node;
|
|
747
|
+
while (current) {
|
|
748
|
+
// Check if we're inside a call expression
|
|
749
|
+
if (ts.isCallExpression(current)) {
|
|
750
|
+
const expr = current.expression;
|
|
751
|
+
// Check if it's createOverlayHandler or createOverlayHandlerWithQueryParamLifecycle
|
|
752
|
+
if (ts.isIdentifier(expr)) {
|
|
753
|
+
const name = expr.text;
|
|
754
|
+
if (name === 'createOverlayHandler' || name === 'createOverlayHandlerWithQueryParamLifecycle') {
|
|
755
|
+
return true;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
current = current.parent;
|
|
760
|
+
}
|
|
761
|
+
return false;
|
|
762
|
+
}
|
|
763
|
+
function transformBuilderBodyForDefaults(bodyCode, builderParamName, sourceFile) {
|
|
764
|
+
let code = bodyCode;
|
|
765
|
+
let usesMergeConfigs = false;
|
|
766
|
+
const strategiesUsed = new Set();
|
|
767
|
+
// Check if mergeConfigs is used
|
|
768
|
+
if (code.includes(`${builderParamName}.mergeConfigs(`)) {
|
|
769
|
+
usesMergeConfigs = true;
|
|
770
|
+
}
|
|
771
|
+
// Replace config: with strategy:
|
|
772
|
+
code = code.replace(/\bconfig:/g, 'strategy:');
|
|
773
|
+
// Replace builder.DEFAULTS.X with Xstrategy.build()
|
|
774
|
+
Object.entries(STRATEGY_INJECT_MAP).forEach(([strategyName, injectFn]) => {
|
|
775
|
+
const pattern = new RegExp(`${builderParamName}\\.DEFAULTS\\.${strategyName}`, 'g');
|
|
776
|
+
if (pattern.test(code)) {
|
|
777
|
+
const varName = getStrategyVariableName(injectFn);
|
|
778
|
+
code = code.replace(pattern, `${varName}.build()`);
|
|
779
|
+
strategiesUsed.add(injectFn);
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
// Replace builder.mergeConfigs(Xstrategy.build(), ...args) with Xstrategy.build(mergeOverlayBreakpointConfigs(...args))
|
|
783
|
+
Object.entries(STRATEGY_INJECT_MAP).forEach(([strategyName, injectFn]) => {
|
|
784
|
+
const varName = getStrategyVariableName(injectFn);
|
|
785
|
+
// Pattern: builder.mergeConfigs(dialogStrategy.build(), {...})
|
|
786
|
+
const pattern = new RegExp(`${builderParamName}\\.mergeConfigs\\(\\s*${varName}\\.build\\(\\)\\s*,\\s*`, 'g');
|
|
787
|
+
let match;
|
|
788
|
+
const replacements = [];
|
|
789
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
790
|
+
const matchStart = match.index;
|
|
791
|
+
const afterComma = match.index + match[0].length;
|
|
792
|
+
// Find the closing paren by counting parentheses
|
|
793
|
+
let parenCount = 1;
|
|
794
|
+
let pos = afterComma;
|
|
795
|
+
while (pos < code.length && parenCount > 0) {
|
|
796
|
+
if (code[pos] === '(')
|
|
797
|
+
parenCount++;
|
|
798
|
+
if (code[pos] === ')')
|
|
799
|
+
parenCount--;
|
|
800
|
+
pos++;
|
|
801
|
+
}
|
|
802
|
+
const closingParenPos = pos - 1;
|
|
803
|
+
const args = code.substring(afterComma, closingParenPos).trim();
|
|
804
|
+
const cleanedArgs = args.replace(/,\s*$/, '');
|
|
805
|
+
const replacement = `${varName}.build(mergeOverlayBreakpointConfigs(${cleanedArgs}))`;
|
|
806
|
+
replacements.push({
|
|
807
|
+
start: matchStart,
|
|
808
|
+
end: pos,
|
|
809
|
+
replacement,
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
// Apply replacements in reverse order
|
|
813
|
+
replacements.reverse().forEach(({ start, end, replacement }) => {
|
|
814
|
+
code = code.substring(0, start) + replacement + code.substring(end);
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
return { code, usesMergeConfigs, strategiesUsed };
|
|
818
|
+
}
|
|
819
|
+
function transformBuilderPattern(text, importsToAdd) {
|
|
820
|
+
// This handles builder patterns that DON'T use DEFAULTS
|
|
821
|
+
// Example: (builder) => builder.dialog() or (builder) => [builder.bottomSheet(), ...]
|
|
822
|
+
// Extract the builder parameter name
|
|
823
|
+
const builderParamMatch = text.match(/\((\w+)\)\s*=>/);
|
|
824
|
+
if (!builderParamMatch) {
|
|
825
|
+
return text; // Not a builder pattern
|
|
826
|
+
}
|
|
827
|
+
const builderParamName = builderParamMatch[1];
|
|
828
|
+
// Get the function body (after =>)
|
|
829
|
+
const arrowIndex = text.indexOf('=>');
|
|
830
|
+
const body = text.substring(arrowIndex + 2).trim();
|
|
831
|
+
// Replace builder.method() calls with strategy functions
|
|
832
|
+
let transformedBody = body;
|
|
833
|
+
// Replace config: with strategy:
|
|
834
|
+
transformedBody = transformedBody.replace(/\bconfig:/g, 'strategy:');
|
|
835
|
+
// Handle regular strategy methods: builder.dialog() -> dialogOverlayStrategy()
|
|
836
|
+
Object.entries(STRATEGY_MAP).forEach(([method, strategy]) => {
|
|
837
|
+
const pattern = new RegExp(`${builderParamName}\\.${method}\\(`, 'g');
|
|
838
|
+
transformedBody = transformedBody.replace(pattern, `${strategy}(`);
|
|
839
|
+
if (transformedBody.includes(strategy)) {
|
|
840
|
+
importsToAdd.add(strategy);
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
// Handle transforming presets
|
|
844
|
+
Object.entries(TRANSFORMING_PRESET_STRATEGIES).forEach(([preset, strategy]) => {
|
|
845
|
+
const pattern = new RegExp(`${builderParamName}\\.${preset}\\(`, 'g');
|
|
846
|
+
transformedBody = transformedBody.replace(pattern, `${strategy}(`);
|
|
847
|
+
if (transformedBody.includes(strategy)) {
|
|
848
|
+
importsToAdd.add(strategy);
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
// Handle mergeConfigs
|
|
852
|
+
const mergePattern = new RegExp(`${builderParamName}\\.mergeConfigs\\(`, 'g');
|
|
853
|
+
transformedBody = transformedBody.replace(mergePattern, 'mergeOverlayBreakpointConfigs(');
|
|
854
|
+
if (transformedBody.includes('mergeOverlayBreakpointConfigs')) {
|
|
855
|
+
importsToAdd.add('mergeOverlayBreakpointConfigs');
|
|
856
|
+
}
|
|
857
|
+
// If the body was an array or object literal, keep it as is
|
|
858
|
+
// If it was a block with return, extract just the returned value
|
|
859
|
+
if (transformedBody.startsWith('{')) {
|
|
860
|
+
// Block body: { return [...] }
|
|
861
|
+
const returnMatch = transformedBody.match(/return\s+([\s\S]+?);?\s*}$/);
|
|
862
|
+
if (returnMatch) {
|
|
863
|
+
transformedBody = returnMatch[1].trim();
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return transformedBody;
|
|
867
|
+
}
|
|
868
|
+
//# sourceMappingURL=overlay-positions.js.map
|