@progress/kendo-angular-dropdowns 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 +805 -394
- package/codemods/v19/autocomplete-subtitle.js +10 -13
- package/codemods/v19/autocomplete-title.js +10 -13
- package/codemods/v19/combobox-subtitle.js +10 -13
- package/codemods/v19/combobox-title.js +10 -14
- package/codemods/v19/dropdownlist-subtitle.js +10 -13
- package/codemods/v19/dropdownlist-title.js +10 -14
- package/codemods/v19/dropdowntree-subtitle.js +10 -13
- package/codemods/v19/dropdowntree-title.js +10 -13
- package/codemods/v19/multicolumncombobox-subtitle.js +10 -13
- package/codemods/v19/multicolumncombobox-title.js +10 -13
- package/codemods/v19/multiselect-subtitle.js +10 -13
- package/codemods/v19/multiselect-title.js +10 -13
- package/codemods/v19/multiselecttree-subtitle.js +10 -13
- package/codemods/v19/multiselecttree-title.js +10 -13
- package/esm2022/package-metadata.mjs +2 -2
- package/fesm2022/progress-kendo-angular-dropdowns.mjs +2 -2
- package/package.json +11 -12
- package/schematics/ngAdd/index.js +2 -2
- package/codemods/template-transformer/index.js +0 -93
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.
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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.
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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.
|
|
61
|
-
const
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
923
|
+
isImported = true;
|
|
616
924
|
specifier.imported.name = newName;
|
|
617
925
|
}
|
|
618
926
|
});
|
|
619
927
|
}
|
|
620
928
|
});
|
|
621
|
-
if (!
|
|
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
|
+
}
|