@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,1039 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
3
|
+
import { getProjects, logger, visitNotIgnoredFiles } from '@nx/devkit';
|
|
4
|
+
import * as ts from 'typescript';
|
|
5
|
+
// Symbol mappings
|
|
6
|
+
const SYMBOL_REPLACEMENTS = {
|
|
7
|
+
// Refs
|
|
8
|
+
DialogRef: 'OverlayRef',
|
|
9
|
+
BottomSheetRef: 'OverlayRef',
|
|
10
|
+
// Imports
|
|
11
|
+
DialogImports: 'OverlayImports',
|
|
12
|
+
BottomSheetImports: 'OverlayImports',
|
|
13
|
+
DynamicOverlayImports: 'OverlayImports',
|
|
14
|
+
// Providers
|
|
15
|
+
provideDialog: 'provideOverlay',
|
|
16
|
+
provideBottomSheet: 'provideOverlay',
|
|
17
|
+
// Directives
|
|
18
|
+
DialogCloseDirective: 'OverlayCloseDirective',
|
|
19
|
+
DialogTitleDirective: 'OverlayTitleDirective',
|
|
20
|
+
BottomSheetTitleDirective: 'OverlayTitleDirective',
|
|
21
|
+
DynamicOverlayTitleDirective: 'OverlayTitleDirective',
|
|
22
|
+
// Config
|
|
23
|
+
DialogConfig: 'OverlayConfig',
|
|
24
|
+
BottomSheetConfig: 'OverlayConfig',
|
|
25
|
+
// Data tokens
|
|
26
|
+
DIALOG_DATA: 'OVERLAY_DATA',
|
|
27
|
+
BOTTOM_SHEET_DATA: 'OVERLAY_DATA',
|
|
28
|
+
};
|
|
29
|
+
const SERVICE_TYPES = ['DialogService', 'BottomSheetService', 'DynamicOverlayService'];
|
|
30
|
+
// Symbols to remove
|
|
31
|
+
const SYMBOLS_TO_REMOVE = ['BottomSheetDragHandleComponent'];
|
|
32
|
+
// Style properties that need to be moved to strategy config
|
|
33
|
+
const STYLE_PROPERTIES = [
|
|
34
|
+
'panelClass',
|
|
35
|
+
'containerClass',
|
|
36
|
+
'overlayClass',
|
|
37
|
+
'backdropClass',
|
|
38
|
+
'width',
|
|
39
|
+
'height',
|
|
40
|
+
'minWidth',
|
|
41
|
+
'minHeight',
|
|
42
|
+
'maxWidth',
|
|
43
|
+
'maxHeight',
|
|
44
|
+
'position',
|
|
45
|
+
];
|
|
46
|
+
export default async function migrateDialogBottomSheet(tree) {
|
|
47
|
+
logger.log('🔄 Migrating dialog & bottom sheet to overlay...');
|
|
48
|
+
const projects = getProjects(tree);
|
|
49
|
+
for (const [, project] of projects) {
|
|
50
|
+
visitNotIgnoredFiles(tree, project.root, (filePath) => {
|
|
51
|
+
if (filePath.endsWith('.ts')) {
|
|
52
|
+
processTypeScriptFile(filePath, tree, logger);
|
|
53
|
+
}
|
|
54
|
+
else if (filePath.endsWith('.html')) {
|
|
55
|
+
processHtmlFile(filePath, tree);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
logger.log('✅ Dialog & bottom sheet migration completed');
|
|
60
|
+
}
|
|
61
|
+
function processTypeScriptFile(filePath, tree, logger) {
|
|
62
|
+
const content = tree.read(filePath, 'utf-8');
|
|
63
|
+
if (!content)
|
|
64
|
+
return;
|
|
65
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
66
|
+
const changes = [];
|
|
67
|
+
const importsToAdd = new Set();
|
|
68
|
+
const importsToRemove = new Set();
|
|
69
|
+
// Track which symbols are from @ethlete/cdk
|
|
70
|
+
const ethleteSymbols = new Set();
|
|
71
|
+
// Track which identifiers represent which original service types
|
|
72
|
+
const serviceVariableMap = new Map(); // variableName -> original service type
|
|
73
|
+
// First pass: identify symbols from @ethlete/cdk
|
|
74
|
+
function identifyEthleteSymbols(node) {
|
|
75
|
+
if (ts.isImportDeclaration(node)) {
|
|
76
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
77
|
+
if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === '@ethlete/cdk') {
|
|
78
|
+
if (node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
|
|
79
|
+
node.importClause.namedBindings.elements.forEach((element) => {
|
|
80
|
+
const importedName = element.propertyName?.text || element.name.text;
|
|
81
|
+
if (importedName in SYMBOL_REPLACEMENTS ||
|
|
82
|
+
SYMBOLS_TO_REMOVE.includes(importedName) ||
|
|
83
|
+
SERVICE_TYPES.includes(importedName) // ADD THIS LINE
|
|
84
|
+
) {
|
|
85
|
+
ethleteSymbols.add(element.name.text);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
ts.forEachChild(node, identifyEthleteSymbols);
|
|
92
|
+
}
|
|
93
|
+
identifyEthleteSymbols(sourceFile);
|
|
94
|
+
// Second pass: map variable names to their original service types
|
|
95
|
+
function mapServiceVariables(node) {
|
|
96
|
+
// Constructor parameters
|
|
97
|
+
if (ts.isParameter(node) && node.type && ts.isTypeReferenceNode(node.type)) {
|
|
98
|
+
if (ts.isIdentifier(node.type.typeName) && ts.isIdentifier(node.name)) {
|
|
99
|
+
const typeName = node.type.typeName.text;
|
|
100
|
+
if (isEthleteSymbol(typeName)) {
|
|
101
|
+
const originalName = getOriginalSymbolName(typeName);
|
|
102
|
+
if (originalName === 'DialogService' ||
|
|
103
|
+
originalName === 'BottomSheetService' ||
|
|
104
|
+
originalName === 'DynamicOverlayService') {
|
|
105
|
+
serviceVariableMap.set(node.name.text, originalName);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Property declarations with inject()
|
|
111
|
+
if (ts.isPropertyDeclaration(node) && node.initializer && ts.isCallExpression(node.initializer)) {
|
|
112
|
+
if (ts.isIdentifier(node.initializer.expression) &&
|
|
113
|
+
node.initializer.expression.text === 'inject' &&
|
|
114
|
+
node.initializer.arguments.length > 0) {
|
|
115
|
+
const firstArg = node.initializer.arguments[0];
|
|
116
|
+
// Handle both regular identifiers and private identifiers
|
|
117
|
+
let propertyName;
|
|
118
|
+
if (ts.isIdentifier(node.name)) {
|
|
119
|
+
propertyName = node.name.text;
|
|
120
|
+
}
|
|
121
|
+
else if (ts.isPrivateIdentifier(node.name)) {
|
|
122
|
+
propertyName = node.name.text; // This includes the # prefix
|
|
123
|
+
}
|
|
124
|
+
if (firstArg && ts.isIdentifier(firstArg) && propertyName) {
|
|
125
|
+
const argName = firstArg.text;
|
|
126
|
+
if (isEthleteSymbol(argName)) {
|
|
127
|
+
const originalName = getOriginalSymbolName(argName);
|
|
128
|
+
if (originalName === 'DialogService' ||
|
|
129
|
+
originalName === 'BottomSheetService' ||
|
|
130
|
+
originalName === 'DynamicOverlayService') {
|
|
131
|
+
serviceVariableMap.set(propertyName, originalName);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
ts.forEachChild(node, mapServiceVariables);
|
|
138
|
+
}
|
|
139
|
+
mapServiceVariables(sourceFile);
|
|
140
|
+
// Helper to check if a symbol is from @ethlete/cdk
|
|
141
|
+
function isEthleteSymbol(name) {
|
|
142
|
+
return ethleteSymbols.has(name);
|
|
143
|
+
}
|
|
144
|
+
// Helper to get the original symbol name before aliasing
|
|
145
|
+
function getOriginalSymbolName(localName) {
|
|
146
|
+
let originalName;
|
|
147
|
+
function findOriginalName(node) {
|
|
148
|
+
if (ts.isImportDeclaration(node)) {
|
|
149
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
150
|
+
if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === '@ethlete/cdk') {
|
|
151
|
+
if (node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
|
|
152
|
+
node.importClause.namedBindings.elements.forEach((element) => {
|
|
153
|
+
if (element.name.text === localName) {
|
|
154
|
+
originalName = element.propertyName?.text || element.name.text;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
ts.forEachChild(node, findOriginalName);
|
|
161
|
+
}
|
|
162
|
+
findOriginalName(sourceFile);
|
|
163
|
+
return originalName;
|
|
164
|
+
}
|
|
165
|
+
function visit(node) {
|
|
166
|
+
// In the visit function, update the constructor handling section
|
|
167
|
+
if (ts.isConstructorDeclaration(node)) {
|
|
168
|
+
const serviceParams = [];
|
|
169
|
+
const otherParams = [];
|
|
170
|
+
// Classify parameters
|
|
171
|
+
node.parameters.forEach((param) => {
|
|
172
|
+
if (param.type && ts.isTypeReferenceNode(param.type) && ts.isIdentifier(param.type.typeName)) {
|
|
173
|
+
const typeName = param.type.typeName.text;
|
|
174
|
+
if (isEthleteSymbol(typeName)) {
|
|
175
|
+
const originalName = getOriginalSymbolName(typeName);
|
|
176
|
+
if (originalName && SERVICE_TYPES.includes(originalName)) {
|
|
177
|
+
serviceParams.push(param);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
otherParams.push(param);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
otherParams.push(param);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
otherParams.push(param);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
if (serviceParams.length > 0) {
|
|
192
|
+
// Find the class declaration
|
|
193
|
+
let classDecl;
|
|
194
|
+
let current = node.parent;
|
|
195
|
+
while (current) {
|
|
196
|
+
if (ts.isClassDeclaration(current)) {
|
|
197
|
+
classDecl = current;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
current = current.parent;
|
|
201
|
+
}
|
|
202
|
+
if (classDecl) {
|
|
203
|
+
const hasOnlyServiceParams = otherParams.length === 0;
|
|
204
|
+
const hasNoBody = !node.body || node.body.statements.length === 0;
|
|
205
|
+
if (hasOnlyServiceParams && hasNoBody) {
|
|
206
|
+
// Remove entire constructor and convert to field declarations
|
|
207
|
+
const constructorFullStart = node.getFullStart();
|
|
208
|
+
const constructorEnd = node.getEnd();
|
|
209
|
+
// Find the end including trailing newline
|
|
210
|
+
const fullText = sourceFile.getFullText();
|
|
211
|
+
let endPos = constructorEnd;
|
|
212
|
+
while (endPos < fullText.length && (fullText[endPos] === ' ' || fullText[endPos] === '\t')) {
|
|
213
|
+
endPos++;
|
|
214
|
+
}
|
|
215
|
+
if (endPos < fullText.length && fullText[endPos] === '\n') {
|
|
216
|
+
endPos++;
|
|
217
|
+
}
|
|
218
|
+
// Get the indentation
|
|
219
|
+
const constructorLineStart = fullText.lastIndexOf('\n', constructorFullStart) + 1;
|
|
220
|
+
const indent = fullText.substring(constructorLineStart, constructorFullStart);
|
|
221
|
+
// Build replacement with field declarations
|
|
222
|
+
const fieldDeclarations = serviceParams
|
|
223
|
+
.map((param) => {
|
|
224
|
+
const paramName = param.name.getText(sourceFile);
|
|
225
|
+
const modifiers = param.modifiers?.map((m) => m.getText(sourceFile)).join(' ') || 'private';
|
|
226
|
+
// Track the variable name to original service type
|
|
227
|
+
const typeName = param.type.typeName;
|
|
228
|
+
if (ts.isIdentifier(typeName)) {
|
|
229
|
+
const originalName = getOriginalSymbolName(typeName.text);
|
|
230
|
+
if (originalName && SERVICE_TYPES.includes(originalName)) {
|
|
231
|
+
serviceVariableMap.set(paramName, originalName);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return `${indent}${modifiers} ${paramName} = injectOverlayManager();`;
|
|
235
|
+
})
|
|
236
|
+
.join('\n');
|
|
237
|
+
changes.push({
|
|
238
|
+
start: constructorFullStart,
|
|
239
|
+
end: endPos,
|
|
240
|
+
replacement: fieldDeclarations + '\n',
|
|
241
|
+
});
|
|
242
|
+
importsToAdd.add('injectOverlayManager');
|
|
243
|
+
serviceParams.forEach((param) => {
|
|
244
|
+
if (param.type && ts.isTypeReferenceNode(param.type) && ts.isIdentifier(param.type.typeName)) {
|
|
245
|
+
const originalName = getOriginalSymbolName(param.type.typeName.text);
|
|
246
|
+
if (originalName) {
|
|
247
|
+
importsToRemove.add(originalName);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
else if (serviceParams.length > 0 && otherParams.length > 0) {
|
|
253
|
+
// Mixed parameters - replace entire constructor with fields + new constructor
|
|
254
|
+
const constructorFullStart = node.getFullStart();
|
|
255
|
+
const constructorEnd = node.getEnd();
|
|
256
|
+
// Find the end including trailing newline
|
|
257
|
+
const fullText = sourceFile.getFullText();
|
|
258
|
+
let endPos = constructorEnd;
|
|
259
|
+
while (endPos < fullText.length && (fullText[endPos] === ' ' || fullText[endPos] === '\t')) {
|
|
260
|
+
endPos++;
|
|
261
|
+
}
|
|
262
|
+
if (endPos < fullText.length && fullText[endPos] === '\n') {
|
|
263
|
+
endPos++;
|
|
264
|
+
}
|
|
265
|
+
// Get the indentation
|
|
266
|
+
const constructorLineStart = fullText.lastIndexOf('\n', constructorFullStart) + 1;
|
|
267
|
+
const indent = fullText.substring(constructorLineStart, constructorFullStart);
|
|
268
|
+
// Build field declarations for service params
|
|
269
|
+
const fieldDeclarations = serviceParams
|
|
270
|
+
.map((param) => {
|
|
271
|
+
const paramName = param.name.getText(sourceFile);
|
|
272
|
+
const modifiers = param.modifiers?.map((m) => m.getText(sourceFile)).join(' ') || 'private';
|
|
273
|
+
// Track the variable name to original service type
|
|
274
|
+
const typeName = param.type.typeName;
|
|
275
|
+
if (ts.isIdentifier(typeName)) {
|
|
276
|
+
const originalName = getOriginalSymbolName(typeName.text);
|
|
277
|
+
if (originalName && SERVICE_TYPES.includes(originalName)) {
|
|
278
|
+
serviceVariableMap.set(paramName, originalName);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return `${indent}${modifiers} ${paramName} = injectOverlayManager();`;
|
|
282
|
+
})
|
|
283
|
+
.join('\n');
|
|
284
|
+
// Build new constructor with only non-service params
|
|
285
|
+
const newConstructorParams = otherParams.map((param) => param.getText(sourceFile)).join(',\n ');
|
|
286
|
+
const bodyText = node.body ? ` ${node.body.getText(sourceFile)}` : ' {}';
|
|
287
|
+
const newConstructor = `${indent}constructor(\n ${newConstructorParams}\n )${bodyText}`;
|
|
288
|
+
// Replace the entire constructor section with fields + new constructor
|
|
289
|
+
const replacement = `${fieldDeclarations}\n\n${newConstructor}\n`;
|
|
290
|
+
changes.push({
|
|
291
|
+
start: constructorFullStart,
|
|
292
|
+
end: endPos,
|
|
293
|
+
replacement,
|
|
294
|
+
});
|
|
295
|
+
importsToAdd.add('injectOverlayManager');
|
|
296
|
+
serviceParams.forEach((param) => {
|
|
297
|
+
if (param.type && ts.isTypeReferenceNode(param.type) && ts.isIdentifier(param.type.typeName)) {
|
|
298
|
+
const originalName = getOriginalSymbolName(param.type.typeName.text);
|
|
299
|
+
if (originalName) {
|
|
300
|
+
importsToRemove.add(originalName);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return; // Don't traverse children, we've handled this constructor
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Handle inject() calls for services
|
|
310
|
+
if (ts.isCallExpression(node) &&
|
|
311
|
+
ts.isIdentifier(node.expression) &&
|
|
312
|
+
node.expression.text === 'inject' &&
|
|
313
|
+
node.arguments.length > 0) {
|
|
314
|
+
const firstArg = node.arguments[0];
|
|
315
|
+
if (firstArg && ts.isIdentifier(firstArg)) {
|
|
316
|
+
const argName = firstArg.text;
|
|
317
|
+
if (isEthleteSymbol(argName)) {
|
|
318
|
+
const originalName = getOriginalSymbolName(argName);
|
|
319
|
+
if (originalName && SERVICE_TYPES.includes(originalName)) {
|
|
320
|
+
// Replace inject(DialogService) with injectOverlayManager()
|
|
321
|
+
changes.push({
|
|
322
|
+
start: node.getStart(sourceFile),
|
|
323
|
+
end: node.getEnd(),
|
|
324
|
+
replacement: 'injectOverlayManager()',
|
|
325
|
+
});
|
|
326
|
+
importsToAdd.add('injectOverlayManager');
|
|
327
|
+
importsToRemove.add(originalName);
|
|
328
|
+
// Track the variable name if this is a property declaration
|
|
329
|
+
if (ts.isPropertyDeclaration(node.parent)) {
|
|
330
|
+
let propertyName;
|
|
331
|
+
if (ts.isIdentifier(node.parent.name)) {
|
|
332
|
+
propertyName = node.parent.name.text;
|
|
333
|
+
}
|
|
334
|
+
else if (ts.isPrivateIdentifier(node.parent.name)) {
|
|
335
|
+
propertyName = node.parent.name.text; // Includes the # prefix
|
|
336
|
+
}
|
|
337
|
+
if (propertyName) {
|
|
338
|
+
serviceVariableMap.set(propertyName, originalName);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return; // Don't traverse children
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// Handle type references in type annotation positions (e.g., constructor parameters, return types)
|
|
347
|
+
if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
|
|
348
|
+
const typeName = node.typeName.text;
|
|
349
|
+
if (isEthleteSymbol(typeName)) {
|
|
350
|
+
const originalName = getOriginalSymbolName(typeName);
|
|
351
|
+
if (originalName && originalName in SYMBOL_REPLACEMENTS) {
|
|
352
|
+
const replacement = SYMBOL_REPLACEMENTS[originalName];
|
|
353
|
+
changes.push({
|
|
354
|
+
start: node.typeName.getStart(sourceFile),
|
|
355
|
+
end: node.typeName.getEnd(),
|
|
356
|
+
replacement,
|
|
357
|
+
});
|
|
358
|
+
importsToAdd.add(replacement);
|
|
359
|
+
importsToRemove.add(originalName);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// Handle type references in expression positions (e.g., inject(DialogRef<T>))
|
|
364
|
+
if (ts.isExpressionWithTypeArguments(node) && ts.isIdentifier(node.expression)) {
|
|
365
|
+
const typeName = node.expression.text;
|
|
366
|
+
if (isEthleteSymbol(typeName)) {
|
|
367
|
+
const originalName = getOriginalSymbolName(typeName);
|
|
368
|
+
if (originalName && originalName in SYMBOL_REPLACEMENTS) {
|
|
369
|
+
const replacement = SYMBOL_REPLACEMENTS[originalName];
|
|
370
|
+
changes.push({
|
|
371
|
+
start: node.expression.getStart(sourceFile),
|
|
372
|
+
end: node.expression.getEnd(),
|
|
373
|
+
replacement,
|
|
374
|
+
});
|
|
375
|
+
importsToAdd.add(replacement);
|
|
376
|
+
importsToRemove.add(originalName);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// In the identifier handler section, update to track provider removals better:
|
|
381
|
+
// Handle identifiers used as values (e.g., inject(DialogRef), provideDialog(), [BottomSheetDragHandleComponent])
|
|
382
|
+
if (ts.isIdentifier(node)) {
|
|
383
|
+
const name = node.text;
|
|
384
|
+
if (isEthleteSymbol(name)) {
|
|
385
|
+
const originalName = getOriginalSymbolName(name);
|
|
386
|
+
if (originalName) {
|
|
387
|
+
// Check if this identifier is in a position where it needs to be replaced or removed
|
|
388
|
+
const parent = node.parent;
|
|
389
|
+
// Skip if it's part of a type reference (already handled above)
|
|
390
|
+
// Skip if it's part of an import declaration
|
|
391
|
+
// Skip if it's a property name in an object literal or property access
|
|
392
|
+
if (ts.isTypeReferenceNode(parent) ||
|
|
393
|
+
ts.isExpressionWithTypeArguments(parent) ||
|
|
394
|
+
ts.isImportSpecifier(parent) ||
|
|
395
|
+
(ts.isPropertyAccessExpression(parent) && parent.name === node) ||
|
|
396
|
+
(ts.isPropertyAssignment(parent) && parent.name === node)) {
|
|
397
|
+
// Skip - already handled or shouldn't be replaced
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
// Handle symbols to remove (e.g., BottomSheetDragHandleComponent)
|
|
401
|
+
if (SYMBOLS_TO_REMOVE.includes(originalName)) {
|
|
402
|
+
// Check if it's in an array literal (e.g., imports: [BottomSheetDragHandleComponent])
|
|
403
|
+
if (ts.isArrayLiteralExpression(parent)) {
|
|
404
|
+
// Remove the identifier and any trailing comma
|
|
405
|
+
const nodeStart = node.getStart(sourceFile);
|
|
406
|
+
const nodeEnd = node.getEnd();
|
|
407
|
+
// Check if there's a comma after this element
|
|
408
|
+
let end = nodeEnd;
|
|
409
|
+
const fullText = sourceFile.getFullText();
|
|
410
|
+
// Skip whitespace after the identifier
|
|
411
|
+
while (end < fullText.length &&
|
|
412
|
+
(fullText[end] === ' ' || fullText[end] === '\t' || fullText[end] === '\n')) {
|
|
413
|
+
end++;
|
|
414
|
+
}
|
|
415
|
+
// If there's a comma, include it in the removal
|
|
416
|
+
if (fullText[end] === ',') {
|
|
417
|
+
end++;
|
|
418
|
+
// Skip whitespace after the comma
|
|
419
|
+
while (end < fullText.length &&
|
|
420
|
+
(fullText[end] === ' ' || fullText[end] === '\t' || fullText[end] === '\n')) {
|
|
421
|
+
end++;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
// Check if there's a comma BEFORE this element
|
|
426
|
+
let start = nodeStart;
|
|
427
|
+
while (start > 0 &&
|
|
428
|
+
(fullText[start - 1] === ' ' || fullText[start - 1] === '\t' || fullText[start - 1] === '\n')) {
|
|
429
|
+
start--;
|
|
430
|
+
}
|
|
431
|
+
if (start > 0 && fullText[start - 1] === ',') {
|
|
432
|
+
start--;
|
|
433
|
+
// Include preceding whitespace
|
|
434
|
+
while (start > 0 &&
|
|
435
|
+
(fullText[start - 1] === ' ' || fullText[start - 1] === '\t' || fullText[start - 1] === '\n')) {
|
|
436
|
+
start--;
|
|
437
|
+
}
|
|
438
|
+
changes.push({
|
|
439
|
+
start,
|
|
440
|
+
end: nodeEnd,
|
|
441
|
+
replacement: '',
|
|
442
|
+
});
|
|
443
|
+
importsToRemove.add(originalName);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
changes.push({
|
|
448
|
+
start: nodeStart,
|
|
449
|
+
end,
|
|
450
|
+
replacement: '',
|
|
451
|
+
});
|
|
452
|
+
importsToRemove.add(originalName);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
// For other contexts, just remove it
|
|
456
|
+
importsToRemove.add(originalName);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
// Handle symbols to replace
|
|
460
|
+
if (originalName in SYMBOL_REPLACEMENTS) {
|
|
461
|
+
const replacement = SYMBOL_REPLACEMENTS[originalName];
|
|
462
|
+
// Special handling for items in arrays - check for duplicates
|
|
463
|
+
if (ts.isArrayLiteralExpression(parent)) {
|
|
464
|
+
// Count how many elements in this array will map to the same replacement
|
|
465
|
+
const elementsWithSameReplacement = parent.elements.filter((element) => {
|
|
466
|
+
if (ts.isIdentifier(element)) {
|
|
467
|
+
const elemName = element.text;
|
|
468
|
+
if (isEthleteSymbol(elemName)) {
|
|
469
|
+
const elemOriginalName = getOriginalSymbolName(elemName);
|
|
470
|
+
if (elemOriginalName && elemOriginalName in SYMBOL_REPLACEMENTS) {
|
|
471
|
+
return SYMBOL_REPLACEMENTS[elemOriginalName] === replacement;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (ts.isCallExpression(element) && ts.isIdentifier(element.expression)) {
|
|
476
|
+
const elemName = element.expression.text;
|
|
477
|
+
if (isEthleteSymbol(elemName)) {
|
|
478
|
+
const elemOriginalName = getOriginalSymbolName(elemName);
|
|
479
|
+
if (elemOriginalName && elemOriginalName in SYMBOL_REPLACEMENTS) {
|
|
480
|
+
return SYMBOL_REPLACEMENTS[elemOriginalName] === replacement;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return false;
|
|
485
|
+
});
|
|
486
|
+
// If there are multiple elements that map to the same replacement,
|
|
487
|
+
// only keep the first one and remove the rest
|
|
488
|
+
if (elementsWithSameReplacement.length > 1) {
|
|
489
|
+
const firstElement = elementsWithSameReplacement[0];
|
|
490
|
+
// Check if current node is the first element
|
|
491
|
+
const isFirstElement = node === firstElement ||
|
|
492
|
+
(ts.isCallExpression(parent) && parent.expression === node && parent === firstElement);
|
|
493
|
+
if (!isFirstElement) {
|
|
494
|
+
// This is not the first element, remove it
|
|
495
|
+
// We need to remove the parent if it's a call expression
|
|
496
|
+
const nodeToRemove = ts.isCallExpression(parent) ? parent : node;
|
|
497
|
+
const nodeStart = nodeToRemove.getFullStart();
|
|
498
|
+
const nodeEnd = nodeToRemove.getEnd();
|
|
499
|
+
const fullText = sourceFile.getFullText();
|
|
500
|
+
// Find comma and whitespace to remove
|
|
501
|
+
let start = nodeStart;
|
|
502
|
+
let end = nodeEnd;
|
|
503
|
+
// Look for preceding comma and whitespace
|
|
504
|
+
let checkStart = nodeStart;
|
|
505
|
+
while (checkStart > 0 && /[\s\n]/.test(fullText[checkStart - 1])) {
|
|
506
|
+
checkStart--;
|
|
507
|
+
}
|
|
508
|
+
if (checkStart > 0 && fullText[checkStart - 1] === ',') {
|
|
509
|
+
start = checkStart - 1;
|
|
510
|
+
// Include any whitespace before the comma
|
|
511
|
+
while (start > 0 && /[\s\n]/.test(fullText[start - 1])) {
|
|
512
|
+
start--;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
// Look for trailing comma and whitespace
|
|
517
|
+
let checkEnd = nodeEnd;
|
|
518
|
+
while (checkEnd < fullText.length && /[\s\n]/.test(fullText[checkEnd])) {
|
|
519
|
+
checkEnd++;
|
|
520
|
+
}
|
|
521
|
+
if (checkEnd < fullText.length && fullText[checkEnd] === ',') {
|
|
522
|
+
end = checkEnd + 1;
|
|
523
|
+
// Include whitespace after comma
|
|
524
|
+
while (end < fullText.length && /[\s\n]/.test(fullText[end])) {
|
|
525
|
+
end++;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
changes.push({
|
|
530
|
+
start,
|
|
531
|
+
end,
|
|
532
|
+
replacement: '',
|
|
533
|
+
});
|
|
534
|
+
importsToRemove.add(originalName);
|
|
535
|
+
return; // Don't process this node further
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
// Special handling for provider function calls
|
|
540
|
+
if (ts.isCallExpression(parent) &&
|
|
541
|
+
parent.expression === node &&
|
|
542
|
+
(originalName === 'provideDialog' || originalName === 'provideBottomSheet')) {
|
|
543
|
+
// For provider functions, we need to check if provideOverlay() already exists
|
|
544
|
+
// in the same array to avoid duplicates
|
|
545
|
+
const arrayParent = parent.parent;
|
|
546
|
+
if (ts.isArrayLiteralExpression(arrayParent)) {
|
|
547
|
+
// Count provider calls that will map to provideOverlay
|
|
548
|
+
const providerCallsWithSameReplacement = arrayParent.elements.filter((element) => {
|
|
549
|
+
if (ts.isCallExpression(element) && ts.isIdentifier(element.expression)) {
|
|
550
|
+
const elemName = element.expression.text;
|
|
551
|
+
if (isEthleteSymbol(elemName)) {
|
|
552
|
+
const elemOriginalName = getOriginalSymbolName(elemName);
|
|
553
|
+
if (elemOriginalName && elemOriginalName in SYMBOL_REPLACEMENTS) {
|
|
554
|
+
return (SYMBOL_REPLACEMENTS[elemOriginalName] === 'provideOverlay');
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return false;
|
|
559
|
+
});
|
|
560
|
+
if (providerCallsWithSameReplacement.length > 1) {
|
|
561
|
+
const firstCall = providerCallsWithSameReplacement[0];
|
|
562
|
+
if (firstCall !== parent) {
|
|
563
|
+
// Remove this provider call entirely (including call expression)
|
|
564
|
+
const callStart = parent.getStart(sourceFile);
|
|
565
|
+
let callEnd = parent.getEnd();
|
|
566
|
+
const fullText = sourceFile.getFullText();
|
|
567
|
+
// Check for trailing comma
|
|
568
|
+
let end = callEnd;
|
|
569
|
+
while (end < fullText.length &&
|
|
570
|
+
(fullText[end] === ' ' || fullText[end] === '\t' || fullText[end] === '\n')) {
|
|
571
|
+
end++;
|
|
572
|
+
}
|
|
573
|
+
if (fullText[end] === ',') {
|
|
574
|
+
callEnd = end + 1;
|
|
575
|
+
while (callEnd < fullText.length &&
|
|
576
|
+
(fullText[callEnd] === ' ' || fullText[callEnd] === '\t' || fullText[callEnd] === '\n')) {
|
|
577
|
+
callEnd++;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
// Check for preceding comma
|
|
582
|
+
let start = callStart;
|
|
583
|
+
while (start > 0 &&
|
|
584
|
+
(fullText[start - 1] === ' ' || fullText[start - 1] === '\t' || fullText[start - 1] === '\n')) {
|
|
585
|
+
start--;
|
|
586
|
+
}
|
|
587
|
+
if (start > 0 && fullText[start - 1] === ',') {
|
|
588
|
+
changes.push({
|
|
589
|
+
start: start - 1,
|
|
590
|
+
end: callEnd,
|
|
591
|
+
replacement: '',
|
|
592
|
+
});
|
|
593
|
+
importsToRemove.add(originalName);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
changes.push({
|
|
598
|
+
start: callStart,
|
|
599
|
+
end: callEnd,
|
|
600
|
+
replacement: '',
|
|
601
|
+
});
|
|
602
|
+
importsToRemove.add(originalName);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
changes.push({
|
|
609
|
+
start: node.getStart(sourceFile),
|
|
610
|
+
end: node.getEnd(),
|
|
611
|
+
replacement,
|
|
612
|
+
});
|
|
613
|
+
importsToAdd.add(replacement);
|
|
614
|
+
importsToRemove.add(originalName);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
// Handle imports
|
|
620
|
+
if (ts.isImportDeclaration(node)) {
|
|
621
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
622
|
+
if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === '@ethlete/cdk') {
|
|
623
|
+
if (node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
|
|
624
|
+
node.importClause.namedBindings.elements.forEach((element) => {
|
|
625
|
+
const importedName = element.propertyName?.text || element.name.text;
|
|
626
|
+
// Handle symbols to replace
|
|
627
|
+
if (importedName in SYMBOL_REPLACEMENTS) {
|
|
628
|
+
importsToRemove.add(importedName);
|
|
629
|
+
importsToAdd.add(SYMBOL_REPLACEMENTS[importedName]);
|
|
630
|
+
}
|
|
631
|
+
// Handle symbols to remove
|
|
632
|
+
if (SYMBOLS_TO_REMOVE.includes(importedName)) {
|
|
633
|
+
importsToRemove.add(importedName);
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
// Handle service.open() calls
|
|
640
|
+
if (ts.isCallExpression(node) &&
|
|
641
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
642
|
+
node.expression.name.text === 'open') {
|
|
643
|
+
const objectExpr = node.expression.expression;
|
|
644
|
+
// Check if this is a call on one of our services
|
|
645
|
+
let variableName;
|
|
646
|
+
if (ts.isIdentifier(objectExpr)) {
|
|
647
|
+
variableName = objectExpr.text;
|
|
648
|
+
}
|
|
649
|
+
else if (ts.isPropertyAccessExpression(objectExpr)) {
|
|
650
|
+
// Handle this.#dialogService, this._dialogService, or this.dialogService
|
|
651
|
+
if (ts.isIdentifier(objectExpr.name)) {
|
|
652
|
+
variableName = objectExpr.name.text;
|
|
653
|
+
}
|
|
654
|
+
else if (ts.isPrivateIdentifier(objectExpr.name)) {
|
|
655
|
+
variableName = objectExpr.name.text; // Includes the # prefix
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
// Look up the original service type from our map
|
|
659
|
+
// Try with and without underscore prefix
|
|
660
|
+
let originalServiceType;
|
|
661
|
+
if (variableName) {
|
|
662
|
+
originalServiceType = serviceVariableMap.get(variableName);
|
|
663
|
+
// If not found and starts with underscore, try without underscore
|
|
664
|
+
if (!originalServiceType && variableName.startsWith('_')) {
|
|
665
|
+
originalServiceType = serviceVariableMap.get(variableName.substring(1));
|
|
666
|
+
}
|
|
667
|
+
// If not found and doesn't start with underscore, try with underscore
|
|
668
|
+
if (!originalServiceType && !variableName.startsWith('_')) {
|
|
669
|
+
originalServiceType = serviceVariableMap.get('_' + variableName);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (originalServiceType) {
|
|
673
|
+
if (originalServiceType === 'DialogService') {
|
|
674
|
+
handleDialogServiceOpen(node, sourceFile, changes, importsToAdd);
|
|
675
|
+
}
|
|
676
|
+
else if (originalServiceType === 'BottomSheetService') {
|
|
677
|
+
handleBottomSheetServiceOpen(node, sourceFile, changes, importsToAdd);
|
|
678
|
+
}
|
|
679
|
+
else if (originalServiceType === 'DynamicOverlayService') {
|
|
680
|
+
handleDynamicOverlayServiceOpen(node, sourceFile, changes, importsToAdd);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
ts.forEachChild(node, visit);
|
|
685
|
+
}
|
|
686
|
+
visit(sourceFile);
|
|
687
|
+
// Apply changes
|
|
688
|
+
if (changes.length === 0 && importsToRemove.size === 0) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
// Sort changes in reverse order to maintain positions
|
|
692
|
+
changes.sort((a, b) => b.start - a.start);
|
|
693
|
+
// Validate no overlapping changes
|
|
694
|
+
for (let i = 0; i < changes.length - 1; i++) {
|
|
695
|
+
const current = changes[i];
|
|
696
|
+
const next = changes[i + 1];
|
|
697
|
+
if (current.start < next.end) {
|
|
698
|
+
logger.error(`Overlapping changes detected in ${filePath}!`);
|
|
699
|
+
logger.error(`Change 1: ${current.start}-${current.end} = "${current.replacement}"`);
|
|
700
|
+
logger.error(`Change 2: ${next.start}-${next.end} = "${next.replacement}"`);
|
|
701
|
+
throw new Error('Overlapping changes detected - this will corrupt the file');
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
let result = content;
|
|
705
|
+
for (const change of changes) {
|
|
706
|
+
result = result.substring(0, change.start) + change.replacement + result.substring(change.end);
|
|
707
|
+
}
|
|
708
|
+
// Update imports
|
|
709
|
+
result = updateImports(result, importsToAdd, importsToRemove);
|
|
710
|
+
tree.write(filePath, result);
|
|
711
|
+
}
|
|
712
|
+
function processHtmlFile(filePath, tree) {
|
|
713
|
+
let content = tree.read(filePath, 'utf-8');
|
|
714
|
+
if (!content)
|
|
715
|
+
return;
|
|
716
|
+
let modified = false;
|
|
717
|
+
// Remove et-bottom-sheet-drag-handle elements
|
|
718
|
+
const dragHandleElementRegex = /<et-bottom-sheet-drag-handle\s*\/?>|<et-bottom-sheet-drag-handle[^>]*>.*?<\/et-bottom-sheet-drag-handle>/gs;
|
|
719
|
+
if (dragHandleElementRegex.test(content)) {
|
|
720
|
+
content = content.replace(dragHandleElementRegex, '');
|
|
721
|
+
modified = true;
|
|
722
|
+
}
|
|
723
|
+
// Remove elements with etBottomSheetDragHandle directive (both camelCase and kebab-case)
|
|
724
|
+
// This regex matches any opening tag that contains the directive and removes the entire element
|
|
725
|
+
const dragHandleDirectivePatterns = [
|
|
726
|
+
// Match self-closing tags: <div etBottomSheetDragHandle />
|
|
727
|
+
/<(\w+)([^>]*?\s+etBottomSheetDragHandle\s*[^>]*?)\/>/gs,
|
|
728
|
+
/<(\w+)([^>]*?\s+et-bottom-sheet-drag-handle\s*[^>]*?)\/>/gs,
|
|
729
|
+
// Match elements with opening and closing tags: <div etBottomSheetDragHandle>...</div>
|
|
730
|
+
/<(\w+)([^>]*?\s+etBottomSheetDragHandle\s*[^>]*?)>(.*?)<\/\1>/gs,
|
|
731
|
+
/<(\w+)([^>]*?\s+et-bottom-sheet-drag-handle\s*[^>]*?)>(.*?)<\/\1>/gs,
|
|
732
|
+
];
|
|
733
|
+
for (const pattern of dragHandleDirectivePatterns) {
|
|
734
|
+
if (pattern.test(content)) {
|
|
735
|
+
content = content.replace(pattern, '');
|
|
736
|
+
modified = true;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
// Migrate title directives (attribute only)
|
|
740
|
+
const titleAttributeMappings = [
|
|
741
|
+
{ from: 'etBottomSheetTitle', to: 'etOverlayTitle' },
|
|
742
|
+
{ from: 'etDialogTitle', to: 'etOverlayTitle' },
|
|
743
|
+
{ from: 'etDynamicOverlayTitle', to: 'etOverlayTitle' },
|
|
744
|
+
{ from: 'et-bottom-sheet-title', to: 'et-overlay-title' },
|
|
745
|
+
{ from: 'et-dialog-title', to: 'et-overlay-title' },
|
|
746
|
+
{ from: 'et-dynamic-overlay-title', to: 'et-overlay-title' },
|
|
747
|
+
];
|
|
748
|
+
for (const { from, to } of titleAttributeMappings) {
|
|
749
|
+
// Only match as attributes, not as element tags
|
|
750
|
+
const directiveRegex = new RegExp(`\\b${from}\\b`, 'g');
|
|
751
|
+
if (directiveRegex.test(content)) {
|
|
752
|
+
content = content.replace(directiveRegex, to);
|
|
753
|
+
modified = true;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
// Migrate close directives (attribute only)
|
|
757
|
+
const closeAttributeMappings = [
|
|
758
|
+
{ from: 'et-dialog-close', to: 'etOverlayClose' },
|
|
759
|
+
{ from: 'etDialogClose', to: 'etOverlayClose' },
|
|
760
|
+
];
|
|
761
|
+
for (const { from, to } of closeAttributeMappings) {
|
|
762
|
+
const regex = new RegExp(`\\b${from}\\b`, 'g');
|
|
763
|
+
if (regex.test(content)) {
|
|
764
|
+
content = content.replace(regex, to);
|
|
765
|
+
modified = true;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
if (modified) {
|
|
769
|
+
tree.write(filePath, content);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
function updateImports(content, importsToAdd, importsToRemove) {
|
|
773
|
+
const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
|
|
774
|
+
let result = content;
|
|
775
|
+
const changes = [];
|
|
776
|
+
function visit(node) {
|
|
777
|
+
if (ts.isImportDeclaration(node)) {
|
|
778
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
779
|
+
if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === '@ethlete/cdk') {
|
|
780
|
+
if (node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
|
|
781
|
+
const existingImports = new Set(node.importClause.namedBindings.elements.map((el) => el.name.text));
|
|
782
|
+
// Remove imports that need to be removed
|
|
783
|
+
importsToRemove.forEach((imp) => existingImports.delete(imp));
|
|
784
|
+
// Add new imports
|
|
785
|
+
importsToAdd.forEach((imp) => existingImports.add(imp));
|
|
786
|
+
// Build new import statement
|
|
787
|
+
const sortedImports = Array.from(existingImports).sort();
|
|
788
|
+
const newImports = sortedImports.join(', ');
|
|
789
|
+
if (sortedImports.length > 0) {
|
|
790
|
+
const replacement = `import { ${newImports} } from '@ethlete/cdk';`;
|
|
791
|
+
changes.push({
|
|
792
|
+
start: node.getStart(sourceFile),
|
|
793
|
+
end: node.getEnd(),
|
|
794
|
+
replacement,
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
else {
|
|
798
|
+
// Remove the entire import if no imports remain
|
|
799
|
+
changes.push({
|
|
800
|
+
start: node.getFullStart(),
|
|
801
|
+
end: node.getEnd() + 1, // Include newline
|
|
802
|
+
replacement: '',
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
ts.forEachChild(node, visit);
|
|
809
|
+
}
|
|
810
|
+
visit(sourceFile);
|
|
811
|
+
// Apply changes in reverse order
|
|
812
|
+
changes.sort((a, b) => b.start - a.start);
|
|
813
|
+
for (const change of changes) {
|
|
814
|
+
result = result.substring(0, change.start) + change.replacement + result.substring(change.end);
|
|
815
|
+
}
|
|
816
|
+
return result;
|
|
817
|
+
}
|
|
818
|
+
function handleDialogServiceOpen(node, sourceFile, changes, importsToAdd) {
|
|
819
|
+
importsToAdd.add('dialogOverlayStrategy');
|
|
820
|
+
// If no config argument, add one with strategies
|
|
821
|
+
if (node.arguments.length === 1) {
|
|
822
|
+
changes.push({
|
|
823
|
+
start: node.arguments[0].getEnd(),
|
|
824
|
+
end: node.arguments[0].getEnd(),
|
|
825
|
+
replacement: ', { strategies: dialogOverlayStrategy() }',
|
|
826
|
+
});
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
// If config exists, transform it
|
|
830
|
+
const configArg = node.arguments[1];
|
|
831
|
+
if (configArg && ts.isObjectLiteralExpression(configArg)) {
|
|
832
|
+
transformConfigWithStyleProperties(configArg, sourceFile, changes, 'dialogOverlayStrategy');
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
function handleBottomSheetServiceOpen(node, sourceFile, changes, importsToAdd) {
|
|
836
|
+
importsToAdd.add('bottomSheetOverlayStrategy');
|
|
837
|
+
// If no config argument, add one with strategies
|
|
838
|
+
if (node.arguments.length === 1) {
|
|
839
|
+
changes.push({
|
|
840
|
+
start: node.arguments[0].getEnd(),
|
|
841
|
+
end: node.arguments[0].getEnd(),
|
|
842
|
+
replacement: ', { strategies: bottomSheetOverlayStrategy() }',
|
|
843
|
+
});
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
// If config exists, transform it
|
|
847
|
+
const configArg = node.arguments[1];
|
|
848
|
+
if (configArg && ts.isObjectLiteralExpression(configArg)) {
|
|
849
|
+
transformConfigWithStyleProperties(configArg, sourceFile, changes, 'bottomSheetOverlayStrategy');
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
function handleDynamicOverlayServiceOpen(node, sourceFile, changes, importsToAdd) {
|
|
853
|
+
importsToAdd.add('transformingBottomSheetToDialogOverlayStrategy');
|
|
854
|
+
const configArg = node.arguments[1];
|
|
855
|
+
if (!configArg || !ts.isObjectLiteralExpression(configArg)) {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
// Extract properties from the config
|
|
859
|
+
let isDialogFrom;
|
|
860
|
+
let bottomSheetConfig;
|
|
861
|
+
let dialogConfig;
|
|
862
|
+
const otherProperties = [];
|
|
863
|
+
configArg.properties.forEach((prop) => {
|
|
864
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
865
|
+
const propName = prop.name.text;
|
|
866
|
+
if (propName === 'isDialogFrom') {
|
|
867
|
+
isDialogFrom = prop.initializer.getText(sourceFile).replace(/['"]/g, '');
|
|
868
|
+
}
|
|
869
|
+
else if (propName === 'bottomSheetConfig' && ts.isObjectLiteralExpression(prop.initializer)) {
|
|
870
|
+
bottomSheetConfig = prop.initializer;
|
|
871
|
+
}
|
|
872
|
+
else if (propName === 'dialogConfig' && ts.isObjectLiteralExpression(prop.initializer)) {
|
|
873
|
+
dialogConfig = prop.initializer;
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
otherProperties.push(prop);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
// Extract data and style properties from configs
|
|
881
|
+
let dataPropertyText;
|
|
882
|
+
const bottomSheetStyleProps = [];
|
|
883
|
+
const bottomSheetOtherProps = new Map(); // Use Map to deduplicate
|
|
884
|
+
const dialogStyleProps = [];
|
|
885
|
+
const dialogOtherProps = new Map(); // Use Map to deduplicate
|
|
886
|
+
if (bottomSheetConfig) {
|
|
887
|
+
bottomSheetConfig.properties.forEach((prop) => {
|
|
888
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
889
|
+
const propName = prop.name.text;
|
|
890
|
+
if (propName === 'data') {
|
|
891
|
+
// Store the full property assignment text
|
|
892
|
+
dataPropertyText = prop.getText(sourceFile);
|
|
893
|
+
}
|
|
894
|
+
else if (propName === 'scrollStrategy') {
|
|
895
|
+
// Skip scrollStrategy - it's no longer supported
|
|
896
|
+
}
|
|
897
|
+
else if (STYLE_PROPERTIES.includes(propName)) {
|
|
898
|
+
bottomSheetStyleProps.push(prop);
|
|
899
|
+
}
|
|
900
|
+
else {
|
|
901
|
+
bottomSheetOtherProps.set(propName, prop);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
else if (ts.isShorthandPropertyAssignment(prop) && prop.name.text === 'data') {
|
|
905
|
+
// Handle shorthand property: { data }
|
|
906
|
+
dataPropertyText = prop.getText(sourceFile);
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
if (dialogConfig) {
|
|
911
|
+
dialogConfig.properties.forEach((prop) => {
|
|
912
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
913
|
+
const propName = prop.name.text;
|
|
914
|
+
if (propName === 'scrollStrategy') {
|
|
915
|
+
// Skip scrollStrategy - it's no longer supported
|
|
916
|
+
}
|
|
917
|
+
else if (STYLE_PROPERTIES.includes(propName)) {
|
|
918
|
+
dialogStyleProps.push(prop);
|
|
919
|
+
}
|
|
920
|
+
else if (propName !== 'data') {
|
|
921
|
+
// Skip data in dialogConfig as we already got it from bottomSheetConfig
|
|
922
|
+
// Only add if not already in bottomSheetOtherProps (deduplicate)
|
|
923
|
+
if (!bottomSheetOtherProps.has(propName)) {
|
|
924
|
+
dialogOtherProps.set(propName, prop);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
// Build the new config
|
|
931
|
+
const newConfigParts = [];
|
|
932
|
+
// Add data property if exists
|
|
933
|
+
if (dataPropertyText) {
|
|
934
|
+
newConfigParts.push(dataPropertyText);
|
|
935
|
+
}
|
|
936
|
+
// Add other properties from main config
|
|
937
|
+
otherProperties.forEach((prop) => {
|
|
938
|
+
newConfigParts.push(prop.getText(sourceFile));
|
|
939
|
+
});
|
|
940
|
+
// Add other properties from nested configs (non-style, non-data) - deduplicated
|
|
941
|
+
bottomSheetOtherProps.forEach((prop) => {
|
|
942
|
+
newConfigParts.push(prop.getText(sourceFile));
|
|
943
|
+
});
|
|
944
|
+
dialogOtherProps.forEach((prop) => {
|
|
945
|
+
newConfigParts.push(prop.getText(sourceFile));
|
|
946
|
+
});
|
|
947
|
+
// Build the strategy config
|
|
948
|
+
const strategyConfigParts = [];
|
|
949
|
+
// Add bottomSheet config (only if there are style properties)
|
|
950
|
+
if (bottomSheetStyleProps.length > 0) {
|
|
951
|
+
const bottomSheetConfigStr = bottomSheetStyleProps
|
|
952
|
+
.map((prop) => `${prop.name.getText(sourceFile)}: ${prop.initializer.getText(sourceFile)}`)
|
|
953
|
+
.join(',\n ');
|
|
954
|
+
strategyConfigParts.push(`bottomSheet: {\n ${bottomSheetConfigStr}\n }`);
|
|
955
|
+
}
|
|
956
|
+
// Add dialog config (only if there are style properties)
|
|
957
|
+
if (dialogStyleProps.length > 0) {
|
|
958
|
+
const dialogConfigStr = dialogStyleProps
|
|
959
|
+
.map((prop) => `${prop.name.getText(sourceFile)}: ${prop.initializer.getText(sourceFile)}`)
|
|
960
|
+
.join(',\n ');
|
|
961
|
+
strategyConfigParts.push(`dialog: {\n ${dialogConfigStr}\n }`);
|
|
962
|
+
}
|
|
963
|
+
// Add breakpoint
|
|
964
|
+
if (isDialogFrom) {
|
|
965
|
+
strategyConfigParts.push(`breakpoint: '${isDialogFrom}'`);
|
|
966
|
+
}
|
|
967
|
+
const strategyConfig = strategyConfigParts.length > 0 ? `{\n ${strategyConfigParts.join(',\n ')}\n }` : '()';
|
|
968
|
+
newConfigParts.push(`strategies: transformingBottomSheetToDialogOverlayStrategy(${strategyConfig})`);
|
|
969
|
+
// Replace the entire config
|
|
970
|
+
const newConfig = `{\n ${newConfigParts.join(',\n ')}\n }`;
|
|
971
|
+
changes.push({
|
|
972
|
+
start: configArg.getStart(sourceFile),
|
|
973
|
+
end: configArg.getEnd(),
|
|
974
|
+
replacement: newConfig,
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
function transformConfigWithStyleProperties(configArg, sourceFile, changes, strategyFunction) {
|
|
978
|
+
const styleProps = [];
|
|
979
|
+
const otherProps = [];
|
|
980
|
+
configArg.properties.forEach((prop) => {
|
|
981
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
982
|
+
const propName = prop.name.text;
|
|
983
|
+
if (propName === 'scrollStrategy') {
|
|
984
|
+
// Skip scrollStrategy - it's no longer supported
|
|
985
|
+
}
|
|
986
|
+
else if (STYLE_PROPERTIES.includes(propName)) {
|
|
987
|
+
styleProps.push(prop);
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
otherProps.push(prop);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
else if (ts.isShorthandPropertyAssignment(prop)) {
|
|
994
|
+
const propName = prop.name.text;
|
|
995
|
+
if (propName === 'scrollStrategy') {
|
|
996
|
+
// Skip scrollStrategy - it's no longer supported
|
|
997
|
+
}
|
|
998
|
+
else {
|
|
999
|
+
otherProps.push(prop);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
else {
|
|
1003
|
+
otherProps.push(prop);
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
// If no style properties, just add strategies
|
|
1007
|
+
if (styleProps.length === 0) {
|
|
1008
|
+
// Add strategies property to existing config
|
|
1009
|
+
const lastProp = configArg.properties[configArg.properties.length - 1];
|
|
1010
|
+
if (lastProp) {
|
|
1011
|
+
changes.push({
|
|
1012
|
+
start: lastProp.getEnd(),
|
|
1013
|
+
end: lastProp.getEnd(),
|
|
1014
|
+
replacement: `,\n strategies: ${strategyFunction}()`,
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
// Build new config with style props moved to strategy
|
|
1020
|
+
const newConfigParts = [];
|
|
1021
|
+
// Add non-style properties
|
|
1022
|
+
otherProps.forEach((prop) => {
|
|
1023
|
+
newConfigParts.push(prop.getText(sourceFile));
|
|
1024
|
+
});
|
|
1025
|
+
// Build strategy config
|
|
1026
|
+
const strategyConfigParts = styleProps.map((prop) => {
|
|
1027
|
+
return `${prop.name.getText(sourceFile)}: ${prop.initializer.getText(sourceFile)}`;
|
|
1028
|
+
});
|
|
1029
|
+
const strategyConfig = `${strategyFunction}({\n ${strategyConfigParts.join(',\n ')}\n })`;
|
|
1030
|
+
newConfigParts.push(`strategies: ${strategyConfig}`);
|
|
1031
|
+
// Replace the entire config object
|
|
1032
|
+
const newConfig = `{\n ${newConfigParts.join(',\n ')}\n }`;
|
|
1033
|
+
changes.push({
|
|
1034
|
+
start: configArg.getStart(sourceFile),
|
|
1035
|
+
end: configArg.getEnd(),
|
|
1036
|
+
replacement: newConfig,
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
//# sourceMappingURL=dialog-bottom-sheet.js.map
|