@progress/kendo-angular-layout 21.2.0-develop.5 → 21.2.0-develop.7

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 CHANGED
@@ -3,14 +3,11 @@
3
3
  * Licensed under commercial license. See LICENSE.md in the project root for more information
4
4
  *-------------------------------------------------------------------------------------------*/
5
5
  "use strict";
6
- var __importDefault = (this && this.__importDefault) || function (mod) {
7
- return (mod && mod.__esModule) ? mod : { "default": mod };
8
- };
9
6
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.tsInterfaceTransformer = exports.tsComponentPropertyRemoval = exports.templateAttributeRemoval = exports.htmlAttributeRemoval = exports.htmlAttributeValueTransformer = exports.tsPropertyValueTransformer = exports.templateAttributeValueTransformer = exports.htmlEventTransformer = exports.htmlAttributeTransformer = exports.htmlBoundAttributeTransformer = exports.htmlStaticAttributeTransformer = exports.tsPropertyTransformer = exports.templateAttributeTransformer = exports.templateBoundAttributeTransformer = exports.templateStaticAttributeTransformer = exports.templateEventTransformer = exports.blockTextElements = void 0;
7
+ exports.tsInterfaceTransformer = exports.tsPropertyValueTransformer = exports.tsPropertyTransformer = exports.tsComponentPropertyRemoval = exports.attributeRemoval = exports.attributeValueUpdate = exports.attributeNameValueUpdate = exports.attributeNameUpdate = exports.eventUpdate = exports.htmlTransformer = exports.blockTextElements = void 0;
11
8
  exports.hasKendoInTemplate = hasKendoInTemplate;
9
+ exports.isImportedFromPackage = isImportedFromPackage;
12
10
  exports.tsPropertyRemoval = tsPropertyRemoval;
13
- const node_html_parser_1 = __importDefault(require("node-html-parser"));
14
11
  exports.blockTextElements = {
15
12
  script: true,
16
13
  noscript: true,
@@ -21,51 +18,694 @@ function hasKendoInTemplate(source) {
21
18
  const kendoPattern = /(<kendo-[^>\s]+|<[^>]*\s+kendo[A-Z][^>\s]*)/g;
22
19
  return kendoPattern.test(source);
23
20
  }
24
- const templateEventTransformer = (root, tagName, eventName, newEventName) => {
25
- const elements = Array.from(root.getElementsByTagName(tagName)) || [];
26
- for (const element of elements) {
27
- // Handle event bindings like (actionClick)="handler($event)"
28
- const eventAttr = element.getAttribute(`(${eventName})`);
29
- if (eventAttr) {
30
- element.setAttribute(`(${newEventName})`, eventAttr);
31
- element.removeAttribute(`(${eventName})`);
21
+ /**
22
+ * Checks if a specific type/interface is imported from the given package
23
+ * @param root - The JSCodeshift collection
24
+ * @param j - JSCodeshift instance
25
+ * @param packageName - The package name to check (e.g., '@progress/kendo-angular-dateinputs')
26
+ * @param typeName - The type/interface name to check (e.g., 'DatePickerComponent')
27
+ * @returns true if the type is imported from the package, false otherwise
28
+ */
29
+ function isImportedFromPackage(root, j, packageName, typeName) {
30
+ let isImported = false;
31
+ root.find(j.ImportDeclaration).forEach((path) => {
32
+ if (path.node.source &&
33
+ path.node.source.value === packageName &&
34
+ path.node.specifiers) {
35
+ path.node.specifiers.forEach((specifier) => {
36
+ if (specifier.type === 'ImportSpecifier' &&
37
+ specifier.imported.type === 'Identifier' &&
38
+ specifier.imported.name === typeName) {
39
+ isImported = true;
40
+ }
41
+ });
42
+ }
43
+ });
44
+ return isImported;
45
+ }
46
+ /**
47
+ * Transforms HTML files and inline templates using a provided transformer function
48
+ *
49
+ * @param fileInfo - The file info containing path and source
50
+ * @param api - JSCodeshift API
51
+ * @param transformerFn - The transformer function to apply to template content
52
+ * @returns The transformed source code or undefined if no changes
53
+ */
54
+ const htmlTransformer = (fileInfo, api, transformerFn) => {
55
+ const filePath = fileInfo.path;
56
+ if (filePath.endsWith('.html')) {
57
+ if (hasKendoInTemplate(fileInfo.source)) {
58
+ let updatedContent = fileInfo.source;
59
+ updatedContent = transformerFn(updatedContent);
60
+ return updatedContent;
32
61
  }
62
+ return fileInfo.source;
33
63
  }
64
+ const j = api.jscodeshift;
65
+ const rootSource = j(fileInfo.source);
66
+ // Transform inline templates using the provided transformer function
67
+ rootSource
68
+ .find(j.ClassDeclaration)
69
+ .forEach(classPath => {
70
+ const classNode = classPath.node;
71
+ if (!classNode.decorators || !classNode.decorators.length)
72
+ return;
73
+ const componentDecorator = classNode.decorators.find((decorator) => {
74
+ if (decorator.expression && decorator.expression.type === 'CallExpression') {
75
+ const callee = decorator.expression.callee;
76
+ if (callee.type === 'Identifier' && callee.name === 'Component') {
77
+ return true;
78
+ }
79
+ if (callee.type === 'MemberExpression' &&
80
+ callee.property &&
81
+ callee.property.type === 'Identifier' &&
82
+ callee.property.name === 'Component') {
83
+ return true;
84
+ }
85
+ }
86
+ return false;
87
+ });
88
+ if (!componentDecorator || !componentDecorator.expression)
89
+ return;
90
+ const expression = componentDecorator.expression;
91
+ if (expression.type !== 'CallExpression' || !expression.arguments.length)
92
+ return;
93
+ const componentOptions = expression.arguments[0];
94
+ if (componentOptions.type !== 'ObjectExpression')
95
+ return;
96
+ const props = componentOptions.properties || [];
97
+ const templateProp = props.find((prop) => (prop.key.type === 'Identifier' && prop.key.name === 'template') ||
98
+ (prop.key.type === 'StringLiteral' && prop.key.value === 'template'));
99
+ if (templateProp) {
100
+ let originalTemplate;
101
+ if (templateProp.value.type === 'StringLiteral' || templateProp.value.type === 'Literal') {
102
+ originalTemplate = templateProp.value.value;
103
+ }
104
+ else if (templateProp.value.type === 'TemplateLiteral') {
105
+ if (templateProp.value.quasis && templateProp.value.quasis.length) {
106
+ originalTemplate = templateProp.value.quasis
107
+ .map((q) => q.value.cooked || q.value.raw)
108
+ .join('');
109
+ }
110
+ else {
111
+ return;
112
+ }
113
+ }
114
+ else {
115
+ return;
116
+ }
117
+ if (hasKendoInTemplate(originalTemplate)) {
118
+ // Apply the provided transformer function
119
+ const transformedTemplate = transformerFn(originalTemplate);
120
+ if (transformedTemplate !== originalTemplate) {
121
+ if (templateProp.value.type === 'TemplateLiteral') {
122
+ templateProp.value = j.templateLiteral([j.templateElement({ cooked: transformedTemplate, raw: transformedTemplate }, true)], []);
123
+ }
124
+ else {
125
+ templateProp.value.value = transformedTemplate;
126
+ }
127
+ }
128
+ }
129
+ }
130
+ });
131
+ return rootSource.toSource();
34
132
  };
35
- exports.templateEventTransformer = templateEventTransformer;
36
- const templateStaticAttributeTransformer = (root, tagName, attributeName, newAttributeName) => {
37
- const elements = Array.from(root.getElementsByTagName(tagName)) || [];
38
- for (const element of elements) {
39
- // Handle static attributes like title="foo"
40
- const staticAttr = element.getAttribute(attributeName);
41
- if (staticAttr) {
42
- element.setAttribute(newAttributeName, staticAttr);
43
- element.removeAttribute(attributeName);
133
+ exports.htmlTransformer = htmlTransformer;
134
+ const eventUpdate = (templateContent, tagName, oldEventName, newEventName) => {
135
+ // Escape special regex characters in tag name
136
+ const escapedTagName = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
137
+ // Create regex pattern to match the tag with the old event binding
138
+ // Pattern matches: <tagName ...attributes... (oldEventName)="handler" ...>
139
+ const eventPattern = new RegExp(`(<${escapedTagName}[^>]*\\()${oldEventName}(\\)=)`, 'g');
140
+ // Replace old event name with new event name
141
+ const updatedContent = templateContent.replace(eventPattern, `$1${newEventName}$2`);
142
+ return updatedContent;
143
+ };
144
+ exports.eventUpdate = eventUpdate;
145
+ /**
146
+ * Transforms attributes in inline templates using regex patterns.
147
+ * This function handles bound ([attribute]), static (attribute="value"),
148
+ * two-way binding ([(attribute)]), and boolean (attribute) attributes
149
+ * within a specific tag using regular expressions.
150
+ *
151
+ * @param templateContent - The template string content to transform
152
+ * @param tagName - The HTML tag name to target (e.g., 'kendo-datepicker')
153
+ * @param attributeName - The current attribute name to replace
154
+ * @param newAttributeName - The new attribute name
155
+ * @returns The transformed template content
156
+ */
157
+ const attributeNameUpdate = (templateContent, tagName, attributeName, newAttributeName) => {
158
+ // Escape special regex characters in tag and attribute names
159
+ const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
160
+ const escapedTag = escapeRegex(tagName);
161
+ const escapedAttr = escapeRegex(attributeName);
162
+ const escapedNewAttr = escapeRegex(newAttributeName);
163
+ // Pattern to match the opening tag with the attribute
164
+ // This pattern matches: <tagName ... attribute="value" ... > or <tagName ... [attribute]="value" ... >
165
+ // It captures the tag opening, everything before the attribute, the attribute binding type, and everything after
166
+ const boundAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+)\\[${escapedAttr}\\]\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?')([^>]*?>)`, 'gi');
167
+ const staticAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+)${escapedAttr}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?')([^>]*?>)`, 'gi');
168
+ // Pattern for two-way data binding [(attribute)]="value"
169
+ const twoWayBindingPattern = new RegExp(`(<${escapedTag}[^>]*?\\s+)\\[\\(${escapedAttr}\\)\\]\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?')([^>]*?>)`, 'gi');
170
+ // Pattern for boolean attributes without values (e.g., <tag attribute>)
171
+ const booleanAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+)${escapedAttr}(\\s+[^>=]|\\s*[/>])`, 'gi');
172
+ // Replace two-way data binding [(attribute)]="value"
173
+ let result = templateContent.replace(twoWayBindingPattern, `$1[(${escapedNewAttr})]=$2$3`);
174
+ // Replace bound attributes [attribute]="value"
175
+ result = result.replace(boundAttributePattern, `$1[${escapedNewAttr}]=$2$3`);
176
+ // Replace static attributes attribute="value"
177
+ result = result.replace(staticAttributePattern, `$1${escapedNewAttr}=$2$3`);
178
+ // Replace boolean attributes attribute (without value)
179
+ result = result.replace(booleanAttributePattern, `$1${escapedNewAttr}$2`);
180
+ return result;
181
+ };
182
+ exports.attributeNameUpdate = attributeNameUpdate;
183
+ /**
184
+ * Transforms bound attributes with value property extraction using regex patterns.
185
+ * This function handles bound attributes and extracts a specific property from object literals
186
+ * or appends the property to variable references.
187
+ *
188
+ * @param templateContent - The template string content to transform
189
+ * @param tagName - The HTML tag name to target (e.g., 'kendo-chat')
190
+ * @param attributeName - The current attribute name to replace
191
+ * @param newAttributeName - The new attribute name
192
+ * @param valueProperty - The property to extract (e.g., 'id')
193
+ * @returns The transformed template content
194
+ */
195
+ const attributeNameValueUpdate = (templateContent, tagName, attributeName, newAttributeName, valueProperty) => {
196
+ // Escape special regex characters in tag and attribute names
197
+ const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
198
+ const escapedTag = escapeRegex(tagName);
199
+ const escapedAttr = escapeRegex(attributeName);
200
+ const escapedNewAttr = escapeRegex(newAttributeName);
201
+ // Pattern to match bound attributes [attribute]="value"
202
+ // This captures: opening tag, bound attribute, and closing tag
203
+ const boundAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+)\\[${escapedAttr}\\]\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?'|[^\\s>]+)([^>]*?>)`, 'gi');
204
+ return templateContent.replace(boundAttributePattern, (match, beforeAttr, value, afterAttr) => {
205
+ // Remove quotes from value to analyze it
206
+ const trimmedValue = value.replace(/^["']|["']$/g, '');
207
+ // Check if it's an object literal (starts with { and ends with })
208
+ if (trimmedValue.trim().startsWith('{') && trimmedValue.trim().endsWith('}')) {
209
+ // For object literals like {id: 'foo'} or {id: 'foo', bar: 'baz'}
210
+ // We need to append .id inside the quotes if the value is quoted
211
+ if (value.match(/^["']/)) {
212
+ // Extract quote type and content, then add .id before the closing quote
213
+ const quoteType = value.charAt(0);
214
+ const content = value.slice(1, -1); // Remove quotes
215
+ const newValue = `${quoteType}${content}.${valueProperty}${quoteType}`;
216
+ return `${beforeAttr}[${escapedNewAttr}]=${newValue}${afterAttr}`;
217
+ }
218
+ else {
219
+ // Unquoted object literal
220
+ const newValue = `${value}.${valueProperty}`;
221
+ return `${beforeAttr}[${escapedNewAttr}]=${newValue}${afterAttr}`;
222
+ }
44
223
  }
224
+ else {
225
+ // For variable references like "user"
226
+ // We append .id to the variable name
227
+ const newValue = value.replace(/^["'](.*)["']$/, (_match, content) => {
228
+ return `"${content}.${valueProperty}"`;
229
+ });
230
+ // If it's not quoted (bare variable), add quotes and property
231
+ if (!value.match(/^["']/)) {
232
+ return `${beforeAttr}[${escapedNewAttr}]="${trimmedValue}.${valueProperty}"${afterAttr}`;
233
+ }
234
+ return `${beforeAttr}[${escapedNewAttr}]=${newValue}${afterAttr}`;
235
+ }
236
+ });
237
+ };
238
+ exports.attributeNameValueUpdate = attributeNameValueUpdate;
239
+ /**
240
+ * Updates attribute values in HTML templates using regex patterns.
241
+ * This function handles both bound ([attribute]="'value'") and static (attribute="value") attributes
242
+ * and replaces old values with new values within a specific tag.
243
+ *
244
+ * @param templateContent - The template string content to transform
245
+ * @param tagName - The HTML tag name to target (e.g., 'kendo-toolbar')
246
+ * @param attributeName - The attribute name to target (e.g., 'showIcon')
247
+ * @param oldValue - The old attribute value to replace (e.g., 'overflow')
248
+ * @param newValue - The new attribute value (e.g., 'menu')
249
+ * @returns The transformed template content
250
+ */
251
+ const attributeValueUpdate = (templateContent, tagName, attributeName, oldValue, newValue) => {
252
+ // Escape special regex characters in tag, attribute names, and values
253
+ const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
254
+ const escapedTag = escapeRegex(tagName);
255
+ const escapedAttr = escapeRegex(attributeName);
256
+ const escapedOldValue = escapeRegex(oldValue);
257
+ // Pattern for bound attributes [attribute]="'value'" (double quotes containing single quotes)
258
+ const boundDoubleSinglePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+\\[${escapedAttr}\\]\\s*=\\s*")'${escapedOldValue}'("\\s*[^>]*?>)`, 'gi');
259
+ // Pattern for bound attributes [attribute]="\"value\"" (double quotes containing double quotes)
260
+ const boundDoubleDoublePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+\\[${escapedAttr}\\]\\s*=\\s*")\\\\"${escapedOldValue}\\\\"("\\s*[^>]*?>)`, 'gi');
261
+ // Pattern for bound attributes [attribute]='value' (single quotes containing value)
262
+ const boundSinglePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+\\[${escapedAttr}\\]\\s*=\\s*)'${escapedOldValue}'([^>]*?>)`, 'gi');
263
+ // Pattern for static attributes attribute="value"
264
+ const staticAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+${escapedAttr}\\s*=\\s*["'])${escapedOldValue}(["'][^>]*?>)`, 'gi');
265
+ // Replace bound attributes [attribute]="'overflow'" -> [attribute]="'menu'"
266
+ let result = templateContent.replace(boundDoubleSinglePattern, `$1'${newValue}'$2`);
267
+ // Replace bound attributes [attribute]="\"overflow\"" -> [attribute]="\"menu\""
268
+ result = result.replace(boundDoubleDoublePattern, `$1\\"${newValue}\\"$2`);
269
+ // Replace bound attributes [attribute]='overflow' -> [attribute]='menu'
270
+ result = result.replace(boundSinglePattern, `$1'${newValue}'$2`);
271
+ // Replace static attributes showIcon="overflow" -> showIcon="menu"
272
+ result = result.replace(staticAttributePattern, `$1${newValue}$2`);
273
+ return result;
274
+ };
275
+ exports.attributeValueUpdate = attributeValueUpdate;
276
+ /**
277
+ * Removes attributes from HTML templates using regex patterns.
278
+ * This function can remove entire attributes or specific properties from object literal attributes.
279
+ *
280
+ * @param templateContent - The template string content to transform
281
+ * @param tagName - The HTML tag name to target (e.g., 'kendo-chat')
282
+ * @param attributeName - The attribute name to remove or modify
283
+ * @param propertyToRemove - Optional: specific property to remove from object literal attributes
284
+ * @returns The transformed template content
285
+ */
286
+ const attributeRemoval = (templateContent, tagName, attributeName, propertyToRemove) => {
287
+ // Escape special regex characters in tag and attribute names
288
+ const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
289
+ const escapedTag = escapeRegex(tagName);
290
+ const escapedAttr = escapeRegex(attributeName);
291
+ // If no propertyToRemove is specified, remove the entire attribute
292
+ if (!propertyToRemove) {
293
+ // Remove bound attributes [attribute]="value"
294
+ const boundAttributePattern = new RegExp(`(\\s+)\\[${escapedAttr}\\]\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?'|[^\\s>]+)`, 'gi');
295
+ // Remove static attributes attribute="value"
296
+ const staticAttributePattern = new RegExp(`(\\s+)${escapedAttr}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?'|[^\\s>]+)`, 'gi');
297
+ // Apply removals
298
+ let result = templateContent.replace(boundAttributePattern, '');
299
+ result = result.replace(staticAttributePattern, '');
300
+ return result;
45
301
  }
302
+ // Remove specific property from object literal attributes
303
+ const boundAttributePattern = new RegExp(`(<${escapedTag}[^>]*?\\s+\\[${escapedAttr}\\]\\s*=\\s*)("(?:[^"\\\\]|\\\\.)*?"|'(?:[^'\\\\]|\\\\.)*?'|[^\\s>]+)([^>]*?>)`, 'gi');
304
+ return templateContent.replace(boundAttributePattern, (match, beforeAttr, value, afterAttr) => {
305
+ // Remove quotes from value to analyze it
306
+ const trimmedValue = value.replace(/^["']|["']$/g, '');
307
+ // Check if it's an object literal (starts with { and ends with })
308
+ if (trimmedValue.trim().startsWith('{') && trimmedValue.trim().endsWith('}')) {
309
+ const objectLiteral = trimmedValue.trim();
310
+ // Create regex to remove the specific property
311
+ const propRegex = new RegExp(`\\s*${escapeRegex(propertyToRemove)}\\s*:\\s*[^,}]+\\s*(,\\s*)?`, 'g');
312
+ let newObjectLiteral = objectLiteral.replace(propRegex, '');
313
+ // Clean up trailing comma before closing brace
314
+ newObjectLiteral = newObjectLiteral.replace(/,\s*}$/, '}');
315
+ // If the object is now empty, remove the entire attribute
316
+ if (newObjectLiteral === '{}') {
317
+ return beforeAttr.replace(/\s+\[[^\]]+\]\s*=\s*$/, '') + afterAttr;
318
+ }
319
+ else {
320
+ // Restore quotes if the original value was quoted
321
+ if (value.match(/^["']/)) {
322
+ const quoteType = value.charAt(0);
323
+ return `${beforeAttr}${quoteType}${newObjectLiteral}${quoteType}${afterAttr}`;
324
+ }
325
+ else {
326
+ return `${beforeAttr}${newObjectLiteral}${afterAttr}`;
327
+ }
328
+ }
329
+ }
330
+ else {
331
+ // For non-object literals, we can't remove a specific property
332
+ console.warn(`Cannot remove property '${propertyToRemove}' from non-object literal: ${value}`);
333
+ return match; // Return unchanged
334
+ }
335
+ });
46
336
  };
47
- exports.templateStaticAttributeTransformer = templateStaticAttributeTransformer;
48
- const templateBoundAttributeTransformer = (root, tagName, attributeName, newAttributeName, valueProperty) => {
49
- const elements = Array.from(root.getElementsByTagName(tagName)) || [];
50
- for (const element of elements) {
51
- // Handle bound attributes like [title]="foo" or [title]="'foo'"
52
- const boundAttr = element.getAttribute(`[${attributeName}]`);
53
- if (boundAttr) {
54
- const newValue = valueProperty ? `${boundAttr}.${valueProperty}` : boundAttr;
55
- element.setAttribute(`[${newAttributeName}]`, newValue);
56
- element.removeAttribute(`[${attributeName}]`);
337
+ exports.attributeRemoval = attributeRemoval;
338
+ function tsPropertyRemoval(source, rootSource, j, packageName, typeName, propertyName) {
339
+ if (source.includes(typeName)) {
340
+ if (!isImportedFromPackage(rootSource, j, packageName, typeName)) {
341
+ return;
342
+ }
343
+ const typeVariables = new Set();
344
+ // Find function parameters of target type
345
+ rootSource.find(j.Function).forEach((path) => {
346
+ if (path.node.params) {
347
+ path.node.params.forEach((param) => {
348
+ if (param.type === 'Identifier' &&
349
+ param.typeAnnotation &&
350
+ param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
351
+ param.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
352
+ param.typeAnnotation.typeAnnotation.typeName.name === typeName) {
353
+ typeVariables.add(param.name);
354
+ }
355
+ });
356
+ }
357
+ });
358
+ // Find local variables of target type
359
+ rootSource.find(j.VariableDeclarator).forEach((path) => {
360
+ if (path.node.id.type === 'Identifier' &&
361
+ path.node.id.typeAnnotation &&
362
+ path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
363
+ path.node.id.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
364
+ path.node.id.typeAnnotation.typeAnnotation.typeName.name === typeName) {
365
+ typeVariables.add(path.node.id.name);
366
+ }
367
+ });
368
+ // Find class properties of target type
369
+ rootSource.find(j.ClassProperty).forEach((path) => {
370
+ if (path.node.key.type === 'Identifier' &&
371
+ path.node.typeAnnotation &&
372
+ path.node.typeAnnotation.typeAnnotation &&
373
+ path.node.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
374
+ path.node.typeAnnotation.typeAnnotation.typeName &&
375
+ path.node.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
376
+ path.node.typeAnnotation.typeAnnotation.typeName.name === typeName) {
377
+ typeVariables.add(path.node.key.name);
378
+ }
379
+ });
380
+ // Handle class properties with object expressions
381
+ rootSource
382
+ .find(j.ClassProperty)
383
+ .filter((path) => {
384
+ // Check if the property has the correct type annotation
385
+ return !!(path.node.typeAnnotation &&
386
+ path.node.typeAnnotation.typeAnnotation &&
387
+ path.node.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
388
+ path.node.typeAnnotation.typeAnnotation.typeName &&
389
+ path.node.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
390
+ path.node.typeAnnotation.typeAnnotation.typeName.name === typeName);
391
+ })
392
+ .forEach((path) => {
393
+ if (path.node.value && path.node.value.type === 'ObjectExpression') {
394
+ const properties = path.node.value.properties;
395
+ const propIndex = properties.findIndex((p) => p.type === 'ObjectProperty' &&
396
+ p.key &&
397
+ p.key.type === 'Identifier' &&
398
+ p.key.name === propertyName);
399
+ if (propIndex !== -1) {
400
+ // Just remove the property, leaving an empty object if it was the only one
401
+ properties.splice(propIndex, 1);
402
+ }
403
+ }
404
+ });
405
+ // Handle variable declarations with object literal initializers
406
+ // e.g., const modelFields: ConversationalUIModelFields = { pinnedByField: 'value', id: 'bar' };
407
+ rootSource
408
+ .find(j.VariableDeclarator, {
409
+ id: {
410
+ type: 'Identifier',
411
+ typeAnnotation: {
412
+ typeAnnotation: {
413
+ type: 'TSTypeReference',
414
+ typeName: {
415
+ name: typeName,
416
+ },
417
+ },
418
+ },
419
+ },
420
+ })
421
+ .forEach((path) => {
422
+ if (path.node.init && path.node.init.type === 'ObjectExpression') {
423
+ const properties = path.node.init.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 return statements with object literals
435
+ rootSource.find(j.ReturnStatement).forEach((path) => {
436
+ if (path.node.argument && path.node.argument.type === 'ObjectExpression') {
437
+ const properties = path.node.argument.properties;
438
+ const propIndex = properties.findIndex((p) => p.type === 'ObjectProperty' &&
439
+ p.key &&
440
+ p.key.type === 'Identifier' &&
441
+ p.key.name === propertyName);
442
+ if (propIndex !== -1) {
443
+ properties.splice(propIndex, 1);
444
+ }
445
+ }
446
+ });
447
+ // Handle direct member expression assignments to properties of variables/parameters
448
+ // e.g., fields.pinnedByField = value; or this.chatModelFields.pinnedByField = value;
449
+ rootSource
450
+ .find(j.AssignmentExpression)
451
+ .filter((path) => {
452
+ const { left } = path.node;
453
+ if (left.type === 'MemberExpression' &&
454
+ left.property.type === 'Identifier' &&
455
+ left.property.name === propertyName) {
456
+ // Check if the object is a variable/parameter of our type
457
+ if (left.object.type === 'Identifier' && typeVariables.has(left.object.name)) {
458
+ return true;
459
+ }
460
+ // Check if it's this.property where property is of our type
461
+ if (left.object.type === 'MemberExpression' &&
462
+ left.object.object.type === 'ThisExpression' &&
463
+ left.object.property.type === 'Identifier' &&
464
+ typeVariables.has(left.object.property.name)) {
465
+ return true;
466
+ }
467
+ // Check for type assertions: (expr as Type).property
468
+ if (left.object.type === 'TSAsExpression' &&
469
+ left.object.typeAnnotation.type === 'TSTypeReference' &&
470
+ left.object.typeAnnotation.typeName?.type === 'Identifier' &&
471
+ left.object.typeAnnotation.typeName.name === typeName) {
472
+ return true;
473
+ }
474
+ }
475
+ return false;
476
+ })
477
+ .forEach((path) => {
478
+ // Remove the entire expression statement
479
+ const statement = j(path).closest(j.ExpressionStatement);
480
+ if (statement.length > 0) {
481
+ statement.remove();
482
+ }
483
+ });
484
+ // Handle nested member expressions like chatConfig.chat.modelFields.pinnedByField
485
+ rootSource
486
+ .find(j.AssignmentExpression, {
487
+ left: {
488
+ type: 'MemberExpression',
489
+ object: {
490
+ type: 'MemberExpression',
491
+ },
492
+ property: {
493
+ name: propertyName,
494
+ },
495
+ },
496
+ })
497
+ .forEach((path) => {
498
+ j(path).closest(j.ExpressionStatement).remove();
499
+ });
500
+ return rootSource;
501
+ }
502
+ }
503
+ const tsComponentPropertyRemoval = (source, root, j, packageName, componentType, componentProperty, propertyToRemove) => {
504
+ if (source.includes(componentType)) {
505
+ // Check if componentType is imported from the specified package
506
+ if (!isImportedFromPackage(root, j, packageName, componentType)) {
507
+ return;
508
+ }
509
+ // Find all class properties that are of type componentType
510
+ const properties = new Set();
511
+ root.find(j.ClassProperty, {
512
+ typeAnnotation: {
513
+ typeAnnotation: {
514
+ typeName: {
515
+ name: componentType,
516
+ },
517
+ },
518
+ },
519
+ }).forEach((path) => {
520
+ if (path.node.key.type === 'Identifier') {
521
+ properties.add(path.node.key.name);
522
+ }
523
+ });
524
+ // Find function parameters of type componentType
525
+ const parameters = new Set();
526
+ root.find(j.FunctionDeclaration).forEach((path) => {
527
+ if (path.node.params) {
528
+ path.node.params.forEach((param) => {
529
+ if (param.type === 'Identifier' &&
530
+ param.typeAnnotation &&
531
+ param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
532
+ param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
533
+ param.typeAnnotation.typeAnnotation.typeName.name === componentType) {
534
+ parameters.add(param.name);
535
+ }
536
+ });
537
+ }
538
+ });
539
+ // Also check method declarations in classes
540
+ root.find(j.ClassMethod).forEach((path) => {
541
+ if (path.node.params) {
542
+ path.node.params.forEach((param) => {
543
+ if (param.type === 'Identifier' &&
544
+ param.typeAnnotation &&
545
+ param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
546
+ param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
547
+ param.typeAnnotation.typeAnnotation.typeName.name === componentType) {
548
+ parameters.add(param.name);
549
+ }
550
+ });
551
+ }
552
+ });
553
+ // Also check arrow functions
554
+ root.find(j.ArrowFunctionExpression).forEach((path) => {
555
+ if (path.node.params) {
556
+ path.node.params.forEach((param) => {
557
+ if (param.type === 'Identifier' &&
558
+ param.typeAnnotation &&
559
+ param.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
560
+ param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
561
+ param.typeAnnotation.typeAnnotation.typeName.name === componentType) {
562
+ parameters.add(param.name);
563
+ }
564
+ });
565
+ }
566
+ });
567
+ // Find local variable declarations of type componentType
568
+ const localVariables = new Set();
569
+ root.find(j.VariableDeclarator).forEach((path) => {
570
+ if (path.node.id.type === 'Identifier' &&
571
+ path.node.id.typeAnnotation &&
572
+ path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
573
+ path.node.id.typeAnnotation.typeAnnotation.typeName.type === 'Identifier' &&
574
+ path.node.id.typeAnnotation.typeAnnotation.typeName.name === componentType) {
575
+ localVariables.add(path.node.id.name);
576
+ }
577
+ });
578
+ // Find array variables of type componentType[]
579
+ // This handles cases like: const arr: ChatComponent[] = [...]; arr[0].property = value;
580
+ const arrayVariables = new Set();
581
+ root.find(j.VariableDeclarator).forEach((path) => {
582
+ if (path.node.id.type === 'Identifier' &&
583
+ path.node.id.typeAnnotation &&
584
+ path.node.id.typeAnnotation.typeAnnotation?.type === 'TSArrayType' &&
585
+ path.node.id.typeAnnotation.typeAnnotation.elementType?.type === 'TSTypeReference' &&
586
+ path.node.id.typeAnnotation.typeAnnotation.elementType.typeName?.type === 'Identifier' &&
587
+ path.node.id.typeAnnotation.typeAnnotation.elementType.typeName.name === componentType) {
588
+ arrayVariables.add(path.node.id.name);
589
+ }
590
+ });
591
+ // Find object properties that have componentType (e.g., {chat: ChatComponent})
592
+ // This handles cases like: const config: {chat: ChatComponent} = {...}; config.chat.property = value;
593
+ const objectProperties = new Map(); // Maps variable name to property name
594
+ root.find(j.VariableDeclarator).forEach((path) => {
595
+ if (path.node.id.type === 'Identifier' &&
596
+ path.node.id.typeAnnotation &&
597
+ path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeLiteral') {
598
+ const varName = path.node.id.name;
599
+ const members = path.node.id.typeAnnotation.typeAnnotation.members;
600
+ members.forEach((member) => {
601
+ if (member.type === 'TSPropertySignature' &&
602
+ member.key &&
603
+ member.key.type === 'Identifier' &&
604
+ member.typeAnnotation &&
605
+ member.typeAnnotation.typeAnnotation?.type === 'TSTypeReference' &&
606
+ member.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
607
+ member.typeAnnotation.typeAnnotation.typeName.name === componentType) {
608
+ if (!objectProperties.has(varName)) {
609
+ objectProperties.set(varName, []);
610
+ }
611
+ objectProperties.get(varName).push(member.key.name);
612
+ }
613
+ });
614
+ }
615
+ });
616
+ // If no propertyToRemove is specified, remove the entire componentProperty
617
+ if (!propertyToRemove) {
618
+ // Handle direct property assignments like: foo.scrollable = value;
619
+ root.find(j.AssignmentExpression)
620
+ .filter((path) => {
621
+ const { left } = path.value;
622
+ // Check if this assigns to component.componentProperty
623
+ if (left &&
624
+ left.type === 'MemberExpression' &&
625
+ left.property &&
626
+ left.property.name === componentProperty) {
627
+ // Check if the base object is our component type
628
+ return isComponentTypeMatch(root, j, left.object, componentType);
629
+ }
630
+ return false;
631
+ })
632
+ .forEach((path) => {
633
+ // Remove the entire statement
634
+ j(path).closest(j.ExpressionStatement).remove();
635
+ });
636
+ return root;
57
637
  }
638
+ // CASE 1: Handle direct property assignments like: foo.scrollable.mouseScrollSpeed = 3000;
639
+ root.find(j.AssignmentExpression)
640
+ .filter((path) => {
641
+ const { left } = path.value;
642
+ // Check if this is a member expression assignment
643
+ if (left && left.type === 'MemberExpression') {
644
+ // Check if we're accessing the property to remove
645
+ if (left.property && left.property.name === propertyToRemove) {
646
+ // Check if we're accessing it from component.componentProperty
647
+ const obj = left.object;
648
+ if (obj &&
649
+ obj.type === 'MemberExpression' &&
650
+ obj.property &&
651
+ obj.property.name === componentProperty) {
652
+ // Now check if the base object is our component type (includes all cases: this, parameters, variables, casts)
653
+ return isComponentTypeMatch(root, j, obj.object, componentType);
654
+ }
655
+ }
656
+ }
657
+ return false;
658
+ })
659
+ .forEach((path) => {
660
+ // Remove the entire statement
661
+ j(path).closest(j.ExpressionStatement).remove();
662
+ });
663
+ // CASE 2 & 3: Handle object assignments like: foo.scrollable = { mouseScrollSpeed: 3000, ... };
664
+ root.find(j.AssignmentExpression)
665
+ .filter((path) => {
666
+ const { left, right } = path.value;
667
+ // Check if this assigns to component.componentProperty
668
+ if (left &&
669
+ left.type === 'MemberExpression' &&
670
+ left.property &&
671
+ left.property.name === componentProperty &&
672
+ right &&
673
+ right.type === 'ObjectExpression') {
674
+ // Check if the base object is our component type (includes all cases: this, parameters, variables, casts)
675
+ return isComponentTypeMatch(root, j, left.object, componentType);
676
+ }
677
+ return false;
678
+ })
679
+ .forEach((path) => {
680
+ const properties = path.value.right.properties;
681
+ // Find the property we want to remove
682
+ const propIndex = properties.findIndex((p) => p &&
683
+ p.type === 'ObjectProperty' &&
684
+ p.key &&
685
+ p.key.type === 'Identifier' &&
686
+ p.key.name === propertyToRemove);
687
+ if (propIndex !== -1) {
688
+ // Case 2: If it's the only property, remove the entire statement
689
+ if (properties.length === 1) {
690
+ j(path).closest(j.ExpressionStatement).remove();
691
+ }
692
+ // Case 3: If there are other properties, just remove this one property
693
+ else {
694
+ properties.splice(propIndex, 1);
695
+ }
696
+ }
697
+ });
698
+ return root;
58
699
  }
59
700
  };
60
- exports.templateBoundAttributeTransformer = templateBoundAttributeTransformer;
61
- const templateAttributeTransformer = (root, tagName, attributeName, newAttributeName) => {
62
- (0, exports.templateBoundAttributeTransformer)(root, tagName, attributeName, newAttributeName);
63
- (0, exports.templateStaticAttributeTransformer)(root, tagName, attributeName, newAttributeName);
64
- };
65
- exports.templateAttributeTransformer = templateAttributeTransformer;
66
- const tsPropertyTransformer = (source, root, j, componentType, propertyName, newPropertyName, valueProperty) => {
701
+ exports.tsComponentPropertyRemoval = tsComponentPropertyRemoval;
702
+ const tsPropertyTransformer = (source, root, j, packageName, componentType, propertyName, newPropertyName, valueProperty) => {
67
703
  if (source.includes(componentType)) {
68
- // Find all class properties that are of type DropDownListComponent
704
+ // Check if componentType is imported from the specified package
705
+ if (!isImportedFromPackage(root, j, packageName, componentType)) {
706
+ return;
707
+ }
708
+ // Find all class properties that are of type componentType
69
709
  const properties = new Set();
70
710
  // Find properties with type annotations
71
711
  root.find(j.ClassProperty, {
@@ -135,7 +775,7 @@ const tsPropertyTransformer = (source, root, j, componentType, propertyName, new
135
775
  localVariables.add(path.node.id.name);
136
776
  }
137
777
  });
138
- // Find all member expressions where title property is accessed on any componentType instance
778
+ // Find all member expressions where propertyName is accessed on any componentType instance
139
779
  root.find(j.MemberExpression, {
140
780
  property: {
141
781
  type: 'Identifier',
@@ -155,6 +795,18 @@ const tsPropertyTransformer = (source, root, j, componentType, propertyName, new
155
795
  if (path.node.object.type === 'Identifier') {
156
796
  return parameters.has(path.node.object.name) || localVariables.has(path.node.object.name);
157
797
  }
798
+ // Handle TypeScript type assertions like (this.componentProperty as ComponentType).property
799
+ if (path.node.object.type === 'TSAsExpression') {
800
+ const typeAssertion = path.node.object;
801
+ // Check if the type assertion is casting to our componentType
802
+ if (typeAssertion.typeAnnotation &&
803
+ typeAssertion.typeAnnotation.type === 'TSTypeReference' &&
804
+ typeAssertion.typeAnnotation.typeName &&
805
+ typeAssertion.typeAnnotation.typeName.type === 'Identifier' &&
806
+ typeAssertion.typeAnnotation.typeName.name === componentType) {
807
+ return true;
808
+ }
809
+ }
158
810
  return false;
159
811
  })
160
812
  .forEach((path) => {
@@ -195,91 +847,12 @@ const tsPropertyTransformer = (source, root, j, componentType, propertyName, new
195
847
  }
196
848
  };
197
849
  exports.tsPropertyTransformer = tsPropertyTransformer;
198
- const htmlStaticAttributeTransformer = (fileInfo, tagName, oldName, newName) => {
199
- const fileContent = fileInfo.source;
200
- const root = (0, node_html_parser_1.default)(fileContent, { comment: true, blockTextElements: exports.blockTextElements });
201
- let modified = false;
202
- const elements = Array.from(root.querySelectorAll(tagName));
203
- for (const element of elements) {
204
- const staticAttr = element.getAttribute(oldName);
205
- if (staticAttr) {
206
- element.removeAttribute(oldName);
207
- element.setAttribute(newName, staticAttr);
208
- modified = true;
209
- }
210
- }
211
- if (modified) {
212
- return root.toString();
213
- }
214
- return fileContent;
215
- };
216
- exports.htmlStaticAttributeTransformer = htmlStaticAttributeTransformer;
217
- const htmlBoundAttributeTransformer = (fileInfo, tagName, oldName, newName, valueProperty) => {
218
- const fileContent = fileInfo.source;
219
- const root = (0, node_html_parser_1.default)(fileContent, { comment: true, blockTextElements: exports.blockTextElements });
220
- let modified = false;
221
- const elements = Array.from(root.querySelectorAll(tagName));
222
- for (const element of elements) {
223
- const boundAttr = element.getAttribute(`[${oldName}]`);
224
- if (boundAttr) {
225
- element.removeAttribute(`[${oldName}]`);
226
- // If valueProperty is provided, append it to the bound value
227
- const newValue = valueProperty ? `${boundAttr}.${valueProperty}` : boundAttr;
228
- element.setAttribute(`[${newName}]`, newValue);
229
- modified = true;
230
- }
231
- }
232
- if (modified) {
233
- return root.toString();
234
- }
235
- return fileContent;
236
- };
237
- exports.htmlBoundAttributeTransformer = htmlBoundAttributeTransformer;
238
- const htmlAttributeTransformer = (fileInfo, tagName, oldName, newName) => {
239
- let content = (0, exports.htmlBoundAttributeTransformer)(fileInfo, tagName, oldName, newName);
240
- content = (0, exports.htmlStaticAttributeTransformer)({ path: fileInfo.path, source: content }, tagName, oldName, newName);
241
- return content;
242
- };
243
- exports.htmlAttributeTransformer = htmlAttributeTransformer;
244
- const htmlEventTransformer = (fileInfo, tagName, oldEventName, newEventName) => {
245
- const fileContent = fileInfo.source;
246
- const root = (0, node_html_parser_1.default)(fileContent, { comment: true, blockTextElements: exports.blockTextElements });
247
- let modified = false;
248
- const elements = Array.from(root.querySelectorAll(tagName));
249
- for (const element of elements) {
250
- // Handle event bindings like (actionClick)="handler($event)"
251
- const eventAttr = element.getAttribute(`(${oldEventName})`);
252
- if (eventAttr) {
253
- element.removeAttribute(`(${oldEventName})`);
254
- element.setAttribute(`(${newEventName})`, eventAttr);
255
- modified = true;
256
- }
257
- }
258
- if (modified) {
259
- return root.toString();
260
- }
261
- return fileContent;
262
- };
263
- exports.htmlEventTransformer = htmlEventTransformer;
264
- const templateAttributeValueTransformer = (root, tagName, attributeName, oldAttributeValue, newAttributeValue) => {
265
- const elements = Array.from(root.getElementsByTagName(tagName)) || [];
266
- for (const element of elements) {
267
- // Handle bound attributes (e.g., [showText]="'overflow'")
268
- const boundAttr = element.getAttribute(`[${attributeName}]`);
269
- if (boundAttr === `'${oldAttributeValue}'`) {
270
- // For bound literals like [showText]="'overflow'" or [showText]="\"overflow\""
271
- element.setAttribute(`[${attributeName}]`, boundAttr.replace(oldAttributeValue, newAttributeValue));
272
- }
273
- // Handle static attributes like title="foo"
274
- const staticAttrValue = element.getAttribute(attributeName);
275
- if (staticAttrValue === oldAttributeValue) {
276
- element.setAttribute(attributeName, newAttributeValue);
277
- }
278
- }
279
- };
280
- exports.templateAttributeValueTransformer = templateAttributeValueTransformer;
281
- const tsPropertyValueTransformer = (source, root, j, typeName, oldValue, newValue) => {
850
+ const tsPropertyValueTransformer = (source, root, j, packageName, typeName, oldValue, newValue) => {
282
851
  if (source.includes(typeName)) {
852
+ // Check if typeName is imported from the specified package
853
+ if (!isImportedFromPackage(root, j, packageName, typeName)) {
854
+ return;
855
+ }
283
856
  root.find(j.ClassProperty)
284
857
  .filter((path) => {
285
858
  if (path.node.typeAnnotation?.typeAnnotation &&
@@ -334,276 +907,11 @@ const tsPropertyValueTransformer = (source, root, j, typeName, oldValue, newValu
334
907
  }
335
908
  };
336
909
  exports.tsPropertyValueTransformer = tsPropertyValueTransformer;
337
- const htmlAttributeValueTransformer = (fileInfo, tagName, attributeName, oldValue, newValue) => {
338
- const fileContent = fileInfo.source;
339
- const root = (0, node_html_parser_1.default)(fileContent, { comment: true, blockTextElements: exports.blockTextElements });
340
- const elements = root.querySelectorAll(tagName);
341
- let modified = false;
342
- for (const element of elements) {
343
- // Handle static attributes (e.g., showText="overflow")
344
- const staticAttr = element.getAttribute(attributeName);
345
- if (staticAttr === oldValue) {
346
- element.setAttribute(attributeName, newValue);
347
- modified = true;
348
- console.log(`Modified static attribute ${attributeName} from "${oldValue}" to "${newValue}" in element:`, element.toString().substring(0, 100));
349
- }
350
- // Handle bound attributes (e.g., [showText]="overflow")
351
- const boundAttr = element.getAttribute(`[${attributeName}]`);
352
- if (boundAttr) {
353
- // For bound literals like [showText]="'overflow'" or [showText]="\"overflow\""
354
- if (boundAttr === `'${oldValue}'` || boundAttr === `"${oldValue}"`) {
355
- const updatedValue = boundAttr.replace(oldValue, newValue);
356
- element.setAttribute(`[${attributeName}]`, updatedValue);
357
- modified = true;
358
- console.log(`Modified bound attribute [${attributeName}] from "${boundAttr}" to "${updatedValue}" in element:`, element.toString().substring(0, 100));
359
- }
360
- }
361
- }
362
- if (modified) {
363
- const updatedContent = root.toString();
364
- return updatedContent;
365
- }
366
- return fileContent;
367
- };
368
- exports.htmlAttributeValueTransformer = htmlAttributeValueTransformer;
369
- const htmlAttributeRemoval = (fileInfo, tagName, attributeName, propertyToRemove) => {
370
- const filePath = fileInfo.path;
371
- const fileContent = fileInfo.source;
372
- const root = (0, node_html_parser_1.default)(fileContent, { comment: true, blockTextElements: exports.blockTextElements });
373
- // Use the same logic as templateAttributeRemoval
374
- const elements = root.querySelectorAll(tagName);
375
- for (const element of elements) {
376
- // If no propertyToRemove is specified, remove the entire attribute
377
- if (!propertyToRemove) {
378
- // Remove both bound and static attributes
379
- element.removeAttribute(`[${attributeName}]`);
380
- element.removeAttribute(attributeName);
381
- continue;
382
- }
383
- // Look for bound attribute (e.g., [scrollable]="...")
384
- const boundAttr = element.getAttribute(`[${attributeName}]`);
385
- if (boundAttr) {
386
- if (boundAttr.trim().startsWith('{') && boundAttr.trim().endsWith('}')) {
387
- const objectLiteral = boundAttr.trim();
388
- const propRegex = new RegExp(`\\s*${propertyToRemove}\\s*:\\s*[^,}]+\\s*(,\\s*)?`, 'g');
389
- let newObjectLiteral = objectLiteral.replace(propRegex, '');
390
- newObjectLiteral = newObjectLiteral.replace(/,\s*}$/, '}');
391
- if (newObjectLiteral === '{}') {
392
- element.removeAttribute(`[${attributeName}]`);
393
- }
394
- else {
395
- element.setAttribute(`[${attributeName}]`, newObjectLiteral);
396
- }
397
- }
398
- else {
399
- console.warn(`Cannot remove property from variable reference: ${boundAttr} in file ${filePath}`);
400
- }
401
- }
402
- }
403
- return root.toString();
404
- };
405
- exports.htmlAttributeRemoval = htmlAttributeRemoval;
406
- const templateAttributeRemoval = (root, tagName, attributeName, propertyToRemove) => {
407
- const elements = root.querySelectorAll(tagName);
408
- for (const element of elements) {
409
- if (!propertyToRemove) {
410
- element.removeAttribute(`[${attributeName}]`);
411
- element.removeAttribute(attributeName);
412
- continue;
413
- }
414
- const boundAttr = element.getAttribute(`[${attributeName}]`);
415
- if (boundAttr) {
416
- if (boundAttr.trim().startsWith('{') && boundAttr.trim().endsWith('}')) {
417
- const objectLiteral = boundAttr.trim();
418
- const propRegex = new RegExp(`\\s*${propertyToRemove}\\s*:\\s*[^,}]+\\s*(,\\s*)?`, 'g');
419
- let newObjectLiteral = objectLiteral.replace(propRegex, '');
420
- newObjectLiteral = newObjectLiteral.replace(/,\s*}$/, '}');
421
- if (newObjectLiteral === '{}') {
422
- element.removeAttribute(`[${attributeName}]`);
423
- }
424
- else {
425
- element.setAttribute(`[${attributeName}]`, newObjectLiteral);
426
- }
427
- }
428
- else {
429
- console.warn(`Cannot remove property from variable reference: ${boundAttr}`);
430
- }
431
- }
432
- }
433
- };
434
- exports.templateAttributeRemoval = templateAttributeRemoval;
435
- function tsPropertyRemoval(source, rootSource, j, typeName, propertyName) {
436
- if (source.includes(typeName)) {
437
- rootSource
438
- .find(j.ClassProperty, {
439
- typeAnnotation: {
440
- typeAnnotation: {
441
- typeName: {
442
- name: typeName,
443
- },
444
- },
445
- },
446
- })
447
- .forEach((path) => {
448
- if (path.node.value && path.node.value.type === 'ObjectExpression') {
449
- const properties = path.node.value.properties;
450
- const propIndex = properties.findIndex((p) => p.type === 'ObjectProperty' &&
451
- p.key &&
452
- p.key.type === 'Identifier' &&
453
- p.key.name === propertyName);
454
- if (propIndex !== -1) {
455
- if (properties.length === 1) {
456
- j(path).remove();
457
- }
458
- else {
459
- properties.splice(propIndex, 1);
460
- }
461
- }
462
- }
463
- });
464
- rootSource
465
- .find(j.AssignmentExpression, {
466
- left: {
467
- type: 'MemberExpression',
468
- object: {
469
- type: 'MemberExpression',
470
- },
471
- property: {
472
- name: propertyName,
473
- },
474
- },
475
- })
476
- .forEach((path) => {
477
- j(path).remove();
478
- });
479
- return rootSource;
480
- }
481
- }
482
- const tsComponentPropertyRemoval = (source, root, j, componentType, componentProperty, propertyToRemove) => {
483
- if (source.includes(componentType)) {
484
- // If no propertyToRemove is specified, remove the entire componentProperty
485
- if (!propertyToRemove) {
486
- // Handle direct property assignments like: foo.scrollable = value;
487
- root.find(j.AssignmentExpression)
488
- .filter((path) => {
489
- const { left } = path.value;
490
- // Check if this assigns to component.componentProperty
491
- if (left &&
492
- left.type === 'MemberExpression' &&
493
- left.property &&
494
- left.property.name === componentProperty) {
495
- // Check if the base object is our component type
496
- return isComponentTypeMatch(root, j, left.object, componentType);
497
- }
498
- return false;
499
- })
500
- .forEach((path) => {
501
- // Remove the entire statement
502
- j(path).closest(j.ExpressionStatement).remove();
503
- });
504
- return root;
505
- }
506
- // CASE 1: Handle direct property assignments like: foo.scrollable.mouseScrollSpeed = 3000;
507
- root.find(j.AssignmentExpression)
508
- .filter((path) => {
509
- const { left } = path.value;
510
- // Check if this is a member expression assignment
511
- if (left && left.type === 'MemberExpression') {
512
- // Check if we're accessing the property to remove
513
- if (left.property && left.property.name === propertyToRemove) {
514
- // Check if we're accessing it from component.componentProperty
515
- const obj = left.object;
516
- if (obj &&
517
- obj.type === 'MemberExpression' &&
518
- obj.property &&
519
- obj.property.name === componentProperty) {
520
- // Now check if the base object is our component type
521
- return isComponentTypeMatch(root, j, obj.object, componentType);
522
- }
523
- }
524
- }
525
- return false;
526
- })
527
- .forEach((path) => {
528
- // Remove the entire statement
529
- j(path).closest(j.ExpressionStatement).remove();
530
- });
531
- // CASE 2 & 3: Handle object assignments like: foo.scrollable = { mouseScrollSpeed: 3000, ... };
532
- root.find(j.AssignmentExpression)
533
- .filter((path) => {
534
- const { left, right } = path.value;
535
- // Check if this assigns to component.componentProperty
536
- if (left &&
537
- left.type === 'MemberExpression' &&
538
- left.property &&
539
- left.property.name === componentProperty &&
540
- right &&
541
- right.type === 'ObjectExpression') {
542
- // Check if the base object is our component type
543
- return isComponentTypeMatch(root, j, left.object, componentType);
544
- }
545
- return false;
546
- })
547
- .forEach((path) => {
548
- const properties = path.value.right.properties;
549
- // Find the property we want to remove
550
- const propIndex = properties.findIndex((p) => p &&
551
- p.type === 'ObjectProperty' &&
552
- p.key &&
553
- p.key.type === 'Identifier' &&
554
- p.key.name === propertyToRemove);
555
- if (propIndex !== -1) {
556
- // Case 2: If it's the only property, remove the entire statement
557
- if (properties.length === 1) {
558
- j(path).closest(j.ExpressionStatement).remove();
559
- }
560
- // Case 3: If there are other properties, just remove this one property
561
- else {
562
- properties.splice(propIndex, 1);
563
- }
564
- }
565
- });
566
- return root;
567
- }
568
- };
569
- exports.tsComponentPropertyRemoval = tsComponentPropertyRemoval;
570
- // Helper function to check if a node is a component of the specified type
571
- function isComponentTypeMatch(root, j, node, componentType) {
572
- if (!node)
573
- return false;
574
- if (node.type === 'ThisExpression') {
575
- return true;
576
- }
577
- if (node.type === 'Identifier') {
578
- const paramName = node.name;
579
- return root.find(j.Function).some((path) => {
580
- return (path.node.params &&
581
- path.node.params.some((param) => param.type === 'Identifier' &&
582
- param.name === paramName &&
583
- param.typeAnnotation?.typeAnnotation?.typeName?.name === componentType));
584
- });
585
- }
586
- if (node.type === 'MemberExpression') {
587
- if (node.object.type === 'ThisExpression' && node.property.type === 'Identifier') {
588
- const propName = node.property.name;
589
- return (root
590
- .find(j.ClassProperty, {
591
- key: { name: propName },
592
- typeAnnotation: {
593
- typeAnnotation: {
594
- typeName: { name: componentType },
595
- },
596
- },
597
- })
598
- .size() > 0);
599
- }
600
- }
601
- return false;
602
- }
603
910
  const tsInterfaceTransformer = (fileInfo, rootSource, j, packageName, interfaceName, newName) => {
604
911
  const source = fileInfo.source;
605
912
  if (source.includes(interfaceName)) {
606
- let isImportedFromKendoListbox = false;
913
+ // Check if interface is imported from the specified package and rename it
914
+ let isImported = false;
607
915
  rootSource.find(j.ImportDeclaration).forEach((path) => {
608
916
  if (path.node.source &&
609
917
  path.node.source.value === packageName &&
@@ -612,13 +920,13 @@ const tsInterfaceTransformer = (fileInfo, rootSource, j, packageName, interfaceN
612
920
  if (specifier.type === 'ImportSpecifier' &&
613
921
  specifier.imported.type === 'Identifier' &&
614
922
  specifier.imported.name === interfaceName) {
615
- isImportedFromKendoListbox = true;
923
+ isImported = true;
616
924
  specifier.imported.name = newName;
617
925
  }
618
926
  });
619
927
  }
620
928
  });
621
- if (!isImportedFromKendoListbox) {
929
+ if (!isImported) {
622
930
  return;
623
931
  }
624
932
  rootSource.find(j.ClassProperty).forEach((path) => {
@@ -706,6 +1014,109 @@ const tsInterfaceTransformer = (fileInfo, rootSource, j, packageName, interfaceN
706
1014
  path.node.typeAnnotation.typeName.name = newName;
707
1015
  }
708
1016
  });
1017
+ // Handle constructor calls with 'new' keyword
1018
+ rootSource.find(j.NewExpression).forEach((path) => {
1019
+ if (path.node.callee &&
1020
+ path.node.callee.type === 'Identifier' &&
1021
+ path.node.callee.name === interfaceName) {
1022
+ path.node.callee.name = newName;
1023
+ }
1024
+ });
709
1025
  }
710
1026
  };
711
1027
  exports.tsInterfaceTransformer = tsInterfaceTransformer;
1028
+ // Helper function to check if a node is a component of the specified type
1029
+ function isComponentTypeMatch(root, j, node, componentType) {
1030
+ if (!node)
1031
+ return false;
1032
+ if (node.type === 'ThisExpression') {
1033
+ return true;
1034
+ }
1035
+ if (node.type === 'Identifier') {
1036
+ const paramName = node.name;
1037
+ // Check function parameters
1038
+ const isParameter = root.find(j.Function).some((path) => {
1039
+ return (path.node.params &&
1040
+ path.node.params.some((param) => param.type === 'Identifier' &&
1041
+ param.name === paramName &&
1042
+ param.typeAnnotation?.typeAnnotation?.typeName?.name === componentType));
1043
+ });
1044
+ if (isParameter)
1045
+ return true;
1046
+ // Check local variable declarations
1047
+ const isLocalVariable = root.find(j.VariableDeclarator).some((path) => {
1048
+ return (path.node.id.type === 'Identifier' &&
1049
+ path.node.id.name === paramName &&
1050
+ path.node.id.typeAnnotation?.typeAnnotation?.type === 'TSTypeReference' &&
1051
+ path.node.id.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
1052
+ path.node.id.typeAnnotation.typeAnnotation.typeName.name === componentType);
1053
+ });
1054
+ return isLocalVariable;
1055
+ }
1056
+ if (node.type === 'MemberExpression') {
1057
+ if (node.object.type === 'ThisExpression' && node.property.type === 'Identifier') {
1058
+ const propName = node.property.name;
1059
+ return (root
1060
+ .find(j.ClassProperty, {
1061
+ key: { name: propName },
1062
+ typeAnnotation: {
1063
+ typeAnnotation: {
1064
+ typeName: { name: componentType },
1065
+ },
1066
+ },
1067
+ })
1068
+ .size() > 0);
1069
+ }
1070
+ // Handle nested member expressions like chatConfig.chat where chat is ChatComponent
1071
+ if (node.object.type === 'Identifier' && node.property.type === 'Identifier') {
1072
+ const varName = node.object.name;
1073
+ const propName = node.property.name;
1074
+ // Check if this is an object with a property of componentType
1075
+ const hasMatchingProperty = root.find(j.VariableDeclarator).some((path) => {
1076
+ if (path.node.id.type === 'Identifier' &&
1077
+ path.node.id.name === varName &&
1078
+ path.node.id.typeAnnotation &&
1079
+ path.node.id.typeAnnotation.typeAnnotation?.type === 'TSTypeLiteral') {
1080
+ const members = path.node.id.typeAnnotation.typeAnnotation.members;
1081
+ return members.some((member) => {
1082
+ return (member.type === 'TSPropertySignature' &&
1083
+ member.key?.type === 'Identifier' &&
1084
+ member.key.name === propName &&
1085
+ member.typeAnnotation?.typeAnnotation?.type === 'TSTypeReference' &&
1086
+ member.typeAnnotation.typeAnnotation.typeName?.type === 'Identifier' &&
1087
+ member.typeAnnotation.typeAnnotation.typeName.name === componentType);
1088
+ });
1089
+ }
1090
+ return false;
1091
+ });
1092
+ return hasMatchingProperty;
1093
+ }
1094
+ }
1095
+ // Handle array element access like chatComponents[0] where chatComponents is ChatComponent[]
1096
+ // When we have chatComponents[0].property, node is chatComponents[0] which is a MemberExpression with computed: true
1097
+ if (node.type === 'MemberExpression' && node.computed === true && node.object.type === 'Identifier') {
1098
+ const arrayName = node.object.name;
1099
+ // Check if this array is of type ChatComponent[]
1100
+ const isComponentArray = root.find(j.VariableDeclarator).some((path) => {
1101
+ return (path.node.id.type === 'Identifier' &&
1102
+ path.node.id.name === arrayName &&
1103
+ path.node.id.typeAnnotation?.typeAnnotation?.type === 'TSArrayType' &&
1104
+ path.node.id.typeAnnotation.typeAnnotation.elementType?.type === 'TSTypeReference' &&
1105
+ path.node.id.typeAnnotation.typeAnnotation.elementType.typeName?.type === 'Identifier' &&
1106
+ path.node.id.typeAnnotation.typeAnnotation.elementType.typeName.name === componentType);
1107
+ });
1108
+ return isComponentArray;
1109
+ }
1110
+ // Handle TypeScript type assertions like (this.componentProperty as ComponentType)
1111
+ if (node.type === 'TSAsExpression') {
1112
+ // Check if the type assertion is casting to our componentType
1113
+ if (node.typeAnnotation &&
1114
+ node.typeAnnotation.type === 'TSTypeReference' &&
1115
+ node.typeAnnotation.typeName &&
1116
+ node.typeAnnotation.typeName.type === 'Identifier' &&
1117
+ node.typeAnnotation.typeName.name === componentType) {
1118
+ return true;
1119
+ }
1120
+ }
1121
+ return false;
1122
+ }