@progress/kendo-angular-inputs 23.4.0-develop.1 → 23.4.0-develop.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/codemods/utils.js +1430 -0
- package/codemods/v19/colorpicker-closebuttontitle.js +50 -0
- package/codemods/v19/colorpicker-subtitle.js +50 -0
- package/codemods/v19/colorpicker-title.js +50 -0
- package/codemods/v19/inputs-rendering-changes.js +37 -0
- package/fesm2022/progress-kendo-angular-inputs.mjs +3 -3
- package/package-metadata.mjs +2 -2
- package/package.json +42 -12
|
@@ -0,0 +1,1430 @@
|
|
|
1
|
+
/**-----------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright © 2026 Progress Software Corporation. All rights reserved.
|
|
3
|
+
* Licensed under commercial license. See LICENSE.md in the project root for more information
|
|
4
|
+
*-------------------------------------------------------------------------------------------*/
|
|
5
|
+
"use strict";
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
23
|
+
if (mod && mod.__esModule) return mod;
|
|
24
|
+
var result = {};
|
|
25
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
26
|
+
__setModuleDefault(result, mod);
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.tsInterfaceTransformer = exports.tsPropertyValueTransformer = exports.tsPropertyTransformer = exports.tsComponentPropertyRemoval = exports.attributeRemoval = exports.attributeValueUpdate = exports.attributeNameValueUpdate = exports.attributeNameUpdate = exports.eventUpdate = exports.htmlTransformer = exports.blockTextElements = void 0;
|
|
31
|
+
exports.hasKendoInTemplate = hasKendoInTemplate;
|
|
32
|
+
exports.isImportedFromPackage = isImportedFromPackage;
|
|
33
|
+
exports.tsPropertyRemoval = tsPropertyRemoval;
|
|
34
|
+
exports.makePattern = makePattern;
|
|
35
|
+
exports.writeInstructionMarker = writeInstructionMarker;
|
|
36
|
+
exports.isApiChangeTarget = isApiChangeTarget;
|
|
37
|
+
exports.isRenderingChangeTarget = isRenderingChangeTarget;
|
|
38
|
+
const fs = __importStar(require("node:fs"));
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
exports.blockTextElements = {
|
|
41
|
+
script: true,
|
|
42
|
+
noscript: true,
|
|
43
|
+
style: true,
|
|
44
|
+
pre: true,
|
|
45
|
+
};
|
|
46
|
+
function hasKendoInTemplate(source) {
|
|
47
|
+
const kendoPattern = /(<kendo-[^>\s]+|<[^>]*\s+kendo[A-Z][^>\s]*)/g;
|
|
48
|
+
return kendoPattern.test(source);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Checks if a specific type/interface is imported from the given package
|
|
52
|
+
* @param root - The JSCodeshift collection
|
|
53
|
+
* @param j - JSCodeshift instance
|
|
54
|
+
* @param packageName - The package name to check (e.g., '@progress/kendo-angular-dateinputs')
|
|
55
|
+
* @param typeName - The type/interface name to check (e.g., 'DatePickerComponent')
|
|
56
|
+
* @returns true if the type is imported from the package, false otherwise
|
|
57
|
+
*/
|
|
58
|
+
function isImportedFromPackage(root, j, packageName, typeName) {
|
|
59
|
+
let isImported = false;
|
|
60
|
+
root.find(j.ImportDeclaration).forEach((path) => {
|
|
61
|
+
if (path.node.source &&
|
|
62
|
+
path.node.source.value === packageName &&
|
|
63
|
+
path.node.specifiers) {
|
|
64
|
+
path.node.specifiers.forEach((specifier) => {
|
|
65
|
+
if (specifier.type === 'ImportSpecifier' &&
|
|
66
|
+
specifier.imported.type === 'Identifier' &&
|
|
67
|
+
specifier.imported.name === typeName) {
|
|
68
|
+
isImported = true;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return isImported;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Transforms HTML files and inline templates using a provided transformer function
|
|
77
|
+
*
|
|
78
|
+
* @param fileInfo - The file info containing path and source
|
|
79
|
+
* @param api - JSCodeshift API
|
|
80
|
+
* @param transformerFn - The transformer function to apply to template content
|
|
81
|
+
* @returns The transformed source code or undefined if no changes
|
|
82
|
+
*/
|
|
83
|
+
const htmlTransformer = (fileInfo, api, transformerFn) => {
|
|
84
|
+
const filePath = fileInfo.path;
|
|
85
|
+
if (filePath.endsWith('.html')) {
|
|
86
|
+
if (hasKendoInTemplate(fileInfo.source)) {
|
|
87
|
+
let updatedContent = fileInfo.source;
|
|
88
|
+
updatedContent = transformerFn(updatedContent);
|
|
89
|
+
return updatedContent;
|
|
90
|
+
}
|
|
91
|
+
return fileInfo.source;
|
|
92
|
+
}
|
|
93
|
+
const j = api.jscodeshift;
|
|
94
|
+
const rootSource = j(fileInfo.source);
|
|
95
|
+
// Transform inline templates using the provided transformer function
|
|
96
|
+
rootSource
|
|
97
|
+
.find(j.ClassDeclaration)
|
|
98
|
+
.forEach(classPath => {
|
|
99
|
+
const classNode = classPath.node;
|
|
100
|
+
if (!classNode.decorators || !classNode.decorators.length)
|
|
101
|
+
return;
|
|
102
|
+
const componentDecorator = classNode.decorators.find((decorator) => {
|
|
103
|
+
if (decorator.expression && decorator.expression.type === 'CallExpression') {
|
|
104
|
+
const callee = decorator.expression.callee;
|
|
105
|
+
if (callee.type === 'Identifier' && callee.name === 'Component') {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (callee.type === 'MemberExpression' &&
|
|
109
|
+
callee.property &&
|
|
110
|
+
callee.property.type === 'Identifier' &&
|
|
111
|
+
callee.property.name === 'Component') {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
});
|
|
117
|
+
if (!componentDecorator || !componentDecorator.expression)
|
|
118
|
+
return;
|
|
119
|
+
const expression = componentDecorator.expression;
|
|
120
|
+
if (expression.type !== 'CallExpression' || !expression.arguments.length)
|
|
121
|
+
return;
|
|
122
|
+
const componentOptions = expression.arguments[0];
|
|
123
|
+
if (componentOptions.type !== 'ObjectExpression')
|
|
124
|
+
return;
|
|
125
|
+
const props = componentOptions.properties || [];
|
|
126
|
+
const templateProp = props.find((prop) => (prop.key.type === 'Identifier' && prop.key.name === 'template') ||
|
|
127
|
+
(prop.key.type === 'StringLiteral' && prop.key.value === 'template'));
|
|
128
|
+
if (templateProp) {
|
|
129
|
+
let originalTemplate;
|
|
130
|
+
if (templateProp.value.type === 'StringLiteral' || templateProp.value.type === 'Literal') {
|
|
131
|
+
originalTemplate = templateProp.value.value;
|
|
132
|
+
}
|
|
133
|
+
else if (templateProp.value.type === 'TemplateLiteral') {
|
|
134
|
+
if (templateProp.value.quasis && templateProp.value.quasis.length) {
|
|
135
|
+
originalTemplate = templateProp.value.quasis
|
|
136
|
+
.map((q) => q.value.cooked || q.value.raw)
|
|
137
|
+
.join('');
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (hasKendoInTemplate(originalTemplate)) {
|
|
147
|
+
// Apply the provided transformer function
|
|
148
|
+
const transformedTemplate = transformerFn(originalTemplate);
|
|
149
|
+
if (transformedTemplate !== originalTemplate) {
|
|
150
|
+
if (templateProp.value.type === 'TemplateLiteral') {
|
|
151
|
+
templateProp.value = j.templateLiteral([j.templateElement({ cooked: transformedTemplate, raw: transformedTemplate }, true)], []);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
templateProp.value.value = transformedTemplate;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
return rootSource.toSource();
|
|
161
|
+
};
|
|
162
|
+
exports.htmlTransformer = htmlTransformer;
|
|
163
|
+
const eventUpdate = (templateContent, tagName, oldEventName, newEventName) => {
|
|
164
|
+
// Escape special regex characters in tag name
|
|
165
|
+
const escapedTagName = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
166
|
+
// Create regex pattern to match the tag with the old event binding
|
|
167
|
+
// Pattern matches: <tagName ...attributes... (oldEventName)="handler" ...>
|
|
168
|
+
const eventPattern = new RegExp(`(<${escapedTagName}[^>]*\\()${oldEventName}(\\)=)`, 'g');
|
|
169
|
+
// Replace old event name with new event name
|
|
170
|
+
const updatedContent = templateContent.replace(eventPattern, `$1${newEventName}$2`);
|
|
171
|
+
return updatedContent;
|
|
172
|
+
};
|
|
173
|
+
exports.eventUpdate = eventUpdate;
|
|
174
|
+
/**
|
|
175
|
+
* Transforms attributes in inline templates using regex patterns.
|
|
176
|
+
* This function handles bound ([attribute]), static (attribute="value"),
|
|
177
|
+
* two-way binding ([(attribute)]), and boolean (attribute) attributes
|
|
178
|
+
* within a specific tag using regular expressions.
|
|
179
|
+
*
|
|
180
|
+
* @param templateContent - The template string content to transform
|
|
181
|
+
* @param tagName - The HTML tag name to target (e.g., 'kendo-datepicker')
|
|
182
|
+
* @param attributeName - The current attribute name to replace
|
|
183
|
+
* @param newAttributeName - The new attribute name
|
|
184
|
+
* @returns The transformed template content
|
|
185
|
+
*/
|
|
186
|
+
const attributeNameUpdate = (templateContent, tagName, attributeName, newAttributeName) => {
|
|
187
|
+
// Escape special regex characters in tag and attribute names
|
|
188
|
+
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
189
|
+
const escapedTag = escapeRegex(tagName);
|
|
190
|
+
const escapedAttr = escapeRegex(attributeName);
|
|
191
|
+
const escapedNewAttr = escapeRegex(newAttributeName);
|
|
192
|
+
// Pattern to match the opening tag with the attribute
|
|
193
|
+
// This pattern matches: <tagName ... attribute="value" ... > or <tagName ... [attribute]="value" ... >
|
|
194
|
+
// It captures the tag opening, everything before the attribute, the attribute binding type, and everything after
|
|
195
|
+
const boundAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+)\\[${escapedAttr}\\]\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?')([^>]*?>)`, 'gi');
|
|
196
|
+
const staticAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+)${escapedAttr}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?')([^>]*?>)`, 'gi');
|
|
197
|
+
// Pattern for two-way data binding [(attribute)]="value"
|
|
198
|
+
const twoWayBindingPattern = new RegExp(`(<${escapedTag}[^>]*?\\s+)\\[\\(${escapedAttr}\\)\\]\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?')([^>]*?>)`, 'gi');
|
|
199
|
+
// Pattern for boolean attributes without values (e.g., <tag attribute>)
|
|
200
|
+
const booleanAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+)${escapedAttr}(\\s+[^>=]|\\s*[/>])`, 'gi');
|
|
201
|
+
// Replace two-way data binding [(attribute)]="value"
|
|
202
|
+
let result = templateContent.replace(twoWayBindingPattern, `$1[(${escapedNewAttr})]=$2$3`);
|
|
203
|
+
// Replace bound attributes [attribute]="value"
|
|
204
|
+
result = result.replace(boundAttributePattern, `$1[${escapedNewAttr}]=$2$3`);
|
|
205
|
+
// Replace static attributes attribute="value"
|
|
206
|
+
result = result.replace(staticAttributePattern, `$1${escapedNewAttr}=$2$3`);
|
|
207
|
+
// Replace boolean attributes attribute (without value)
|
|
208
|
+
result = result.replace(booleanAttributePattern, `$1${escapedNewAttr}$2`);
|
|
209
|
+
return result;
|
|
210
|
+
};
|
|
211
|
+
exports.attributeNameUpdate = attributeNameUpdate;
|
|
212
|
+
/**
|
|
213
|
+
* Transforms bound attributes with value property extraction using regex patterns.
|
|
214
|
+
* This function handles bound attributes and extracts a specific property from object literals
|
|
215
|
+
* or appends the property to variable references.
|
|
216
|
+
*
|
|
217
|
+
* @param templateContent - The template string content to transform
|
|
218
|
+
* @param tagName - The HTML tag name to target (e.g., 'kendo-chat')
|
|
219
|
+
* @param attributeName - The current attribute name to replace
|
|
220
|
+
* @param newAttributeName - The new attribute name
|
|
221
|
+
* @param valueProperty - The property to extract (e.g., 'id')
|
|
222
|
+
* @returns The transformed template content
|
|
223
|
+
*/
|
|
224
|
+
const attributeNameValueUpdate = (templateContent, tagName, attributeName, newAttributeName, valueProperty) => {
|
|
225
|
+
// Escape special regex characters in tag and attribute names
|
|
226
|
+
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
227
|
+
const escapedTag = escapeRegex(tagName);
|
|
228
|
+
const escapedAttr = escapeRegex(attributeName);
|
|
229
|
+
const escapedNewAttr = escapeRegex(newAttributeName);
|
|
230
|
+
// Pattern to match bound attributes [attribute]="value"
|
|
231
|
+
// This captures: opening tag, bound attribute, and closing tag
|
|
232
|
+
const boundAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+)\\[${escapedAttr}\\]\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?'|[^\\s>]+)([^>]*?>)`, 'gi');
|
|
233
|
+
return templateContent.replace(boundAttributePattern, (match, beforeAttr, value, afterAttr) => {
|
|
234
|
+
// Remove quotes from value to analyze it
|
|
235
|
+
const trimmedValue = value.replace(/^["']|["']$/g, '');
|
|
236
|
+
// Check if it's an object literal (starts with { and ends with })
|
|
237
|
+
if (trimmedValue.trim().startsWith('{') && trimmedValue.trim().endsWith('}')) {
|
|
238
|
+
// For object literals like {id: 'foo'} or {id: 'foo', bar: 'baz'}
|
|
239
|
+
// We need to append .id inside the quotes if the value is quoted
|
|
240
|
+
if (value.match(/^["']/)) {
|
|
241
|
+
// Extract quote type and content, then add .id before the closing quote
|
|
242
|
+
const quoteType = value.charAt(0);
|
|
243
|
+
const content = value.slice(1, -1); // Remove quotes
|
|
244
|
+
const newValue = `${quoteType}${content}.${valueProperty}${quoteType}`;
|
|
245
|
+
return `${beforeAttr}[${escapedNewAttr}]=${newValue}${afterAttr}`;
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// Unquoted object literal
|
|
249
|
+
const newValue = `${value}.${valueProperty}`;
|
|
250
|
+
return `${beforeAttr}[${escapedNewAttr}]=${newValue}${afterAttr}`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
// For variable references like "user"
|
|
255
|
+
// We append .id to the variable name
|
|
256
|
+
const newValue = value.replace(/^["'](.*)["']$/, (_match, content) => {
|
|
257
|
+
return `"${content}.${valueProperty}"`;
|
|
258
|
+
});
|
|
259
|
+
// If it's not quoted (bare variable), add quotes and property
|
|
260
|
+
if (!value.match(/^["']/)) {
|
|
261
|
+
return `${beforeAttr}[${escapedNewAttr}]="${trimmedValue}.${valueProperty}"${afterAttr}`;
|
|
262
|
+
}
|
|
263
|
+
return `${beforeAttr}[${escapedNewAttr}]=${newValue}${afterAttr}`;
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
exports.attributeNameValueUpdate = attributeNameValueUpdate;
|
|
268
|
+
/**
|
|
269
|
+
* Updates attribute values in HTML templates using regex patterns.
|
|
270
|
+
* This function handles both bound ([attribute]="'value'") and static (attribute="value") attributes
|
|
271
|
+
* and replaces old values with new values within a specific tag.
|
|
272
|
+
*
|
|
273
|
+
* @param templateContent - The template string content to transform
|
|
274
|
+
* @param tagName - The HTML tag name to target (e.g., 'kendo-toolbar')
|
|
275
|
+
* @param attributeName - The attribute name to target (e.g., 'showIcon')
|
|
276
|
+
* @param oldValue - The old attribute value to replace (e.g., 'overflow')
|
|
277
|
+
* @param newValue - The new attribute value (e.g., 'menu')
|
|
278
|
+
* @returns The transformed template content
|
|
279
|
+
*/
|
|
280
|
+
const attributeValueUpdate = (templateContent, tagName, attributeName, oldValue, newValue) => {
|
|
281
|
+
// Escape special regex characters in tag, attribute names, and values
|
|
282
|
+
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
283
|
+
const escapedTag = escapeRegex(tagName);
|
|
284
|
+
const escapedAttr = escapeRegex(attributeName);
|
|
285
|
+
const escapedOldValue = escapeRegex(oldValue);
|
|
286
|
+
// Pattern for bound attributes [attribute]="'value'" (double quotes containing single quotes)
|
|
287
|
+
const boundDoubleSinglePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+\\[${escapedAttr}\\]\\s*=\\s*")'${escapedOldValue}'("\\s*[^>]*?>)`, 'gi');
|
|
288
|
+
// Pattern for bound attributes [attribute]="\"value\"" (double quotes containing double quotes)
|
|
289
|
+
const boundDoubleDoublePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+\\[${escapedAttr}\\]\\s*=\\s*")\\\\"${escapedOldValue}\\\\"("\\s*[^>]*?>)`, 'gi');
|
|
290
|
+
// Pattern for bound attributes [attribute]='value' (single quotes containing value)
|
|
291
|
+
const boundSinglePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+\\[${escapedAttr}\\]\\s*=\\s*)'${escapedOldValue}'([^>]*?>)`, 'gi');
|
|
292
|
+
// Pattern for static attributes attribute="value"
|
|
293
|
+
const staticAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+${escapedAttr}\\s*=\\s*["'])${escapedOldValue}(["'][^>]*?>)`, 'gi');
|
|
294
|
+
// Replace bound attributes [attribute]="'overflow'" -> [attribute]="'menu'"
|
|
295
|
+
let result = templateContent.replace(boundDoubleSinglePattern, `$1'${newValue}'$2`);
|
|
296
|
+
// Replace bound attributes [attribute]="\"overflow\"" -> [attribute]="\"menu\""
|
|
297
|
+
result = result.replace(boundDoubleDoublePattern, `$1\\"${newValue}\\"$2`);
|
|
298
|
+
// Replace bound attributes [attribute]='overflow' -> [attribute]='menu'
|
|
299
|
+
result = result.replace(boundSinglePattern, `$1'${newValue}'$2`);
|
|
300
|
+
// Replace static attributes showIcon="overflow" -> showIcon="menu"
|
|
301
|
+
result = result.replace(staticAttributePattern, `$1${newValue}$2`);
|
|
302
|
+
return result;
|
|
303
|
+
};
|
|
304
|
+
exports.attributeValueUpdate = attributeValueUpdate;
|
|
305
|
+
/**
|
|
306
|
+
* Removes attributes from HTML templates using regex patterns.
|
|
307
|
+
* This function can remove entire attributes or specific properties from object literal attributes.
|
|
308
|
+
*
|
|
309
|
+
* @param templateContent - The template string content to transform
|
|
310
|
+
* @param tagName - The HTML tag name to target (e.g., 'kendo-chat')
|
|
311
|
+
* @param attributeName - The attribute name to remove or modify
|
|
312
|
+
* @param propertyToRemove - Optional: specific property to remove from object literal attributes
|
|
313
|
+
* @returns The transformed template content
|
|
314
|
+
*/
|
|
315
|
+
const attributeRemoval = (templateContent, tagName, attributeName, propertyToRemove) => {
|
|
316
|
+
// Escape special regex characters in tag and attribute names
|
|
317
|
+
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
318
|
+
const escapedTag = escapeRegex(tagName);
|
|
319
|
+
const escapedAttr = escapeRegex(attributeName);
|
|
320
|
+
// If no propertyToRemove is specified, remove the entire attribute
|
|
321
|
+
if (!propertyToRemove) {
|
|
322
|
+
// Remove bound attributes [attribute]="value"
|
|
323
|
+
const boundAttributePattern = new RegExp(`(\\s+)\\[${escapedAttr}\\]\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?'|[^\\s>]+)`, 'gi');
|
|
324
|
+
// Remove static attributes attribute="value"
|
|
325
|
+
const staticAttributePattern = new RegExp(`(\\s+)${escapedAttr}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?'|[^\\s>]+)`, 'gi');
|
|
326
|
+
// Apply removals
|
|
327
|
+
let result = templateContent.replace(boundAttributePattern, '');
|
|
328
|
+
result = result.replace(staticAttributePattern, '');
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
// Remove specific property from object literal attributes
|
|
332
|
+
const boundAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+\\[${escapedAttr}\\]\\s*=\\s*)("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?'|[^\\s>]+)([^>]*?>)`, 'gi');
|
|
333
|
+
return templateContent.replace(boundAttributePattern, (match, beforeAttr, value, afterAttr) => {
|
|
334
|
+
// Remove quotes from value to analyze it
|
|
335
|
+
const trimmedValue = value.replace(/^["']|["']$/g, '');
|
|
336
|
+
// Check if it's an object literal (starts with { and ends with })
|
|
337
|
+
if (trimmedValue.trim().startsWith('{') && trimmedValue.trim().endsWith('}')) {
|
|
338
|
+
const objectLiteral = trimmedValue.trim();
|
|
339
|
+
// Create regex to remove the specific property
|
|
340
|
+
const propRegex = new RegExp(`\\s*${escapeRegex(propertyToRemove)}\\s*:\\s*[^,}]+\\s*(,\\s*)?`, 'g');
|
|
341
|
+
let newObjectLiteral = objectLiteral.replace(propRegex, '');
|
|
342
|
+
// Clean up trailing comma before closing brace
|
|
343
|
+
newObjectLiteral = newObjectLiteral.replace(/,\s*}$/, '}');
|
|
344
|
+
// If the object is now empty, remove the entire attribute
|
|
345
|
+
if (newObjectLiteral === '{}') {
|
|
346
|
+
return beforeAttr.replace(/\s+\[[^\]]+\]\s*=\s*$/, '') + afterAttr;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
// Restore quotes if the original value was quoted
|
|
350
|
+
if (value.match(/^["']/)) {
|
|
351
|
+
const quoteType = value.charAt(0);
|
|
352
|
+
return `${beforeAttr}${quoteType}${newObjectLiteral}${quoteType}${afterAttr}`;
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
return `${beforeAttr}${newObjectLiteral}${afterAttr}`;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
// For non-object literals, we can't remove a specific property
|
|
361
|
+
console.warn(`Cannot remove property '${propertyToRemove}' from non-object literal: ${value}`);
|
|
362
|
+
return match; // Return unchanged
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
exports.attributeRemoval = attributeRemoval;
|
|
367
|
+
function tsPropertyRemoval(source, rootSource, j, packageName, typeName, propertyName) {
|
|
368
|
+
if (source.includes(typeName)) {
|
|
369
|
+
if (!isImportedFromPackage(rootSource, j, packageName, typeName)) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const typeVariables = new Set();
|
|
373
|
+
// Find function parameters of target type
|
|
374
|
+
rootSource.find(j.Function).forEach((path) => {
|
|
375
|
+
if (path.node.params) {
|
|
376
|
+
path.node.params.forEach((param) => {
|
|
377
|
+
if (param.type === 'Identifier' &&
|
|
378
|
+
param.typeAnnotation &&
|
|
379
|
+
param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
380
|
+
param.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
|
|
381
|
+
param.typeAnnotation.typeAnnotation.typeName.name === typeName) {
|
|
382
|
+
typeVariables.add(param.name);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
// Find local variables of target type
|
|
388
|
+
rootSource.find(j.VariableDeclarator).forEach((path) => {
|
|
389
|
+
if (path.node.id.type === 'Identifier' &&
|
|
390
|
+
path.node.id.typeAnnotation &&
|
|
391
|
+
path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
392
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
|
|
393
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.name === typeName) {
|
|
394
|
+
typeVariables.add(path.node.id.name);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
// Find class properties of target type
|
|
398
|
+
rootSource.find(j.ClassProperty).forEach((path) => {
|
|
399
|
+
if (path.node.key.type === 'Identifier' &&
|
|
400
|
+
path.node.typeAnnotation &&
|
|
401
|
+
path.node.typeAnnotation.typeAnnotation &&
|
|
402
|
+
path.node.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
|
|
403
|
+
path.node.typeAnnotation.typeAnnotation.typeName &&
|
|
404
|
+
path.node.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
405
|
+
path.node.typeAnnotation.typeAnnotation.typeName.name === typeName) {
|
|
406
|
+
typeVariables.add(path.node.key.name);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
// Handle class properties with object expressions
|
|
410
|
+
rootSource
|
|
411
|
+
.find(j.ClassProperty)
|
|
412
|
+
.filter((path) => {
|
|
413
|
+
// Check if the property has the correct type annotation
|
|
414
|
+
return !!(path.node.typeAnnotation &&
|
|
415
|
+
path.node.typeAnnotation.typeAnnotation &&
|
|
416
|
+
path.node.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
|
|
417
|
+
path.node.typeAnnotation.typeAnnotation.typeName &&
|
|
418
|
+
path.node.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
419
|
+
path.node.typeAnnotation.typeAnnotation.typeName.name === typeName);
|
|
420
|
+
})
|
|
421
|
+
.forEach((path) => {
|
|
422
|
+
if (path.node.value && path.node.value.type === 'ObjectExpression') {
|
|
423
|
+
const properties = path.node.value.properties;
|
|
424
|
+
const propIndex = properties.findIndex((p) => p.type === 'ObjectProperty' &&
|
|
425
|
+
p.key &&
|
|
426
|
+
p.key.type === 'Identifier' &&
|
|
427
|
+
p.key.name === propertyName);
|
|
428
|
+
if (propIndex !== -1) {
|
|
429
|
+
// Just remove the property, leaving an empty object if it was the only one
|
|
430
|
+
properties.splice(propIndex, 1);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
// Handle variable declarations with object literal initializers
|
|
435
|
+
// e.g., const modelFields: ConversationalUIModelFields = { pinnedByField: 'value', id: 'bar' };
|
|
436
|
+
rootSource
|
|
437
|
+
.find(j.VariableDeclarator, {
|
|
438
|
+
id: {
|
|
439
|
+
type: 'Identifier',
|
|
440
|
+
typeAnnotation: {
|
|
441
|
+
typeAnnotation: {
|
|
442
|
+
type: 'TSTypeReference',
|
|
443
|
+
typeName: {
|
|
444
|
+
name: typeName,
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
})
|
|
450
|
+
.forEach((path) => {
|
|
451
|
+
if (path.node.init && path.node.init.type === 'ObjectExpression') {
|
|
452
|
+
const properties = path.node.init.properties;
|
|
453
|
+
const propIndex = properties.findIndex((p) => p.type === 'ObjectProperty' &&
|
|
454
|
+
p.key &&
|
|
455
|
+
p.key.type === 'Identifier' &&
|
|
456
|
+
p.key.name === propertyName);
|
|
457
|
+
if (propIndex !== -1) {
|
|
458
|
+
// Just remove the property, leaving an empty object if it was the only one
|
|
459
|
+
properties.splice(propIndex, 1);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
// Handle return statements with object literals
|
|
464
|
+
rootSource.find(j.ReturnStatement).forEach((path) => {
|
|
465
|
+
if (path.node.argument && path.node.argument.type === 'ObjectExpression') {
|
|
466
|
+
const properties = path.node.argument.properties;
|
|
467
|
+
const propIndex = properties.findIndex((p) => p.type === 'ObjectProperty' &&
|
|
468
|
+
p.key &&
|
|
469
|
+
p.key.type === 'Identifier' &&
|
|
470
|
+
p.key.name === propertyName);
|
|
471
|
+
if (propIndex !== -1) {
|
|
472
|
+
properties.splice(propIndex, 1);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
// Handle direct member expression assignments to properties of variables/parameters
|
|
477
|
+
// e.g., fields.pinnedByField = value; or this.chatModelFields.pinnedByField = value;
|
|
478
|
+
rootSource
|
|
479
|
+
.find(j.AssignmentExpression)
|
|
480
|
+
.filter((path) => {
|
|
481
|
+
const { left } = path.node;
|
|
482
|
+
if (left.type === 'MemberExpression' &&
|
|
483
|
+
left.property.type === 'Identifier' &&
|
|
484
|
+
left.property.name === propertyName) {
|
|
485
|
+
// Check if the object is a variable/parameter of our type
|
|
486
|
+
if (left.object.type === 'Identifier' && typeVariables.has(left.object.name)) {
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
// Check if it's this.property where property is of our type
|
|
490
|
+
if (left.object.type === 'MemberExpression' &&
|
|
491
|
+
left.object.object.type === 'ThisExpression' &&
|
|
492
|
+
left.object.property.type === 'Identifier' &&
|
|
493
|
+
typeVariables.has(left.object.property.name)) {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
// Check for type assertions: (expr as Type).property
|
|
497
|
+
if (left.object.type === 'TSAsExpression' &&
|
|
498
|
+
left.object.typeAnnotation.type === 'TSTypeReference' &&
|
|
499
|
+
left.object.typeAnnotation.typeName?.type === 'Identifier' &&
|
|
500
|
+
left.object.typeAnnotation.typeName.name === typeName) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return false;
|
|
505
|
+
})
|
|
506
|
+
.forEach((path) => {
|
|
507
|
+
// Remove the entire expression statement
|
|
508
|
+
const statement = j(path).closest(j.ExpressionStatement);
|
|
509
|
+
if (statement.length > 0) {
|
|
510
|
+
statement.remove();
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
// Handle nested member expressions like chatConfig.chat.modelFields.pinnedByField
|
|
514
|
+
rootSource
|
|
515
|
+
.find(j.AssignmentExpression, {
|
|
516
|
+
left: {
|
|
517
|
+
type: 'MemberExpression',
|
|
518
|
+
object: {
|
|
519
|
+
type: 'MemberExpression',
|
|
520
|
+
},
|
|
521
|
+
property: {
|
|
522
|
+
name: propertyName,
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
})
|
|
526
|
+
.forEach((path) => {
|
|
527
|
+
j(path).closest(j.ExpressionStatement).remove();
|
|
528
|
+
});
|
|
529
|
+
return rootSource;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
const tsComponentPropertyRemoval = (source, root, j, packageName, componentType, componentProperty, propertyToRemove) => {
|
|
533
|
+
if (source.includes(componentType)) {
|
|
534
|
+
// Check if componentType is imported from the specified package
|
|
535
|
+
if (!isImportedFromPackage(root, j, packageName, componentType)) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
// Find all class properties that are of type componentType
|
|
539
|
+
const properties = new Set();
|
|
540
|
+
root.find(j.ClassProperty, {
|
|
541
|
+
typeAnnotation: {
|
|
542
|
+
typeAnnotation: {
|
|
543
|
+
typeName: {
|
|
544
|
+
name: componentType,
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
}).forEach((path) => {
|
|
549
|
+
if (path.node.key.type === 'Identifier') {
|
|
550
|
+
properties.add(path.node.key.name);
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
// Find function parameters of type componentType
|
|
554
|
+
const parameters = new Set();
|
|
555
|
+
root.find(j.FunctionDeclaration).forEach((path) => {
|
|
556
|
+
if (path.node.params) {
|
|
557
|
+
path.node.params.forEach((param) => {
|
|
558
|
+
if (param.type === 'Identifier' &&
|
|
559
|
+
param.typeAnnotation &&
|
|
560
|
+
param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
561
|
+
param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
562
|
+
param.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
563
|
+
parameters.add(param.name);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
// Also check method declarations in classes
|
|
569
|
+
root.find(j.ClassMethod).forEach((path) => {
|
|
570
|
+
if (path.node.params) {
|
|
571
|
+
path.node.params.forEach((param) => {
|
|
572
|
+
if (param.type === 'Identifier' &&
|
|
573
|
+
param.typeAnnotation &&
|
|
574
|
+
param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
575
|
+
param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
576
|
+
param.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
577
|
+
parameters.add(param.name);
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
// Also check arrow functions
|
|
583
|
+
root.find(j.ArrowFunctionExpression).forEach((path) => {
|
|
584
|
+
if (path.node.params) {
|
|
585
|
+
path.node.params.forEach((param) => {
|
|
586
|
+
if (param.type === 'Identifier' &&
|
|
587
|
+
param.typeAnnotation &&
|
|
588
|
+
param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
589
|
+
param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
590
|
+
param.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
591
|
+
parameters.add(param.name);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
// Find local variable declarations of type componentType
|
|
597
|
+
const localVariables = new Set();
|
|
598
|
+
root.find(j.VariableDeclarator).forEach((path) => {
|
|
599
|
+
if (path.node.id.type === 'Identifier' &&
|
|
600
|
+
path.node.id.typeAnnotation &&
|
|
601
|
+
path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
602
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
603
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
604
|
+
localVariables.add(path.node.id.name);
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
// Find array variables of type componentType[]
|
|
608
|
+
// This handles cases like: const arr: ChatComponent[] = [...]; arr[0].property = value;
|
|
609
|
+
const arrayVariables = new Set();
|
|
610
|
+
root.find(j.VariableDeclarator).forEach((path) => {
|
|
611
|
+
if (path.node.id.type === 'Identifier' &&
|
|
612
|
+
path.node.id.typeAnnotation &&
|
|
613
|
+
path.node.id.typeAnnotation.typeAnnotation?.type === 'TSArrayType' &&
|
|
614
|
+
path.node.id.typeAnnotation.typeAnnotation.elementType?.type === 'TSTypeReference' &&
|
|
615
|
+
path.node.id.typeAnnotation.typeAnnotation.elementType.typeName?.type === 'Identifier' &&
|
|
616
|
+
path.node.id.typeAnnotation.typeAnnotation.elementType.typeName.name === componentType) {
|
|
617
|
+
arrayVariables.add(path.node.id.name);
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
// Find object properties that have componentType (e.g., {chat: ChatComponent})
|
|
621
|
+
// This handles cases like: const config: {chat: ChatComponent} = {...}; config.chat.property = value;
|
|
622
|
+
const objectProperties = new Map(); // Maps variable name to property name
|
|
623
|
+
root.find(j.VariableDeclarator).forEach((path) => {
|
|
624
|
+
if (path.node.id.type === 'Identifier' &&
|
|
625
|
+
path.node.id.typeAnnotation &&
|
|
626
|
+
path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeLiteral') {
|
|
627
|
+
const varName = path.node.id.name;
|
|
628
|
+
const members = path.node.id.typeAnnotation.typeAnnotation.members;
|
|
629
|
+
members.forEach((member) => {
|
|
630
|
+
if (member.type === 'TSPropertySignature' &&
|
|
631
|
+
member.key &&
|
|
632
|
+
member.key.type === 'Identifier' &&
|
|
633
|
+
member.typeAnnotation &&
|
|
634
|
+
member.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
635
|
+
member.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
|
|
636
|
+
member.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
637
|
+
if (!objectProperties.has(varName)) {
|
|
638
|
+
objectProperties.set(varName, []);
|
|
639
|
+
}
|
|
640
|
+
objectProperties.get(varName).push(member.key.name);
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
// If no propertyToRemove is specified, remove the entire componentProperty
|
|
646
|
+
if (!propertyToRemove) {
|
|
647
|
+
// Handle direct property assignments like: foo.scrollable = value;
|
|
648
|
+
root.find(j.AssignmentExpression)
|
|
649
|
+
.filter((path) => {
|
|
650
|
+
const { left } = path.value;
|
|
651
|
+
// Check if this assigns to component.componentProperty
|
|
652
|
+
if (left &&
|
|
653
|
+
left.type === 'MemberExpression' &&
|
|
654
|
+
left.property &&
|
|
655
|
+
left.property.name === componentProperty) {
|
|
656
|
+
// Check if the base object is our component type
|
|
657
|
+
return isComponentTypeMatch(root, j, left.object, componentType);
|
|
658
|
+
}
|
|
659
|
+
return false;
|
|
660
|
+
})
|
|
661
|
+
.forEach((path) => {
|
|
662
|
+
// Remove the entire statement
|
|
663
|
+
j(path).closest(j.ExpressionStatement).remove();
|
|
664
|
+
});
|
|
665
|
+
return root;
|
|
666
|
+
}
|
|
667
|
+
// CASE 1: Handle direct property assignments like: foo.scrollable.mouseScrollSpeed = 3000;
|
|
668
|
+
root.find(j.AssignmentExpression)
|
|
669
|
+
.filter((path) => {
|
|
670
|
+
const { left } = path.value;
|
|
671
|
+
// Check if this is a member expression assignment
|
|
672
|
+
if (left && left.type === 'MemberExpression') {
|
|
673
|
+
// Check if we're accessing the property to remove
|
|
674
|
+
if (left.property && left.property.name === propertyToRemove) {
|
|
675
|
+
// Check if we're accessing it from component.componentProperty
|
|
676
|
+
const obj = left.object;
|
|
677
|
+
if (obj &&
|
|
678
|
+
obj.type === 'MemberExpression' &&
|
|
679
|
+
obj.property &&
|
|
680
|
+
obj.property.name === componentProperty) {
|
|
681
|
+
// Now check if the base object is our component type (includes all cases: this, parameters, variables, casts)
|
|
682
|
+
return isComponentTypeMatch(root, j, obj.object, componentType);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return false;
|
|
687
|
+
})
|
|
688
|
+
.forEach((path) => {
|
|
689
|
+
// Remove the entire statement
|
|
690
|
+
j(path).closest(j.ExpressionStatement).remove();
|
|
691
|
+
});
|
|
692
|
+
// CASE 2 & 3: Handle object assignments like: foo.scrollable = { mouseScrollSpeed: 3000, ... };
|
|
693
|
+
root.find(j.AssignmentExpression)
|
|
694
|
+
.filter((path) => {
|
|
695
|
+
const { left, right } = path.value;
|
|
696
|
+
// Check if this assigns to component.componentProperty
|
|
697
|
+
if (left &&
|
|
698
|
+
left.type === 'MemberExpression' &&
|
|
699
|
+
left.property &&
|
|
700
|
+
left.property.name === componentProperty &&
|
|
701
|
+
right &&
|
|
702
|
+
right.type === 'ObjectExpression') {
|
|
703
|
+
// Check if the base object is our component type (includes all cases: this, parameters, variables, casts)
|
|
704
|
+
return isComponentTypeMatch(root, j, left.object, componentType);
|
|
705
|
+
}
|
|
706
|
+
return false;
|
|
707
|
+
})
|
|
708
|
+
.forEach((path) => {
|
|
709
|
+
const properties = path.value.right.properties;
|
|
710
|
+
// Find the property we want to remove
|
|
711
|
+
const propIndex = properties.findIndex((p) => p &&
|
|
712
|
+
p.type === 'ObjectProperty' &&
|
|
713
|
+
p.key &&
|
|
714
|
+
p.key.type === 'Identifier' &&
|
|
715
|
+
p.key.name === propertyToRemove);
|
|
716
|
+
if (propIndex !== -1) {
|
|
717
|
+
// Case 2: If it's the only property, remove the entire statement
|
|
718
|
+
if (properties.length === 1) {
|
|
719
|
+
j(path).closest(j.ExpressionStatement).remove();
|
|
720
|
+
}
|
|
721
|
+
// Case 3: If there are other properties, just remove this one property
|
|
722
|
+
else {
|
|
723
|
+
properties.splice(propIndex, 1);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
return root;
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
exports.tsComponentPropertyRemoval = tsComponentPropertyRemoval;
|
|
731
|
+
const tsPropertyTransformer = (source, root, j, packageName, componentType, propertyName, newPropertyName, valueProperty) => {
|
|
732
|
+
if (source.includes(componentType)) {
|
|
733
|
+
// Check if componentType is imported from the specified package
|
|
734
|
+
if (!isImportedFromPackage(root, j, packageName, componentType)) {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
// Find all class properties that are of type componentType
|
|
738
|
+
const properties = new Set();
|
|
739
|
+
// Find properties with type annotations
|
|
740
|
+
root.find(j.ClassProperty, {
|
|
741
|
+
typeAnnotation: {
|
|
742
|
+
typeAnnotation: {
|
|
743
|
+
typeName: {
|
|
744
|
+
name: componentType,
|
|
745
|
+
},
|
|
746
|
+
},
|
|
747
|
+
},
|
|
748
|
+
}).forEach((path) => {
|
|
749
|
+
if (path.node.key.type === 'Identifier') {
|
|
750
|
+
properties.add(path.node.key.name);
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
// Find function parameters of type componentType
|
|
754
|
+
const parameters = new Set();
|
|
755
|
+
root.find(j.FunctionDeclaration).forEach((path) => {
|
|
756
|
+
if (path.node.params) {
|
|
757
|
+
path.node.params.forEach((param) => {
|
|
758
|
+
if (param.type === 'Identifier' &&
|
|
759
|
+
param.typeAnnotation &&
|
|
760
|
+
param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
761
|
+
param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
762
|
+
param.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
763
|
+
parameters.add(param.name);
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
// Also check method declarations in classes
|
|
769
|
+
root.find(j.ClassMethod).forEach((path) => {
|
|
770
|
+
if (path.node.params) {
|
|
771
|
+
path.node.params.forEach((param) => {
|
|
772
|
+
if (param.type === 'Identifier' &&
|
|
773
|
+
param.typeAnnotation &&
|
|
774
|
+
param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
775
|
+
param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
776
|
+
param.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
777
|
+
parameters.add(param.name);
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
// Also check arrow functions
|
|
783
|
+
root.find(j.ArrowFunctionExpression).forEach((path) => {
|
|
784
|
+
if (path.node.params) {
|
|
785
|
+
path.node.params.forEach((param) => {
|
|
786
|
+
if (param.type === 'Identifier' &&
|
|
787
|
+
param.typeAnnotation &&
|
|
788
|
+
param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
789
|
+
param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
790
|
+
param.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
791
|
+
parameters.add(param.name);
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
// Find local variable declarations of type componentType
|
|
797
|
+
const localVariables = new Set();
|
|
798
|
+
root.find(j.VariableDeclarator).forEach((path) => {
|
|
799
|
+
if (path.node.id.type === 'Identifier' &&
|
|
800
|
+
path.node.id.typeAnnotation &&
|
|
801
|
+
path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
802
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
803
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
804
|
+
localVariables.add(path.node.id.name);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
// Find array variables of type componentType[]
|
|
808
|
+
// This handles cases like: const arr: ChatComponent[] = [...]; arr[0].property = value;
|
|
809
|
+
const arrayVariables = new Set();
|
|
810
|
+
root.find(j.VariableDeclarator).forEach((path) => {
|
|
811
|
+
if (path.node.id.type === 'Identifier' &&
|
|
812
|
+
path.node.id.typeAnnotation &&
|
|
813
|
+
path.node.id.typeAnnotation.typeAnnotation?.type === 'TSArrayType' &&
|
|
814
|
+
path.node.id.typeAnnotation.typeAnnotation.elementType?.type === 'TSTypeReference' &&
|
|
815
|
+
path.node.id.typeAnnotation.typeAnnotation.elementType.typeName?.type === 'Identifier' &&
|
|
816
|
+
path.node.id.typeAnnotation.typeAnnotation.elementType.typeName.name === componentType) {
|
|
817
|
+
arrayVariables.add(path.node.id.name);
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
// Find object properties that have componentType (e.g., {chat: ChatComponent})
|
|
821
|
+
// This handles cases like: const config: {chat: ChatComponent} = {...}; config.chat.property = value;
|
|
822
|
+
const objectProperties = new Map(); // Maps variable name to property names
|
|
823
|
+
root.find(j.VariableDeclarator).forEach((path) => {
|
|
824
|
+
if (path.node.id.type === 'Identifier' &&
|
|
825
|
+
path.node.id.typeAnnotation &&
|
|
826
|
+
path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeLiteral') {
|
|
827
|
+
const varName = path.node.id.name;
|
|
828
|
+
const members = path.node.id.typeAnnotation.typeAnnotation.members;
|
|
829
|
+
members.forEach((member) => {
|
|
830
|
+
if (member.type === 'TSPropertySignature' &&
|
|
831
|
+
member.key &&
|
|
832
|
+
member.key.type === 'Identifier' &&
|
|
833
|
+
member.typeAnnotation &&
|
|
834
|
+
member.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
835
|
+
member.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
|
|
836
|
+
member.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
837
|
+
if (!objectProperties.has(varName)) {
|
|
838
|
+
objectProperties.set(varName, []);
|
|
839
|
+
}
|
|
840
|
+
objectProperties.get(varName).push(member.key.name);
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
// Helper function to check if a node is a componentType instance
|
|
846
|
+
const isComponentTypeInstance = (node) => {
|
|
847
|
+
// Direct identifier (parameter or local variable)
|
|
848
|
+
if (node.type === 'Identifier') {
|
|
849
|
+
return parameters.has(node.name) || localVariables.has(node.name);
|
|
850
|
+
}
|
|
851
|
+
// this.property where property is of componentType
|
|
852
|
+
if (node.type === 'MemberExpression' && node.property.type === 'Identifier') {
|
|
853
|
+
if (node.object.type === 'ThisExpression' && properties.has(node.property.name)) {
|
|
854
|
+
return true;
|
|
855
|
+
}
|
|
856
|
+
// Handle nested object properties: objectVar.propertyName where propertyName is of componentType
|
|
857
|
+
if (node.object.type === 'Identifier') {
|
|
858
|
+
const objName = node.object.name;
|
|
859
|
+
const propName = node.property.name;
|
|
860
|
+
if (objectProperties.has(objName)) {
|
|
861
|
+
const props = objectProperties.get(objName);
|
|
862
|
+
return props.includes(propName);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
// Array element access: arrayVar[index]
|
|
867
|
+
if (node.type === 'MemberExpression' &&
|
|
868
|
+
node.object.type === 'Identifier' &&
|
|
869
|
+
arrayVariables.has(node.object.name)) {
|
|
870
|
+
return true;
|
|
871
|
+
}
|
|
872
|
+
// TypeScript type assertions like (this.componentProperty as ComponentType)
|
|
873
|
+
if (node.type === 'TSAsExpression') {
|
|
874
|
+
if (node.typeAnnotation &&
|
|
875
|
+
node.typeAnnotation.type === 'TSTypeReference' &&
|
|
876
|
+
node.typeAnnotation.typeName &&
|
|
877
|
+
node.typeAnnotation.typeName.type === 'Identifier' &&
|
|
878
|
+
node.typeAnnotation.typeName.name === componentType) {
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
return false;
|
|
883
|
+
};
|
|
884
|
+
// Find all member expressions where propertyName is accessed on any componentType instance
|
|
885
|
+
root.find(j.MemberExpression, {
|
|
886
|
+
property: {
|
|
887
|
+
type: 'Identifier',
|
|
888
|
+
name: propertyName,
|
|
889
|
+
},
|
|
890
|
+
})
|
|
891
|
+
.filter((path) => {
|
|
892
|
+
return isComponentTypeInstance(path.node.object);
|
|
893
|
+
})
|
|
894
|
+
.forEach((path) => {
|
|
895
|
+
// Replace old property name with new property name
|
|
896
|
+
if (path.node.property.type === 'Identifier') {
|
|
897
|
+
path.node.property.name = newPropertyName;
|
|
898
|
+
}
|
|
899
|
+
// If valueProperty is specified and this is part of an assignment,
|
|
900
|
+
// we need to also modify the right-hand side of the assignment
|
|
901
|
+
if (valueProperty) {
|
|
902
|
+
const assignmentExpression = path.parent;
|
|
903
|
+
if (assignmentExpression &&
|
|
904
|
+
assignmentExpression.value &&
|
|
905
|
+
assignmentExpression.value.type === 'AssignmentExpression' &&
|
|
906
|
+
assignmentExpression.value.left === path.node) {
|
|
907
|
+
const rightSide = assignmentExpression.value.right;
|
|
908
|
+
// Case 1: Right side is a member expression (e.g., this.user, obj.user) -> transform to this.user.id, obj.user.id
|
|
909
|
+
// Case 2: Right side is an identifier (e.g., user, foo) -> transform to user.id, foo.id
|
|
910
|
+
if (rightSide.type === 'MemberExpression' || rightSide.type === 'Identifier') {
|
|
911
|
+
const newRightSide = j.memberExpression(rightSide, j.identifier(valueProperty));
|
|
912
|
+
assignmentExpression.value.right = newRightSide;
|
|
913
|
+
}
|
|
914
|
+
// Case 3: Right side is object literal -> extract the valueProperty value
|
|
915
|
+
else if (rightSide.type === 'ObjectExpression') {
|
|
916
|
+
// Find the property that matches valueProperty
|
|
917
|
+
const targetProperty = rightSide.properties.find((prop) => prop.type === 'ObjectProperty' &&
|
|
918
|
+
prop.key &&
|
|
919
|
+
prop.key.type === 'Identifier' &&
|
|
920
|
+
prop.key.name === valueProperty);
|
|
921
|
+
if (targetProperty) {
|
|
922
|
+
// Replace the entire object literal with just the value of the target property
|
|
923
|
+
assignmentExpression.value.right = targetProperty.value;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
// Transform object literal properties in variable declarations and assignments
|
|
930
|
+
// This handles cases like: const obj: ComponentType = { oldProperty: 'value' }
|
|
931
|
+
root.find(j.VariableDeclarator)
|
|
932
|
+
.filter((path) => {
|
|
933
|
+
// Check if the variable has the componentType type annotation
|
|
934
|
+
return Boolean(path.node.id.type === 'Identifier' &&
|
|
935
|
+
path.node.id.typeAnnotation &&
|
|
936
|
+
path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
937
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
|
|
938
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.name === componentType);
|
|
939
|
+
})
|
|
940
|
+
.forEach((path) => {
|
|
941
|
+
// Check if the initializer is an object expression
|
|
942
|
+
if (path.node.init && path.node.init.type === 'ObjectExpression') {
|
|
943
|
+
path.node.init.properties.forEach((prop) => {
|
|
944
|
+
// Rename the property if it matches
|
|
945
|
+
if (prop.type === 'ObjectProperty' &&
|
|
946
|
+
prop.key &&
|
|
947
|
+
prop.key.type === 'Identifier' &&
|
|
948
|
+
prop.key.name === propertyName) {
|
|
949
|
+
prop.key.name = newPropertyName;
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
// Transform object literal properties in class properties
|
|
955
|
+
// This handles cases like: customMessages: ComponentType = { oldProperty: 'value' }
|
|
956
|
+
root.find(j.ClassProperty)
|
|
957
|
+
.filter((path) => {
|
|
958
|
+
// Check if the property has the componentType type annotation
|
|
959
|
+
return Boolean(path.node.typeAnnotation &&
|
|
960
|
+
path.node.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
|
|
961
|
+
path.node.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
|
|
962
|
+
path.node.typeAnnotation.typeAnnotation.typeName.name === componentType);
|
|
963
|
+
})
|
|
964
|
+
.forEach((path) => {
|
|
965
|
+
// Check if the value is an object expression
|
|
966
|
+
if (path.node.value && path.node.value.type === 'ObjectExpression') {
|
|
967
|
+
path.node.value.properties.forEach((prop) => {
|
|
968
|
+
// Rename the property if it matches
|
|
969
|
+
if (prop.type === 'ObjectProperty' &&
|
|
970
|
+
prop.key &&
|
|
971
|
+
prop.key.type === 'Identifier' &&
|
|
972
|
+
prop.key.name === propertyName) {
|
|
973
|
+
prop.key.name = newPropertyName;
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
// Transform object literal properties in assignment expressions
|
|
979
|
+
// This handles cases like: this.obj = { oldProperty: 'value' }
|
|
980
|
+
root.find(j.AssignmentExpression)
|
|
981
|
+
.filter((path) => {
|
|
982
|
+
// Check if we're assigning an object literal
|
|
983
|
+
return path.node.right.type === 'ObjectExpression';
|
|
984
|
+
})
|
|
985
|
+
.forEach((path) => {
|
|
986
|
+
// We need to determine if the left side is of componentType
|
|
987
|
+
// This is more complex as we need to check the type of the assignment target
|
|
988
|
+
const leftSide = path.node.left;
|
|
989
|
+
let isTargetComponentType = false;
|
|
990
|
+
// Check if it's a property that we know is of componentType
|
|
991
|
+
if (leftSide.type === 'MemberExpression' && leftSide.property.type === 'Identifier') {
|
|
992
|
+
if (leftSide.object.type === 'ThisExpression' && properties.has(leftSide.property.name)) {
|
|
993
|
+
isTargetComponentType = true;
|
|
994
|
+
}
|
|
995
|
+
else if (leftSide.object.type === 'Identifier' && localVariables.has(leftSide.object.name)) {
|
|
996
|
+
isTargetComponentType = true;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
else if (leftSide.type === 'Identifier') {
|
|
1000
|
+
isTargetComponentType = localVariables.has(leftSide.name) || parameters.has(leftSide.name);
|
|
1001
|
+
}
|
|
1002
|
+
if (isTargetComponentType && path.node.right.type === 'ObjectExpression') {
|
|
1003
|
+
path.node.right.properties.forEach((prop) => {
|
|
1004
|
+
// Rename the property if it matches
|
|
1005
|
+
if (prop.type === 'ObjectProperty' &&
|
|
1006
|
+
prop.key &&
|
|
1007
|
+
prop.key.type === 'Identifier' &&
|
|
1008
|
+
prop.key.name === propertyName) {
|
|
1009
|
+
prop.key.name = newPropertyName;
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1016
|
+
exports.tsPropertyTransformer = tsPropertyTransformer;
|
|
1017
|
+
const tsPropertyValueTransformer = (source, root, j, packageName, typeName, oldValue, newValue) => {
|
|
1018
|
+
if (source.includes(typeName)) {
|
|
1019
|
+
// Check if typeName is imported from the specified package
|
|
1020
|
+
if (!isImportedFromPackage(root, j, packageName, typeName)) {
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
root.find(j.ClassProperty)
|
|
1024
|
+
.filter((path) => {
|
|
1025
|
+
if (path.node.typeAnnotation?.typeAnnotation &&
|
|
1026
|
+
path.node.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
|
|
1027
|
+
path.node.typeAnnotation.typeAnnotation.typeName &&
|
|
1028
|
+
path.node.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
1029
|
+
path.node.typeAnnotation.typeAnnotation.typeName.name === typeName) {
|
|
1030
|
+
return true;
|
|
1031
|
+
}
|
|
1032
|
+
return false;
|
|
1033
|
+
})
|
|
1034
|
+
.forEach((path) => {
|
|
1035
|
+
if (path.node.value && path.node.value.type === 'StringLiteral' && path.node.value.value === oldValue) {
|
|
1036
|
+
path.node.value.value = newValue;
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
root.find(j.VariableDeclarator)
|
|
1040
|
+
.filter((path) => {
|
|
1041
|
+
if (path.node.id.type === 'Identifier' &&
|
|
1042
|
+
path.node.id.typeAnnotation?.typeAnnotation &&
|
|
1043
|
+
path.node.id.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
|
|
1044
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
1045
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.name === typeName) {
|
|
1046
|
+
return true;
|
|
1047
|
+
}
|
|
1048
|
+
return false;
|
|
1049
|
+
})
|
|
1050
|
+
.forEach((path) => {
|
|
1051
|
+
if (path.node.id.type === 'Identifier') {
|
|
1052
|
+
if (path.node.init && path.node.init.type === 'StringLiteral' && path.node.init.value === oldValue) {
|
|
1053
|
+
path.node.init.value = newValue;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
root.find(j.AssignmentExpression)
|
|
1058
|
+
.filter((path) => {
|
|
1059
|
+
return path.node.right.type === 'StringLiteral' && path.node.right.value === oldValue;
|
|
1060
|
+
})
|
|
1061
|
+
.forEach((path) => {
|
|
1062
|
+
path.node.right.value = newValue;
|
|
1063
|
+
});
|
|
1064
|
+
root.find(j.JSXAttribute, {
|
|
1065
|
+
value: {
|
|
1066
|
+
type: 'StringLiteral',
|
|
1067
|
+
value: oldValue,
|
|
1068
|
+
},
|
|
1069
|
+
}).forEach((path) => {
|
|
1070
|
+
if (path.node.value?.type === 'StringLiteral') {
|
|
1071
|
+
path.node.value.value = newValue;
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
exports.tsPropertyValueTransformer = tsPropertyValueTransformer;
|
|
1077
|
+
const tsInterfaceTransformer = (fileInfo, rootSource, j, packageName, interfaceName, newName) => {
|
|
1078
|
+
const source = fileInfo.source;
|
|
1079
|
+
if (source.includes(interfaceName)) {
|
|
1080
|
+
// Check if interface is imported from the specified package and rename it
|
|
1081
|
+
let isImported = false;
|
|
1082
|
+
rootSource.find(j.ImportDeclaration).forEach((path) => {
|
|
1083
|
+
if (path.node.source &&
|
|
1084
|
+
path.node.source.value === packageName &&
|
|
1085
|
+
path.node.specifiers) {
|
|
1086
|
+
path.node.specifiers.forEach((specifier) => {
|
|
1087
|
+
if (specifier.type === 'ImportSpecifier' &&
|
|
1088
|
+
specifier.imported.type === 'Identifier' &&
|
|
1089
|
+
specifier.imported.name === interfaceName) {
|
|
1090
|
+
isImported = true;
|
|
1091
|
+
specifier.imported.name = newName;
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
if (!isImported) {
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
rootSource.find(j.ClassProperty).forEach((path) => {
|
|
1100
|
+
if (path.node.typeAnnotation &&
|
|
1101
|
+
path.node.typeAnnotation.typeAnnotation &&
|
|
1102
|
+
path.node.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
|
|
1103
|
+
path.node.typeAnnotation.typeAnnotation.typeName &&
|
|
1104
|
+
path.node.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
1105
|
+
path.node.typeAnnotation.typeAnnotation.typeName.name === interfaceName) {
|
|
1106
|
+
path.node.typeAnnotation.typeAnnotation.typeName.name = newName;
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
rootSource.find(j.VariableDeclarator).forEach((path) => {
|
|
1110
|
+
if (path.node.id.type === 'Identifier' &&
|
|
1111
|
+
path.node.id.typeAnnotation &&
|
|
1112
|
+
path.node.id.typeAnnotation.typeAnnotation &&
|
|
1113
|
+
path.node.id.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
|
|
1114
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName &&
|
|
1115
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
1116
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.name === interfaceName) {
|
|
1117
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.name = newName;
|
|
1118
|
+
}
|
|
1119
|
+
});
|
|
1120
|
+
rootSource.find(j.FunctionDeclaration).forEach((path) => {
|
|
1121
|
+
if (path.node.params) {
|
|
1122
|
+
path.node.params.forEach((param) => {
|
|
1123
|
+
if (param.type === 'Identifier' &&
|
|
1124
|
+
param.typeAnnotation &&
|
|
1125
|
+
param.typeAnnotation.typeAnnotation &&
|
|
1126
|
+
param.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
|
|
1127
|
+
param.typeAnnotation.typeAnnotation.typeName &&
|
|
1128
|
+
param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
1129
|
+
param.typeAnnotation.typeAnnotation.typeName.name === interfaceName) {
|
|
1130
|
+
param.typeAnnotation.typeAnnotation.typeName.name = newName;
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
});
|
|
1135
|
+
rootSource.find(j.ArrowFunctionExpression).forEach((path) => {
|
|
1136
|
+
if (path.node.params) {
|
|
1137
|
+
path.node.params.forEach((param) => {
|
|
1138
|
+
if (param.type === 'Identifier' &&
|
|
1139
|
+
param.typeAnnotation &&
|
|
1140
|
+
param.typeAnnotation.typeAnnotation &&
|
|
1141
|
+
param.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
|
|
1142
|
+
param.typeAnnotation.typeAnnotation.typeName &&
|
|
1143
|
+
param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
1144
|
+
param.typeAnnotation.typeAnnotation.typeName.name === interfaceName) {
|
|
1145
|
+
param.typeAnnotation.typeAnnotation.typeName.name = newName;
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
rootSource.find(j.ClassMethod).forEach((path) => {
|
|
1151
|
+
if (path.node.params) {
|
|
1152
|
+
path.node.params.forEach((param) => {
|
|
1153
|
+
if (param.type === 'Identifier' &&
|
|
1154
|
+
param.typeAnnotation &&
|
|
1155
|
+
param.typeAnnotation.typeAnnotation &&
|
|
1156
|
+
param.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
|
|
1157
|
+
param.typeAnnotation.typeAnnotation.typeName &&
|
|
1158
|
+
param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
|
|
1159
|
+
param.typeAnnotation.typeAnnotation.typeName.name === interfaceName) {
|
|
1160
|
+
param.typeAnnotation.typeAnnotation.typeName.name = newName;
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
rootSource.find(j.Function).forEach((path) => {
|
|
1166
|
+
if (path.node.returnType &&
|
|
1167
|
+
path.node.returnType.typeAnnotation &&
|
|
1168
|
+
path.node.returnType.typeAnnotation.type === 'TSTypeReference' &&
|
|
1169
|
+
path.node.returnType.typeAnnotation.typeName &&
|
|
1170
|
+
path.node.returnType.typeAnnotation.typeName.type === 'Identifier' &&
|
|
1171
|
+
path.node.returnType.typeAnnotation.typeName.name === interfaceName) {
|
|
1172
|
+
path.node.returnType.typeAnnotation.typeName.name = newName;
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1175
|
+
rootSource.find(j.TSAsExpression).forEach((path) => {
|
|
1176
|
+
if (path.node.typeAnnotation &&
|
|
1177
|
+
path.node.typeAnnotation.type === 'TSTypeReference' &&
|
|
1178
|
+
path.node.typeAnnotation.typeName &&
|
|
1179
|
+
path.node.typeAnnotation.typeName.type === 'Identifier' &&
|
|
1180
|
+
path.node.typeAnnotation.typeName.name === interfaceName) {
|
|
1181
|
+
path.node.typeAnnotation.typeName.name = newName;
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
// Handle constructor calls with 'new' keyword
|
|
1185
|
+
rootSource.find(j.NewExpression).forEach((path) => {
|
|
1186
|
+
if (path.node.callee &&
|
|
1187
|
+
path.node.callee.type === 'Identifier' &&
|
|
1188
|
+
path.node.callee.name === interfaceName) {
|
|
1189
|
+
path.node.callee.name = newName;
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
// Helper function to recursively transform type references
|
|
1193
|
+
const transformTypeReference = (typeNode) => {
|
|
1194
|
+
if (!typeNode)
|
|
1195
|
+
return;
|
|
1196
|
+
// Handle TSTypeReference (e.g., FileSelectSettings)
|
|
1197
|
+
if (typeNode.type === 'TSTypeReference' &&
|
|
1198
|
+
typeNode.typeName &&
|
|
1199
|
+
typeNode.typeName.type === 'Identifier' &&
|
|
1200
|
+
typeNode.typeName.name === interfaceName) {
|
|
1201
|
+
typeNode.typeName.name = newName;
|
|
1202
|
+
}
|
|
1203
|
+
// Handle TSArrayType (e.g., FileSelectSettings[])
|
|
1204
|
+
if (typeNode.type === 'TSArrayType' && typeNode.elementType) {
|
|
1205
|
+
transformTypeReference(typeNode.elementType);
|
|
1206
|
+
}
|
|
1207
|
+
// Handle generic Array<T> (e.g., Array<FileSelectSettings>)
|
|
1208
|
+
if (typeNode.type === 'TSTypeReference' &&
|
|
1209
|
+
typeNode.typeParameters &&
|
|
1210
|
+
typeNode.typeParameters.params) {
|
|
1211
|
+
typeNode.typeParameters.params.forEach((param) => {
|
|
1212
|
+
transformTypeReference(param);
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
// Handle TSTypeLiteral (e.g., { primary: FileSelectSettings })
|
|
1216
|
+
if (typeNode.type === 'TSTypeLiteral' && typeNode.members) {
|
|
1217
|
+
typeNode.members.forEach((member) => {
|
|
1218
|
+
if (member.typeAnnotation && member.typeAnnotation.typeAnnotation) {
|
|
1219
|
+
transformTypeReference(member.typeAnnotation.typeAnnotation);
|
|
1220
|
+
}
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
// Handle TSFunctionType (e.g., (settings: FileSelectSettings) => void)
|
|
1224
|
+
if (typeNode.type === 'TSFunctionType') {
|
|
1225
|
+
if (typeNode.parameters) {
|
|
1226
|
+
typeNode.parameters.forEach((param) => {
|
|
1227
|
+
if (param.typeAnnotation && param.typeAnnotation.typeAnnotation) {
|
|
1228
|
+
transformTypeReference(param.typeAnnotation.typeAnnotation);
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
if (typeNode.typeAnnotation && typeNode.typeAnnotation.typeAnnotation) {
|
|
1233
|
+
transformTypeReference(typeNode.typeAnnotation.typeAnnotation);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
// Apply recursive transformation to all type annotations
|
|
1238
|
+
rootSource.find(j.VariableDeclarator).forEach((path) => {
|
|
1239
|
+
if (path.node.id.type === 'Identifier' &&
|
|
1240
|
+
path.node.id.typeAnnotation &&
|
|
1241
|
+
path.node.id.typeAnnotation.typeAnnotation) {
|
|
1242
|
+
transformTypeReference(path.node.id.typeAnnotation.typeAnnotation);
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
rootSource.find(j.ClassProperty).forEach((path) => {
|
|
1246
|
+
if (path.node.typeAnnotation && path.node.typeAnnotation.typeAnnotation) {
|
|
1247
|
+
transformTypeReference(path.node.typeAnnotation.typeAnnotation);
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
rootSource.find(j.ClassMethod).forEach((path) => {
|
|
1251
|
+
if (path.node.returnType && path.node.returnType.typeAnnotation) {
|
|
1252
|
+
transformTypeReference(path.node.returnType.typeAnnotation);
|
|
1253
|
+
}
|
|
1254
|
+
// Transform parameter types recursively
|
|
1255
|
+
if (path.node.params) {
|
|
1256
|
+
path.node.params.forEach((param) => {
|
|
1257
|
+
if (param.typeAnnotation && param.typeAnnotation.typeAnnotation) {
|
|
1258
|
+
transformTypeReference(param.typeAnnotation.typeAnnotation);
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
rootSource.find(j.FunctionDeclaration).forEach((path) => {
|
|
1264
|
+
if (path.node.returnType && path.node.returnType.typeAnnotation) {
|
|
1265
|
+
transformTypeReference(path.node.returnType.typeAnnotation);
|
|
1266
|
+
}
|
|
1267
|
+
// Transform parameter types recursively
|
|
1268
|
+
if (path.node.params) {
|
|
1269
|
+
path.node.params.forEach((param) => {
|
|
1270
|
+
if (param.typeAnnotation && param.typeAnnotation.typeAnnotation) {
|
|
1271
|
+
transformTypeReference(param.typeAnnotation.typeAnnotation);
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
rootSource.find(j.ArrowFunctionExpression).forEach((path) => {
|
|
1277
|
+
if (path.node.returnType && path.node.returnType.typeAnnotation) {
|
|
1278
|
+
transformTypeReference(path.node.returnType.typeAnnotation);
|
|
1279
|
+
}
|
|
1280
|
+
// Transform parameter types recursively
|
|
1281
|
+
if (path.node.params) {
|
|
1282
|
+
path.node.params.forEach((param) => {
|
|
1283
|
+
if (param.typeAnnotation && param.typeAnnotation.typeAnnotation) {
|
|
1284
|
+
transformTypeReference(param.typeAnnotation.typeAnnotation);
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1291
|
+
exports.tsInterfaceTransformer = tsInterfaceTransformer;
|
|
1292
|
+
// Helper function to check if a node is a component of the specified type
|
|
1293
|
+
function isComponentTypeMatch(root, j, node, componentType) {
|
|
1294
|
+
if (!node)
|
|
1295
|
+
return false;
|
|
1296
|
+
if (node.type === 'ThisExpression') {
|
|
1297
|
+
return true;
|
|
1298
|
+
}
|
|
1299
|
+
if (node.type === 'Identifier') {
|
|
1300
|
+
const paramName = node.name;
|
|
1301
|
+
// Check function parameters
|
|
1302
|
+
let isParameter = false;
|
|
1303
|
+
root.find(j.Function).forEach((path) => {
|
|
1304
|
+
if (path.node.params && path.node.params.some((param) => param.type === 'Identifier' &&
|
|
1305
|
+
param.name === paramName &&
|
|
1306
|
+
param.typeAnnotation?.typeAnnotation?.typeName?.name === componentType)) {
|
|
1307
|
+
isParameter = true;
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
if (isParameter)
|
|
1311
|
+
return true;
|
|
1312
|
+
// Check local variable declarations
|
|
1313
|
+
let isLocalVariable = false;
|
|
1314
|
+
root.find(j.VariableDeclarator).forEach((path) => {
|
|
1315
|
+
if (path.node.id.type === 'Identifier' &&
|
|
1316
|
+
path.node.id.name === paramName &&
|
|
1317
|
+
path.node.id.typeAnnotation?.typeAnnotation?.type === 'TSTypeReference' &&
|
|
1318
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
|
|
1319
|
+
path.node.id.typeAnnotation.typeAnnotation.typeName.name === componentType) {
|
|
1320
|
+
isLocalVariable = true;
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
return isLocalVariable;
|
|
1324
|
+
}
|
|
1325
|
+
if (node.type === 'MemberExpression') {
|
|
1326
|
+
if (node.object.type === 'ThisExpression' && node.property.type === 'Identifier') {
|
|
1327
|
+
const propName = node.property.name;
|
|
1328
|
+
return (root
|
|
1329
|
+
.find(j.ClassProperty, {
|
|
1330
|
+
key: { name: propName },
|
|
1331
|
+
typeAnnotation: {
|
|
1332
|
+
typeAnnotation: {
|
|
1333
|
+
typeName: { name: componentType },
|
|
1334
|
+
},
|
|
1335
|
+
},
|
|
1336
|
+
})
|
|
1337
|
+
.size() > 0);
|
|
1338
|
+
}
|
|
1339
|
+
// Handle nested member expressions like chatConfig.chat where chat is ChatComponent
|
|
1340
|
+
if (node.object.type === 'Identifier' && node.property.type === 'Identifier') {
|
|
1341
|
+
const varName = node.object.name;
|
|
1342
|
+
const propName = node.property.name;
|
|
1343
|
+
// Check if this is an object with a property of componentType
|
|
1344
|
+
let hasMatchingProperty = false;
|
|
1345
|
+
root.find(j.VariableDeclarator).forEach((path) => {
|
|
1346
|
+
if (path.node.id.type === 'Identifier' &&
|
|
1347
|
+
path.node.id.name === varName &&
|
|
1348
|
+
path.node.id.typeAnnotation &&
|
|
1349
|
+
path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeLiteral') {
|
|
1350
|
+
const members = path.node.id.typeAnnotation.typeAnnotation.members;
|
|
1351
|
+
const found = members.some((member) => {
|
|
1352
|
+
return (member.type === 'TSPropertySignature' &&
|
|
1353
|
+
member.key?.type === 'Identifier' &&
|
|
1354
|
+
member.key.name === propName &&
|
|
1355
|
+
member.typeAnnotation?.typeAnnotation?.type === 'TSTypeReference' &&
|
|
1356
|
+
member.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
|
|
1357
|
+
member.typeAnnotation.typeAnnotation.typeName.name === componentType);
|
|
1358
|
+
});
|
|
1359
|
+
if (found) {
|
|
1360
|
+
hasMatchingProperty = true;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
return hasMatchingProperty;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
// Handle array element access like chatComponents[0] where chatComponents is ChatComponent[]
|
|
1368
|
+
// When we have chatComponents[0].property, node is chatComponents[0] which is a MemberExpression with computed: true
|
|
1369
|
+
if (node.type === 'MemberExpression' && node.computed === true && node.object.type === 'Identifier') {
|
|
1370
|
+
const arrayName = node.object.name;
|
|
1371
|
+
// Check if this array is of type ChatComponent[]
|
|
1372
|
+
let isComponentArray = false;
|
|
1373
|
+
root.find(j.VariableDeclarator).forEach((path) => {
|
|
1374
|
+
if (path.node.id.type === 'Identifier' &&
|
|
1375
|
+
path.node.id.name === arrayName &&
|
|
1376
|
+
path.node.id.typeAnnotation?.typeAnnotation?.type === 'TSArrayType' &&
|
|
1377
|
+
path.node.id.typeAnnotation.typeAnnotation.elementType?.type === 'TSTypeReference' &&
|
|
1378
|
+
path.node.id.typeAnnotation.typeAnnotation.elementType.typeName?.type === 'Identifier' &&
|
|
1379
|
+
path.node.id.typeAnnotation.typeAnnotation.elementType.typeName.name === componentType) {
|
|
1380
|
+
isComponentArray = true;
|
|
1381
|
+
}
|
|
1382
|
+
});
|
|
1383
|
+
return isComponentArray;
|
|
1384
|
+
}
|
|
1385
|
+
// Handle TypeScript type assertions like (this.componentProperty as ComponentType)
|
|
1386
|
+
if (node.type === 'TSAsExpression') {
|
|
1387
|
+
// Check if the type assertion is casting to our componentType
|
|
1388
|
+
if (node.typeAnnotation &&
|
|
1389
|
+
node.typeAnnotation.type === 'TSTypeReference' &&
|
|
1390
|
+
node.typeAnnotation.typeName &&
|
|
1391
|
+
node.typeAnnotation.typeName.type === 'Identifier' &&
|
|
1392
|
+
node.typeAnnotation.typeName.name === componentType) {
|
|
1393
|
+
return true;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
return false;
|
|
1397
|
+
}
|
|
1398
|
+
// Matches CSS class names in CSS selectors (.foo) and as whitespace/quote-delimited
|
|
1399
|
+
// tokens within attribute values, covering both single-class ("foo") and multi-class ("foo bar") cases.
|
|
1400
|
+
function makePattern(classes) {
|
|
1401
|
+
return new RegExp(classes.map(c => String.raw `\.${c}\b|(?<=["'\s])${c}(?=["'\s])`).join('|'));
|
|
1402
|
+
}
|
|
1403
|
+
function writeInstructionMarker(instruction, codemodFilename, affectedFile) {
|
|
1404
|
+
// Write to node_modules/.kendo/migration/<basename(codemodFilename)>
|
|
1405
|
+
// kendo-cli reads the marker by looking up basename(codemod.file) in that directory
|
|
1406
|
+
const markerDir = path.join(process.cwd(), 'node_modules', '.kendo', 'migration');
|
|
1407
|
+
const markerPath = path.join(markerDir, path.basename(codemodFilename));
|
|
1408
|
+
try {
|
|
1409
|
+
fs.mkdirSync(markerDir, { recursive: true });
|
|
1410
|
+
const existing = fs.existsSync(markerPath) ? fs.readFileSync(markerPath, 'utf8') : '';
|
|
1411
|
+
let content = existing;
|
|
1412
|
+
if (!existing.includes(instruction)) {
|
|
1413
|
+
content += instruction + '\n';
|
|
1414
|
+
}
|
|
1415
|
+
const fileLine = ` - ${path.relative(process.cwd(), affectedFile)}`;
|
|
1416
|
+
if (!content.includes(fileLine)) {
|
|
1417
|
+
content += fileLine + '\n';
|
|
1418
|
+
}
|
|
1419
|
+
fs.writeFileSync(markerPath, content);
|
|
1420
|
+
}
|
|
1421
|
+
catch { /* suppress error */ }
|
|
1422
|
+
}
|
|
1423
|
+
function isApiChangeTarget(filePath) {
|
|
1424
|
+
const ext = path.extname(filePath);
|
|
1425
|
+
return ext === '.ts' || ext === '.html';
|
|
1426
|
+
}
|
|
1427
|
+
function isRenderingChangeTarget(filePath) {
|
|
1428
|
+
const ext = path.extname(filePath);
|
|
1429
|
+
return ext === '.ts' || ext === '.html' || ext === '.css' || ext === '.scss' || ext === '.sass' || ext === '.less';
|
|
1430
|
+
}
|