@feardread/fear 1.0.1
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/FEAR.js +459 -0
- package/FEARServer.js +280 -0
- package/controllers/agent.js +438 -0
- package/controllers/auth/index.js +345 -0
- package/controllers/auth/token.js +50 -0
- package/controllers/blog.js +105 -0
- package/controllers/brand.js +10 -0
- package/controllers/cart.js +425 -0
- package/controllers/category.js +9 -0
- package/controllers/coupon.js +63 -0
- package/controllers/crud/crud.js +508 -0
- package/controllers/crud/index.js +36 -0
- package/controllers/email.js +34 -0
- package/controllers/enquiry.js +65 -0
- package/controllers/events.js +9 -0
- package/controllers/order.js +125 -0
- package/controllers/payment.js +31 -0
- package/controllers/product.js +147 -0
- package/controllers/review.js +247 -0
- package/controllers/tag.js +10 -0
- package/controllers/task.js +10 -0
- package/controllers/upload.js +41 -0
- package/controllers/user.js +401 -0
- package/index.js +7 -0
- package/libs/agent/index.js +561 -0
- package/libs/agent/modules/ai/ai.js +285 -0
- package/libs/agent/modules/ai/chat.js +518 -0
- package/libs/agent/modules/ai/config.js +688 -0
- package/libs/agent/modules/ai/operations.js +787 -0
- package/libs/agent/modules/analyze/api.js +546 -0
- package/libs/agent/modules/analyze/dorks.js +395 -0
- package/libs/agent/modules/ccard/README.md +454 -0
- package/libs/agent/modules/ccard/audit.js +479 -0
- package/libs/agent/modules/ccard/checker.js +674 -0
- package/libs/agent/modules/ccard/payment-processors.json +16 -0
- package/libs/agent/modules/ccard/validator.js +629 -0
- package/libs/agent/modules/code/analyzer.js +303 -0
- package/libs/agent/modules/code/jquery.js +1093 -0
- package/libs/agent/modules/code/react.js +1536 -0
- package/libs/agent/modules/code/refactor.js +499 -0
- package/libs/agent/modules/crypto/exchange.js +564 -0
- package/libs/agent/modules/net/proxy.js +409 -0
- package/libs/agent/modules/security/cve.js +442 -0
- package/libs/agent/modules/security/monitor.js +360 -0
- package/libs/agent/modules/security/scanner.js +300 -0
- package/libs/agent/modules/security/vulnerability.js +506 -0
- package/libs/agent/modules/security/web.js +465 -0
- package/libs/agent/modules/utils/browser.js +492 -0
- package/libs/agent/modules/utils/colorizer.js +285 -0
- package/libs/agent/modules/utils/manager.js +478 -0
- package/libs/cloud/index.js +228 -0
- package/libs/config/db.js +21 -0
- package/libs/config/validator.js +82 -0
- package/libs/db/index.js +318 -0
- package/libs/emailer/imap.js +126 -0
- package/libs/emailer/info.js +41 -0
- package/libs/emailer/smtp.js +77 -0
- package/libs/handler/async.js +3 -0
- package/libs/handler/error.js +66 -0
- package/libs/handler/index.js +161 -0
- package/libs/logger/index.js +49 -0
- package/libs/logger/morgan.js +24 -0
- package/libs/passport/passport.js +109 -0
- package/libs/search/api.js +384 -0
- package/libs/search/features.js +219 -0
- package/libs/search/service.js +64 -0
- package/libs/swagger/config.js +18 -0
- package/libs/swagger/index.js +35 -0
- package/libs/validator/index.js +254 -0
- package/models/blog.js +31 -0
- package/models/brand.js +12 -0
- package/models/cart.js +14 -0
- package/models/category.js +11 -0
- package/models/coupon.js +9 -0
- package/models/customer.js +0 -0
- package/models/enquiry.js +29 -0
- package/models/events.js +13 -0
- package/models/order.js +94 -0
- package/models/product.js +32 -0
- package/models/review.js +14 -0
- package/models/tag.js +10 -0
- package/models/task.js +11 -0
- package/models/user.js +68 -0
- package/package.json +12 -0
- package/routes/agent.js +615 -0
- package/routes/auth.js +13 -0
- package/routes/blog.js +19 -0
- package/routes/brand.js +15 -0
- package/routes/cart.js +105 -0
- package/routes/category.js +16 -0
- package/routes/coupon.js +15 -0
- package/routes/enquiry.js +14 -0
- package/routes/events.js +16 -0
- package/routes/mail.js +170 -0
- package/routes/order.js +19 -0
- package/routes/product.js +22 -0
- package/routes/review.js +11 -0
- package/routes/task.js +12 -0
- package/routes/user.js +17 -0
|
@@ -0,0 +1,1093 @@
|
|
|
1
|
+
// modules/code/jquery-to-react.js - jQuery to React Converter
|
|
2
|
+
const fs = require('fs').promises;
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const colorizer = require('../utils/colorizer');
|
|
5
|
+
|
|
6
|
+
const JQueryToReact = function() {
|
|
7
|
+
this.stateVariables = new Map();
|
|
8
|
+
this.refVariables = new Set();
|
|
9
|
+
this.effects = [];
|
|
10
|
+
this.functions = [];
|
|
11
|
+
this.eventHandlers = new Map();
|
|
12
|
+
this.ajaxCalls = [];
|
|
13
|
+
this.animations = [];
|
|
14
|
+
this.hooks = new Set();
|
|
15
|
+
this.componentName = '';
|
|
16
|
+
this.imports = new Set(['React']);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
JQueryToReact.prototype = {
|
|
20
|
+
|
|
21
|
+
convert(args) {
|
|
22
|
+
const filePath = args[0];
|
|
23
|
+
const outputPath = args[1] || filePath.replace(/\.js$/, '.jsx');
|
|
24
|
+
|
|
25
|
+
if (!filePath) {
|
|
26
|
+
console.log(colorizer.error('Usage: jquery-to-react <jquery-file.js> [output-file.jsx]'));
|
|
27
|
+
console.log(colorizer.info('Examples:'));
|
|
28
|
+
console.log(colorizer.dim(' jquery-to-react script.js'));
|
|
29
|
+
console.log(colorizer.dim(' jquery-to-react old-app.js new-app.jsx\n'));
|
|
30
|
+
return Promise.resolve();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log(colorizer.header('jQuery to React Converter'));
|
|
34
|
+
console.log(colorizer.separator());
|
|
35
|
+
console.log(colorizer.cyan('Input: ') + colorizer.bright(filePath));
|
|
36
|
+
console.log(colorizer.cyan('Output: ') + colorizer.bright(outputPath));
|
|
37
|
+
console.log();
|
|
38
|
+
|
|
39
|
+
return fs.readFile(filePath, 'utf8')
|
|
40
|
+
.then(code => {
|
|
41
|
+
console.log(colorizer.info('Analyzing jQuery code...'));
|
|
42
|
+
|
|
43
|
+
// Reset state
|
|
44
|
+
this.reset();
|
|
45
|
+
|
|
46
|
+
// Set component name from filename
|
|
47
|
+
this.componentName = this.toPascalCase(
|
|
48
|
+
path.basename(filePath, path.extname(filePath))
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Analyze and convert
|
|
52
|
+
const reactCode = this.convertJQueryToReact(code);
|
|
53
|
+
|
|
54
|
+
return fs.writeFile(outputPath, reactCode);
|
|
55
|
+
})
|
|
56
|
+
.then(() => {
|
|
57
|
+
console.log(colorizer.success('\nConversion complete!'));
|
|
58
|
+
console.log(colorizer.cyan('Generated: ') + colorizer.bright(outputPath));
|
|
59
|
+
console.log();
|
|
60
|
+
|
|
61
|
+
this.printConversionSummary();
|
|
62
|
+
})
|
|
63
|
+
.catch(err => {
|
|
64
|
+
console.log(colorizer.error('Conversion failed: ' + err.message));
|
|
65
|
+
if (err.stack) {
|
|
66
|
+
console.log(colorizer.dim(err.stack));
|
|
67
|
+
}
|
|
68
|
+
console.log();
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
reset() {
|
|
73
|
+
this.stateVariables.clear();
|
|
74
|
+
this.refVariables.clear();
|
|
75
|
+
this.effects = [];
|
|
76
|
+
this.functions = [];
|
|
77
|
+
this.eventHandlers.clear();
|
|
78
|
+
this.ajaxCalls = [];
|
|
79
|
+
this.animations = [];
|
|
80
|
+
this.hooks.clear();
|
|
81
|
+
this.imports.clear();
|
|
82
|
+
this.imports.add('React');
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
convertJQueryToReact(jqueryCode) {
|
|
86
|
+
// Clean up the code
|
|
87
|
+
let code = this.cleanCode(jqueryCode);
|
|
88
|
+
|
|
89
|
+
// Extract different patterns
|
|
90
|
+
this.extractVariables(code);
|
|
91
|
+
this.extractDOMReady(code);
|
|
92
|
+
this.extractEventHandlers(code);
|
|
93
|
+
this.extractAjaxCalls(code);
|
|
94
|
+
this.extractAnimations(code);
|
|
95
|
+
this.extractFunctions(code);
|
|
96
|
+
this.extractDOMManipulation(code);
|
|
97
|
+
this.extractSelectors(code);
|
|
98
|
+
|
|
99
|
+
// Generate React component
|
|
100
|
+
return this.generateReactComponent();
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
cleanCode(code) {
|
|
104
|
+
// Remove comments but preserve structure
|
|
105
|
+
code = code.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
106
|
+
code = code.replace(/\/\/.*/g, '');
|
|
107
|
+
|
|
108
|
+
return code;
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
extractVariables(code) {
|
|
112
|
+
// Extract variable declarations
|
|
113
|
+
const varRegex = /(var|let|const)\s+(\w+)\s*=\s*([^;]+);/g;
|
|
114
|
+
let match;
|
|
115
|
+
|
|
116
|
+
while ((match = varRegex.exec(code)) !== null) {
|
|
117
|
+
const [, type, name, value] = match;
|
|
118
|
+
|
|
119
|
+
// Determine if it needs to be state
|
|
120
|
+
if (this.needsState(name, value, code)) {
|
|
121
|
+
this.stateVariables.set(name, {
|
|
122
|
+
initialValue: value.trim(),
|
|
123
|
+
type: 'state'
|
|
124
|
+
});
|
|
125
|
+
this.hooks.add('useState');
|
|
126
|
+
console.log(colorizer.info(' Converting to state: ' + name));
|
|
127
|
+
} else if (this.isConstant(name)) {
|
|
128
|
+
this.stateVariables.set(name, {
|
|
129
|
+
initialValue: value.trim(),
|
|
130
|
+
type: 'const'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
needsState(varName, value, code) {
|
|
137
|
+
// Check if variable is modified after declaration
|
|
138
|
+
const assignmentRegex = new RegExp(`\\b${varName}\\s*=\\s*[^=]`, 'g');
|
|
139
|
+
const matches = code.match(assignmentRegex);
|
|
140
|
+
|
|
141
|
+
// If modified, needs state
|
|
142
|
+
if (matches && matches.length > 0) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check if used in DOM manipulation
|
|
147
|
+
const domManipRegex = new RegExp(`${varName}[\\s\\S]*?\\.(?:html|text|val|attr|prop|css|show|hide)\\s*\\(`, 'g');
|
|
148
|
+
if (domManipRegex.test(code)) {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return false;
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
isConstant(varName) {
|
|
156
|
+
return /^[A-Z_]+$/.test(varName) ||
|
|
157
|
+
varName.startsWith('CONFIG') ||
|
|
158
|
+
varName.startsWith('CONST');
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
extractDOMReady(code) {
|
|
162
|
+
const patterns = [
|
|
163
|
+
/\$\(document\)\.ready\s*\(\s*function\s*\(\)\s*\{([\s\S]*?)\}\s*\)/g,
|
|
164
|
+
/\$\(function\s*\(\)\s*\{([\s\S]*?)\}\s*\)/g,
|
|
165
|
+
/document\.addEventListener\s*\(\s*['"]DOMContentLoaded['"]\s*,\s*function\s*\(\)\s*\{([\s\S]*?)\}\s*\)/g,
|
|
166
|
+
/\$\(document\)\.ready\s*\(\s*\(\)\s*=>\s*\{([\s\S]*?)\}\s*\)/g
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
patterns.forEach(pattern => {
|
|
170
|
+
let match;
|
|
171
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
172
|
+
const initCode = match[1];
|
|
173
|
+
if (initCode && initCode.trim()) {
|
|
174
|
+
this.effects.push({
|
|
175
|
+
type: 'mount',
|
|
176
|
+
code: this.convertCodeBlock(initCode),
|
|
177
|
+
dependencies: '[]',
|
|
178
|
+
comment: 'Component initialization (converted from $(document).ready)'
|
|
179
|
+
});
|
|
180
|
+
this.hooks.add('useEffect');
|
|
181
|
+
console.log(colorizer.info(' Found DOM ready handler'));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
extractEventHandlers(code) {
|
|
188
|
+
// Pattern: $('.selector').on('event', handler)
|
|
189
|
+
const onPattern = /\$\(['"](.*?)['"]\)\.on\s*\(\s*['"](\w+)['"]\s*,\s*(function[^}]*\{[\s\S]*?\}|\([^)]*\)\s*=>\s*\{[\s\S]*?\})\s*\)/g;
|
|
190
|
+
let match;
|
|
191
|
+
|
|
192
|
+
while ((match = onPattern.exec(code)) !== null) {
|
|
193
|
+
const [, selector, event, handler] = match;
|
|
194
|
+
this.addEventHandler(selector, event, handler);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Pattern: $('.selector').click(handler)
|
|
198
|
+
const eventMethods = ['click', 'change', 'submit', 'keyup', 'keydown', 'focus', 'blur', 'input', 'mouseover', 'mouseout'];
|
|
199
|
+
eventMethods.forEach(event => {
|
|
200
|
+
const pattern = new RegExp(`\\$\\(['"](.*?)['"]\\)\\.${event}\\s*\\(\\s*(function[^}]*\\{[\\s\\S]*?\\}|\\([^)]*\\)\\s*=>\\s*\\{[\\s\\S]*?\\})\\s*\\)`, 'g');
|
|
201
|
+
|
|
202
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
203
|
+
const [, selector, handler] = match;
|
|
204
|
+
this.addEventHandler(selector, event, handler);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Pattern: $(selector).on('event', '.child', handler) - event delegation
|
|
209
|
+
const delegationPattern = /\$\(['"](.*?)['"]\)\.on\s*\(\s*['"](\w+)['"]\s*,\s*['"](.*?)['"]\s*,\s*(function[^}]*\{[\s\S]*?\}|\([^)]*\)\s*=>\s*\{[\s\S]*?\})\s*\)/g;
|
|
210
|
+
|
|
211
|
+
while ((match = delegationPattern.exec(code)) !== null) {
|
|
212
|
+
const [, parentSelector, event, childSelector, handler] = match;
|
|
213
|
+
this.addEventHandler(childSelector, event, handler, parentSelector);
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
addEventHandler(selector, event, handler, parentSelector = null) {
|
|
218
|
+
const refName = this.selectorToRefName(selector);
|
|
219
|
+
const handlerName = this.generateHandlerName(selector, event);
|
|
220
|
+
const convertedHandler = this.convertFunctionBody(handler);
|
|
221
|
+
|
|
222
|
+
this.refVariables.add(refName);
|
|
223
|
+
this.hooks.add('useRef');
|
|
224
|
+
|
|
225
|
+
this.eventHandlers.set(handlerName, {
|
|
226
|
+
selector,
|
|
227
|
+
event,
|
|
228
|
+
refName,
|
|
229
|
+
handler: convertedHandler,
|
|
230
|
+
parentSelector
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Add effect to attach event listener
|
|
234
|
+
this.effects.push({
|
|
235
|
+
type: 'event',
|
|
236
|
+
code: this.generateEventEffect(refName, event, handlerName),
|
|
237
|
+
dependencies: `[${this.extractHandlerDependencies(convertedHandler).join(', ')}]`,
|
|
238
|
+
comment: `Event listener for ${event} on ${selector}`
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
this.hooks.add('useEffect');
|
|
242
|
+
console.log(colorizer.info(` Found event handler: ${selector}.${event}()`));
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
extractAjaxCalls(code) {
|
|
246
|
+
// $.ajax() calls
|
|
247
|
+
const ajaxPattern = /\$\.ajax\s*\(\s*\{([\s\S]*?)\}\s*\)/g;
|
|
248
|
+
let match;
|
|
249
|
+
|
|
250
|
+
while ((match = ajaxPattern.exec(code)) !== null) {
|
|
251
|
+
const config = this.parseAjaxConfig(match[1]);
|
|
252
|
+
this.ajaxCalls.push({
|
|
253
|
+
type: 'ajax',
|
|
254
|
+
config,
|
|
255
|
+
converted: this.convertAjaxToFetch(config)
|
|
256
|
+
});
|
|
257
|
+
console.log(colorizer.info(' Found $.ajax() call'));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// $.get() calls
|
|
261
|
+
const getPattern = /\$\.get\s*\(\s*['"](.*?)['"]\s*,?\s*(function[^}]*\{[\s\S]*?\}|\([^)]*\)\s*=>\s*\{[\s\S]*?\})?\s*\)/g;
|
|
262
|
+
|
|
263
|
+
while ((match = getPattern.exec(code)) !== null) {
|
|
264
|
+
const [, url, callback] = match;
|
|
265
|
+
this.ajaxCalls.push({
|
|
266
|
+
type: 'get',
|
|
267
|
+
url,
|
|
268
|
+
callback: callback ? this.convertFunctionBody(callback) : null,
|
|
269
|
+
converted: this.convertGetToFetch(url, callback)
|
|
270
|
+
});
|
|
271
|
+
console.log(colorizer.info(' Found $.get() call'));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// $.post() calls
|
|
275
|
+
const postPattern = /\$\.post\s*\(\s*['"](.*?)['"]\s*,\s*([^,]+)\s*,?\s*(function[^}]*\{[\s\S]*?\}|\([^)]*\)\s*=>\s*\{[\s\S]*?\})?\s*\)/g;
|
|
276
|
+
|
|
277
|
+
while ((match = postPattern.exec(code)) !== null) {
|
|
278
|
+
const [, url, data, callback] = match;
|
|
279
|
+
this.ajaxCalls.push({
|
|
280
|
+
type: 'post',
|
|
281
|
+
url,
|
|
282
|
+
data,
|
|
283
|
+
callback: callback ? this.convertFunctionBody(callback) : null,
|
|
284
|
+
converted: this.convertPostToFetch(url, data, callback)
|
|
285
|
+
});
|
|
286
|
+
console.log(colorizer.info(' Found $.post() call'));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (this.ajaxCalls.length > 0) {
|
|
290
|
+
this.hooks.add('useEffect');
|
|
291
|
+
this.hooks.add('useState');
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
parseAjaxConfig(configStr) {
|
|
296
|
+
const config = {};
|
|
297
|
+
|
|
298
|
+
// Extract URL
|
|
299
|
+
const urlMatch = configStr.match(/url\s*:\s*['"](.*?)['"]/);
|
|
300
|
+
if (urlMatch) config.url = urlMatch[1];
|
|
301
|
+
|
|
302
|
+
// Extract method
|
|
303
|
+
const methodMatch = configStr.match(/(?:method|type)\s*:\s*['"](.*?)['"]/i);
|
|
304
|
+
if (methodMatch) config.method = methodMatch[1].toUpperCase();
|
|
305
|
+
|
|
306
|
+
// Extract data
|
|
307
|
+
const dataMatch = configStr.match(/data\s*:\s*([^,}]+)/);
|
|
308
|
+
if (dataMatch) config.data = dataMatch[1].trim();
|
|
309
|
+
|
|
310
|
+
// Extract success callback
|
|
311
|
+
const successMatch = configStr.match(/success\s*:\s*(function[^}]*\{[\s\S]*?\}|\([^)]*\)\s*=>\s*\{[\s\S]*?\})/);
|
|
312
|
+
if (successMatch) config.success = this.convertFunctionBody(successMatch[1]);
|
|
313
|
+
|
|
314
|
+
// Extract error callback
|
|
315
|
+
const errorMatch = configStr.match(/error\s*:\s*(function[^}]*\{[\s\S]*?\}|\([^)]*\)\s*=>\s*\{[\s\S]*?\})/);
|
|
316
|
+
if (errorMatch) config.error = this.convertFunctionBody(errorMatch[1]);
|
|
317
|
+
|
|
318
|
+
return config;
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
convertAjaxToFetch(config) {
|
|
322
|
+
let code = `fetch('${config.url || 'URL_HERE'}', {\n`;
|
|
323
|
+
code += ` method: '${config.method || 'GET'}',\n`;
|
|
324
|
+
|
|
325
|
+
if (config.data) {
|
|
326
|
+
code += ` headers: { 'Content-Type': 'application/json' },\n`;
|
|
327
|
+
code += ` body: JSON.stringify(${config.data})\n`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
code += `})\n`;
|
|
331
|
+
code += ` .then(response => response.json())\n`;
|
|
332
|
+
|
|
333
|
+
if (config.success) {
|
|
334
|
+
code += ` .then(data => {\n ${config.success}\n })\n`;
|
|
335
|
+
} else {
|
|
336
|
+
code += ` .then(data => {\n // Handle success\n console.log(data);\n })\n`;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (config.error) {
|
|
340
|
+
code += ` .catch(error => {\n ${config.error}\n });\n`;
|
|
341
|
+
} else {
|
|
342
|
+
code += ` .catch(error => {\n console.error('Error:', error);\n });\n`;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return code;
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
convertGetToFetch(url, callback) {
|
|
349
|
+
let code = `fetch('${url}')\n`;
|
|
350
|
+
code += ` .then(response => response.json())\n`;
|
|
351
|
+
|
|
352
|
+
if (callback) {
|
|
353
|
+
code += ` .then(data => {\n ${callback}\n })\n`;
|
|
354
|
+
} else {
|
|
355
|
+
code += ` .then(data => {\n // Handle response\n console.log(data);\n })\n`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
code += ` .catch(error => console.error('Error:', error));\n`;
|
|
359
|
+
|
|
360
|
+
return code;
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
convertPostToFetch(url, data, callback) {
|
|
364
|
+
let code = `fetch('${url}', {\n`;
|
|
365
|
+
code += ` method: 'POST',\n`;
|
|
366
|
+
code += ` headers: { 'Content-Type': 'application/json' },\n`;
|
|
367
|
+
code += ` body: JSON.stringify(${data})\n`;
|
|
368
|
+
code += `})\n`;
|
|
369
|
+
code += ` .then(response => response.json())\n`;
|
|
370
|
+
|
|
371
|
+
if (callback) {
|
|
372
|
+
code += ` .then(data => {\n ${callback}\n })\n`;
|
|
373
|
+
} else {
|
|
374
|
+
code += ` .then(data => {\n // Handle response\n console.log(data);\n })\n`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
code += ` .catch(error => console.error('Error:', error));\n`;
|
|
378
|
+
|
|
379
|
+
return code;
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
extractAnimations(code) {
|
|
383
|
+
const animMethods = ['show', 'hide', 'toggle', 'fadeIn', 'fadeOut', 'fadeToggle', 'slideUp', 'slideDown', 'slideToggle', 'animate'];
|
|
384
|
+
|
|
385
|
+
animMethods.forEach(method => {
|
|
386
|
+
const pattern = new RegExp(`\\$\\(['"](.*?)['"]\\)\\.${method}\\s*\\(([^)]*)\\)`, 'g');
|
|
387
|
+
let match;
|
|
388
|
+
|
|
389
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
390
|
+
const [, selector, args] = match;
|
|
391
|
+
this.animations.push({
|
|
392
|
+
selector,
|
|
393
|
+
method,
|
|
394
|
+
args: args.trim(),
|
|
395
|
+
suggestion: this.getAnimationSuggestion(method)
|
|
396
|
+
});
|
|
397
|
+
console.log(colorizer.warning(` Found animation: ${selector}.${method}() - requires CSS/library`));
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
},
|
|
401
|
+
|
|
402
|
+
getAnimationSuggestion(method) {
|
|
403
|
+
const suggestions = {
|
|
404
|
+
'show': 'Use CSS display or visibility with state',
|
|
405
|
+
'hide': 'Use CSS display or visibility with state',
|
|
406
|
+
'toggle': 'Toggle visibility state with conditional rendering',
|
|
407
|
+
'fadeIn': 'Use CSS transition with opacity',
|
|
408
|
+
'fadeOut': 'Use CSS transition with opacity',
|
|
409
|
+
'fadeToggle': 'Toggle opacity state with CSS transition',
|
|
410
|
+
'slideUp': 'Use CSS transition with max-height',
|
|
411
|
+
'slideDown': 'Use CSS transition with max-height',
|
|
412
|
+
'slideToggle': 'Toggle height state with CSS transition',
|
|
413
|
+
'animate': 'Use CSS animations or libraries like Framer Motion, React Spring'
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
return suggestions[method] || 'Convert to CSS animations or use animation library';
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
extractFunctions(code) {
|
|
420
|
+
// Regular function declarations
|
|
421
|
+
const funcPattern = /function\s+(\w+)\s*\(([^)]*)\)\s*\{([\s\S]*?)\n\}/g;
|
|
422
|
+
let match;
|
|
423
|
+
|
|
424
|
+
while ((match = funcPattern.exec(code)) !== null) {
|
|
425
|
+
const [, name, params, body] = match;
|
|
426
|
+
|
|
427
|
+
// Skip if it's an event handler (already extracted)
|
|
428
|
+
if (!Array.from(this.eventHandlers.keys()).some(h => h.includes(name))) {
|
|
429
|
+
this.functions.push({
|
|
430
|
+
name,
|
|
431
|
+
params: params.trim(),
|
|
432
|
+
body: this.convertFunctionBody(body),
|
|
433
|
+
isAsync: this.containsAjax(body)
|
|
434
|
+
});
|
|
435
|
+
console.log(colorizer.info(` Found function: ${name}()`));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Arrow function assignments
|
|
440
|
+
const arrowPattern = /(?:const|let|var)\s+(\w+)\s*=\s*\(([^)]*)\)\s*=>\s*\{([\s\S]*?)\n\}/g;
|
|
441
|
+
|
|
442
|
+
while ((match = arrowPattern.exec(code)) !== null) {
|
|
443
|
+
const [, name, params, body] = match;
|
|
444
|
+
|
|
445
|
+
if (!Array.from(this.eventHandlers.keys()).some(h => h.includes(name))) {
|
|
446
|
+
this.functions.push({
|
|
447
|
+
name,
|
|
448
|
+
params: params.trim(),
|
|
449
|
+
body: this.convertFunctionBody(body),
|
|
450
|
+
isAsync: this.containsAjax(body),
|
|
451
|
+
isArrow: true
|
|
452
|
+
});
|
|
453
|
+
console.log(colorizer.info(` Found arrow function: ${name}()`));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
|
|
458
|
+
containsAjax(code) {
|
|
459
|
+
return /\$\.(ajax|get|post)|fetch\(/.test(code);
|
|
460
|
+
},
|
|
461
|
+
|
|
462
|
+
extractDOMManipulation(code) {
|
|
463
|
+
// .html()
|
|
464
|
+
const htmlPattern = /\$\(['"](.*?)['"]\)\.html\s*\(\s*([^)]+)\s*\)/g;
|
|
465
|
+
let match;
|
|
466
|
+
|
|
467
|
+
while ((match = htmlPattern.exec(code)) !== null) {
|
|
468
|
+
const [, selector, content] = match;
|
|
469
|
+
const varName = this.selectorToStateName(selector);
|
|
470
|
+
|
|
471
|
+
this.stateVariables.set(varName, {
|
|
472
|
+
initialValue: "''",
|
|
473
|
+
type: 'state',
|
|
474
|
+
purpose: 'DOM content'
|
|
475
|
+
});
|
|
476
|
+
this.hooks.add('useState');
|
|
477
|
+
console.log(colorizer.info(` Converting .html() to state: ${varName}`));
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// .text()
|
|
481
|
+
const textPattern = /\$\(['"](.*?)['"]\)\.text\s*\(\s*([^)]+)\s*\)/g;
|
|
482
|
+
|
|
483
|
+
while ((match = textPattern.exec(code)) !== null) {
|
|
484
|
+
const [, selector, content] = match;
|
|
485
|
+
const varName = this.selectorToStateName(selector);
|
|
486
|
+
|
|
487
|
+
this.stateVariables.set(varName, {
|
|
488
|
+
initialValue: "''",
|
|
489
|
+
type: 'state',
|
|
490
|
+
purpose: 'Text content'
|
|
491
|
+
});
|
|
492
|
+
this.hooks.add('useState');
|
|
493
|
+
console.log(colorizer.info(` Converting .text() to state: ${varName}`));
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// .val()
|
|
497
|
+
const valPattern = /\$\(['"](.*?)['"]\)\.val\s*\(\s*([^)]*)\s*\)/g;
|
|
498
|
+
|
|
499
|
+
while ((match = valPattern.exec(code)) !== null) {
|
|
500
|
+
const [, selector, value] = match;
|
|
501
|
+
const varName = this.selectorToStateName(selector);
|
|
502
|
+
|
|
503
|
+
this.stateVariables.set(varName, {
|
|
504
|
+
initialValue: "''",
|
|
505
|
+
type: 'state',
|
|
506
|
+
purpose: 'Form input value'
|
|
507
|
+
});
|
|
508
|
+
this.hooks.add('useState');
|
|
509
|
+
console.log(colorizer.info(` Converting .val() to controlled input: ${varName}`));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// .show() / .hide()
|
|
513
|
+
const visibilityPattern = /\$\(['"](.*?)['"]\)\.(show|hide)\s*\(/g;
|
|
514
|
+
|
|
515
|
+
while ((match = visibilityPattern.exec(code)) !== null) {
|
|
516
|
+
const [, selector, method] = match;
|
|
517
|
+
const varName = `${this.selectorToStateName(selector)}Visible`;
|
|
518
|
+
|
|
519
|
+
this.stateVariables.set(varName, {
|
|
520
|
+
initialValue: method === 'show' ? 'true' : 'false',
|
|
521
|
+
type: 'state',
|
|
522
|
+
purpose: 'Visibility toggle'
|
|
523
|
+
});
|
|
524
|
+
this.hooks.add('useState');
|
|
525
|
+
console.log(colorizer.info(` Converting .${method}() to state: ${varName}`));
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// .addClass() / .removeClass() / .toggleClass()
|
|
529
|
+
const classPattern = /\$\(['"](.*?)['"]\)\.(addClass|removeClass|toggleClass)\s*\(\s*['"](.*?)['"]\s*\)/g;
|
|
530
|
+
|
|
531
|
+
while ((match = classPattern.exec(code)) !== null) {
|
|
532
|
+
const [, selector, method, className] = match;
|
|
533
|
+
const varName = `${this.selectorToStateName(selector)}Class`;
|
|
534
|
+
|
|
535
|
+
this.stateVariables.set(varName, {
|
|
536
|
+
initialValue: "''",
|
|
537
|
+
type: 'state',
|
|
538
|
+
purpose: `CSS class management for ${className}`
|
|
539
|
+
});
|
|
540
|
+
this.hooks.add('useState');
|
|
541
|
+
console.log(colorizer.info(` Converting .${method}() to className state: ${varName}`));
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
|
|
545
|
+
extractSelectors(code) {
|
|
546
|
+
// Extract all jQuery selectors that aren't already handled
|
|
547
|
+
const selectorPattern = /\$\(['"]((?:#|\.)?[\w-]+)['"]\)/g;
|
|
548
|
+
let match;
|
|
549
|
+
|
|
550
|
+
const seenSelectors = new Set();
|
|
551
|
+
|
|
552
|
+
while ((match = selectorPattern.exec(code)) !== null) {
|
|
553
|
+
const selector = match[1];
|
|
554
|
+
|
|
555
|
+
if (!seenSelectors.has(selector) && selector.startsWith('#')) {
|
|
556
|
+
const refName = this.selectorToRefName(selector);
|
|
557
|
+
this.refVariables.add(refName);
|
|
558
|
+
this.hooks.add('useRef');
|
|
559
|
+
seenSelectors.add(selector);
|
|
560
|
+
console.log(colorizer.info(` Creating ref for selector: ${selector}`));
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
|
|
565
|
+
convertCodeBlock(code) {
|
|
566
|
+
let converted = code;
|
|
567
|
+
|
|
568
|
+
// Convert jQuery selectors to refs
|
|
569
|
+
converted = converted.replace(
|
|
570
|
+
/\$\(['"]#([\w-]+)['"]\)/g,
|
|
571
|
+
(match, id) => {
|
|
572
|
+
const refName = this.toCamelCase(id);
|
|
573
|
+
return `${refName}Ref.current`;
|
|
574
|
+
}
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
// Convert class selectors
|
|
578
|
+
converted = converted.replace(
|
|
579
|
+
/\$\(['"]\.([\w-]+)['"]\)/g,
|
|
580
|
+
(match, className) => {
|
|
581
|
+
return `// TODO: Use ref or state for .${className}`;
|
|
582
|
+
}
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
// Convert .html()
|
|
586
|
+
converted = converted.replace(
|
|
587
|
+
/\.html\s*\(\s*([^)]+)\s*\)/g,
|
|
588
|
+
(match, content) => {
|
|
589
|
+
return `// TODO: Use state or dangerouslySetInnerHTML`;
|
|
590
|
+
}
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
// Convert .text()
|
|
594
|
+
converted = converted.replace(
|
|
595
|
+
/\.text\s*\(\s*([^)]+)\s*\)/g,
|
|
596
|
+
(match, content) => {
|
|
597
|
+
return `// TODO: Use state to set text content`;
|
|
598
|
+
}
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
// Convert .val()
|
|
602
|
+
converted = converted.replace(
|
|
603
|
+
/\.val\s*\(\s*([^)]*)\s*\)/g,
|
|
604
|
+
(match, value) => {
|
|
605
|
+
if (value) {
|
|
606
|
+
return `// TODO: Use setState to update input value`;
|
|
607
|
+
} else {
|
|
608
|
+
return `.value // TODO: Use state instead`;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
// Convert .show() / .hide()
|
|
614
|
+
converted = converted.replace(
|
|
615
|
+
/\.(show|hide)\s*\(\)/g,
|
|
616
|
+
'// TODO: Use state to toggle visibility'
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
// Convert .addClass() / .removeClass()
|
|
620
|
+
converted = converted.replace(
|
|
621
|
+
/\.(addClass|removeClass|toggleClass)\s*\([^)]+\)/g,
|
|
622
|
+
'// TODO: Use className state'
|
|
623
|
+
);
|
|
624
|
+
|
|
625
|
+
// Convert .css()
|
|
626
|
+
converted = converted.replace(
|
|
627
|
+
/\.css\s*\([^)]+\)/g,
|
|
628
|
+
'// TODO: Use inline style or CSS classes'
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
// Convert this to proper context
|
|
632
|
+
converted = converted.replace(/\bthis\./g, '');
|
|
633
|
+
|
|
634
|
+
return converted.trim();
|
|
635
|
+
},
|
|
636
|
+
|
|
637
|
+
convertFunctionBody(funcStr) {
|
|
638
|
+
// Extract function body
|
|
639
|
+
const bodyMatch = funcStr.match(/\{([\s\S]*)\}/);
|
|
640
|
+
if (!bodyMatch) return funcStr;
|
|
641
|
+
|
|
642
|
+
return this.convertCodeBlock(bodyMatch[1]);
|
|
643
|
+
},
|
|
644
|
+
|
|
645
|
+
generateEventEffect(refName, event, handlerName) {
|
|
646
|
+
let code = `const element = ${refName}Ref.current;\n`;
|
|
647
|
+
code += `if (element) {\n`;
|
|
648
|
+
code += ` element.addEventListener('${event}', ${handlerName});\n`;
|
|
649
|
+
code += ` return () => element.removeEventListener('${event}', ${handlerName});\n`;
|
|
650
|
+
code += `}`;
|
|
651
|
+
|
|
652
|
+
return code;
|
|
653
|
+
},
|
|
654
|
+
|
|
655
|
+
extractHandlerDependencies(handlerCode) {
|
|
656
|
+
const deps = [];
|
|
657
|
+
|
|
658
|
+
// Find state variables used in handler
|
|
659
|
+
this.stateVariables.forEach((value, key) => {
|
|
660
|
+
if (value.type === 'state' && handlerCode.includes(key)) {
|
|
661
|
+
deps.push(key);
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
return deps;
|
|
666
|
+
},
|
|
667
|
+
|
|
668
|
+
generateHandlerName(selector, event) {
|
|
669
|
+
const selectorName = this.selectorToRefName(selector);
|
|
670
|
+
const eventName = this.toPascalCase(event);
|
|
671
|
+
return `handle${selectorName}${eventName}`;
|
|
672
|
+
},
|
|
673
|
+
|
|
674
|
+
selectorToRefName(selector) {
|
|
675
|
+
return selector
|
|
676
|
+
.replace(/^[#.]/, '')
|
|
677
|
+
.replace(/[^a-zA-Z0-9]/g, '_')
|
|
678
|
+
.replace(/_+/g, '_')
|
|
679
|
+
.replace(/^_|_$/g, '')
|
|
680
|
+
.replace(/^(.)/, (_, c) => c.toLowerCase());
|
|
681
|
+
},
|
|
682
|
+
|
|
683
|
+
selectorToStateName(selector) {
|
|
684
|
+
return this.toCamelCase(
|
|
685
|
+
selector
|
|
686
|
+
.replace(/^[#.]/, '')
|
|
687
|
+
.replace(/[^a-zA-Z0-9]/g, '_')
|
|
688
|
+
);
|
|
689
|
+
},
|
|
690
|
+
|
|
691
|
+
generateReactComponent() {
|
|
692
|
+
let code = '';
|
|
693
|
+
|
|
694
|
+
// Imports
|
|
695
|
+
const hooks = Array.from(this.hooks).filter(h => h !== 'React');
|
|
696
|
+
if (hooks.length > 0) {
|
|
697
|
+
code += `import React, { ${hooks.join(', ')} } from 'react';\n`;
|
|
698
|
+
} else {
|
|
699
|
+
code += `import React from 'react';\n`;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
code += '\n';
|
|
703
|
+
|
|
704
|
+
// Component
|
|
705
|
+
code += `function ${this.componentName}() {\n`;
|
|
706
|
+
|
|
707
|
+
// State variables
|
|
708
|
+
if (this.stateVariables.size > 0) {
|
|
709
|
+
code += ' // State\n';
|
|
710
|
+
this.stateVariables.forEach((value, key) => {
|
|
711
|
+
if (value.type === 'state') {
|
|
712
|
+
const setterName = `set${this.toPascalCase(key)}`;
|
|
713
|
+
code += ` const [${key}, ${setterName}] = useState(${value.initialValue});`;
|
|
714
|
+
if (value.purpose) {
|
|
715
|
+
code += ` // ${value.purpose}`;
|
|
716
|
+
}
|
|
717
|
+
code += '\n';
|
|
718
|
+
} else if (value.type === 'const') {
|
|
719
|
+
code += ` const ${key} = ${value.initialValue};\n`;
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
code += '\n';
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Refs
|
|
726
|
+
if (this.refVariables.size > 0) {
|
|
727
|
+
code += ' // Refs\n';
|
|
728
|
+
this.refVariables.forEach(refName => {
|
|
729
|
+
code += ` const ${refName}Ref = useRef(null);\n`;
|
|
730
|
+
});
|
|
731
|
+
code += '\n';
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Event handlers
|
|
735
|
+
if (this.eventHandlers.size > 0) {
|
|
736
|
+
code += ' // Event Handlers\n';
|
|
737
|
+
this.eventHandlers.forEach((handler, name) => {
|
|
738
|
+
code += ` const ${name} = (e) => {\n`;
|
|
739
|
+
code += ` ${handler.handler.split('\n').join('\n ')}\n`;
|
|
740
|
+
code += ` };\n\n`;
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Functions
|
|
745
|
+
if (this.functions.length > 0) {
|
|
746
|
+
code += ' // Functions\n';
|
|
747
|
+
this.functions.forEach(func => {
|
|
748
|
+
const asyncKeyword = func.isAsync ? 'async ' : '';
|
|
749
|
+
code += ` const ${func.name} = ${asyncKeyword}(${func.params}) => {\n`;
|
|
750
|
+
code += ` ${func.body.split('\n').join('\n ')}\n`;
|
|
751
|
+
code += ` };\n\n`;
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Effects
|
|
756
|
+
if (this.effects.length > 0) {
|
|
757
|
+
code += ' // Effects\n';
|
|
758
|
+
this.effects.forEach(effect => {
|
|
759
|
+
if (effect.comment) {
|
|
760
|
+
code += ` // ${effect.comment}\n`;
|
|
761
|
+
}
|
|
762
|
+
code += ` useEffect(() => {\n`;
|
|
763
|
+
code += ` ${effect.code.split('\n').join('\n ')}\n`;
|
|
764
|
+
code += ` }, ${effect.dependencies});\n\n`;
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// AJAX calls in useEffect (if not already in effects)
|
|
769
|
+
if (this.ajaxCalls.length > 0) {
|
|
770
|
+
const standaloneAjax = this.ajaxCalls.filter(ajax =>
|
|
771
|
+
!this.effects.some(e => e.code.includes('fetch'))
|
|
772
|
+
);
|
|
773
|
+
|
|
774
|
+
if (standaloneAjax.length > 0) {
|
|
775
|
+
code += ' // Data Fetching\n';
|
|
776
|
+
code += ' useEffect(() => {\n';
|
|
777
|
+
standaloneAjax.forEach(ajax => {
|
|
778
|
+
code += ` // Converted from ${ajax.type}\n`;
|
|
779
|
+
code += ` ${ajax.converted.split('\n').join('\n ')}\n`;
|
|
780
|
+
});
|
|
781
|
+
code += ' }, []);\n\n';
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Return JSX
|
|
786
|
+
code += ' return (\n';
|
|
787
|
+
code += ' <div className="' + this.componentName.toLowerCase() + '">\n';
|
|
788
|
+
code += ' {/* TODO: Add your JSX here */}\n';
|
|
789
|
+
|
|
790
|
+
// Add ref examples
|
|
791
|
+
if (this.refVariables.size > 0) {
|
|
792
|
+
code += ' {/* Example refs: */}\n';
|
|
793
|
+
this.refVariables.forEach(refName => {
|
|
794
|
+
code += ` {/* <div ref={${refName}Ref}>...</div> */}\n`;
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Add conditional rendering examples
|
|
799
|
+
const visibilityStates = Array.from(this.stateVariables.entries())
|
|
800
|
+
.filter(([key, val]) => val.purpose && val.purpose.includes('Visibility'));
|
|
801
|
+
|
|
802
|
+
if (visibilityStates.length > 0) {
|
|
803
|
+
code += ' {/* Conditional rendering: */}\n';
|
|
804
|
+
visibilityStates.forEach(([key]) => {
|
|
805
|
+
code += ` {/* {${key} && <div>Visible content</div>} */}\n`;
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// Add controlled input examples
|
|
810
|
+
const inputStates = Array.from(this.stateVariables.entries())
|
|
811
|
+
.filter(([key, val]) => val.purpose && val.purpose.includes('input'));
|
|
812
|
+
|
|
813
|
+
if (inputStates.length > 0) {
|
|
814
|
+
code += ' {/* Controlled inputs: */}\n';
|
|
815
|
+
inputStates.forEach(([key]) => {
|
|
816
|
+
const setterName = `set${this.toPascalCase(key)}`;
|
|
817
|
+
code += ` {/* <input value={${key}} onChange={(e) => ${setterName}(e.target.value)} /> */}\n`;
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
code += ' </div>\n';
|
|
822
|
+
code += ' );\n';
|
|
823
|
+
code += '}\n\n';
|
|
824
|
+
code += `export default ${this.componentName};\n`;
|
|
825
|
+
|
|
826
|
+
// Add conversion notes
|
|
827
|
+
code += '\n/* CONVERSION NOTES:\n';
|
|
828
|
+
code += ' * \n';
|
|
829
|
+
code += ' * 1. All jQuery selectors have been converted to refs or state\n';
|
|
830
|
+
code += ' * 2. Event handlers are attached using useEffect with cleanup\n';
|
|
831
|
+
code += ' * 3. DOM manipulations should use state instead\n';
|
|
832
|
+
code += ' * 4. AJAX calls converted to fetch API\n';
|
|
833
|
+
|
|
834
|
+
if (this.animations.length > 0) {
|
|
835
|
+
code += ' * \n';
|
|
836
|
+
code += ' * ANIMATIONS DETECTED:\n';
|
|
837
|
+
this.animations.forEach(anim => {
|
|
838
|
+
code += ` * - ${anim.selector}.${anim.method}() => ${anim.suggestion}\n`;
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
code += ' * \n';
|
|
843
|
+
code += ' * TODO Items:\n';
|
|
844
|
+
code += ' * - Review all TODO comments in the code\n';
|
|
845
|
+
code += ' * - Add proper JSX structure\n';
|
|
846
|
+
code += ' * - Test event handlers\n';
|
|
847
|
+
code += ' * - Verify state updates work correctly\n';
|
|
848
|
+
code += ' * - Add error handling for async operations\n';
|
|
849
|
+
|
|
850
|
+
if (this.animations.length > 0) {
|
|
851
|
+
code += ' * - Implement animations with CSS or animation library\n';
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
code += ' */\n';
|
|
855
|
+
|
|
856
|
+
return code;
|
|
857
|
+
},
|
|
858
|
+
|
|
859
|
+
printConversionSummary() {
|
|
860
|
+
console.log(colorizer.section('Conversion Summary'));
|
|
861
|
+
console.log(colorizer.cyan(' Component Name: ') + colorizer.bright(this.componentName));
|
|
862
|
+
console.log(colorizer.cyan(' State Variables: ') + this.stateVariables.size);
|
|
863
|
+
console.log(colorizer.cyan(' Refs: ') + this.refVariables.size);
|
|
864
|
+
console.log(colorizer.cyan(' Event Handlers: ') + this.eventHandlers.size);
|
|
865
|
+
console.log(colorizer.cyan(' Functions: ') + this.functions.length);
|
|
866
|
+
console.log(colorizer.cyan(' Effects: ') + this.effects.length);
|
|
867
|
+
console.log(colorizer.cyan(' AJAX Calls: ') + this.ajaxCalls.length);
|
|
868
|
+
console.log(colorizer.cyan(' Animations: ') + this.animations.length);
|
|
869
|
+
console.log(colorizer.cyan(' Hooks Used: ') + Array.from(this.hooks).join(', '));
|
|
870
|
+
|
|
871
|
+
if (this.animations.length > 0) {
|
|
872
|
+
console.log();
|
|
873
|
+
console.log(colorizer.warning('⚠ Animations detected - will need CSS or animation library'));
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
console.log();
|
|
877
|
+
console.log(colorizer.magenta('Next Steps:'));
|
|
878
|
+
console.log(colorizer.bullet('Review the generated component'));
|
|
879
|
+
console.log(colorizer.bullet('Add JSX structure'));
|
|
880
|
+
console.log(colorizer.bullet('Test functionality'));
|
|
881
|
+
console.log(colorizer.bullet('Address all TODO comments'));
|
|
882
|
+
console.log();
|
|
883
|
+
},
|
|
884
|
+
|
|
885
|
+
analyzeBatch(args) {
|
|
886
|
+
const inputDir = args[0] || '.';
|
|
887
|
+
|
|
888
|
+
console.log(colorizer.header('jQuery Files Analysis'));
|
|
889
|
+
console.log(colorizer.separator());
|
|
890
|
+
console.log(colorizer.cyan('Directory: ') + colorizer.bright(inputDir));
|
|
891
|
+
console.log();
|
|
892
|
+
|
|
893
|
+
return this.findJQueryFiles(inputDir)
|
|
894
|
+
.then(files => {
|
|
895
|
+
if (files.length === 0) {
|
|
896
|
+
console.log(colorizer.warning('No jQuery files found\n'));
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
console.log(colorizer.info('Found ' + files.length + ' file(s) with jQuery\n'));
|
|
901
|
+
|
|
902
|
+
const analyses = [];
|
|
903
|
+
|
|
904
|
+
return Promise.all(files.map(file => {
|
|
905
|
+
return fs.readFile(file, 'utf8')
|
|
906
|
+
.then(code => {
|
|
907
|
+
const analysis = this.analyzeJQueryFile(code, file);
|
|
908
|
+
analyses.push({ file, analysis });
|
|
909
|
+
});
|
|
910
|
+
}))
|
|
911
|
+
.then(() => {
|
|
912
|
+
this.printBatchAnalysis(analyses);
|
|
913
|
+
});
|
|
914
|
+
})
|
|
915
|
+
.catch(err => {
|
|
916
|
+
console.log(colorizer.error('Analysis failed: ' + err.message + '\n'));
|
|
917
|
+
});
|
|
918
|
+
},
|
|
919
|
+
|
|
920
|
+
findJQueryFiles(dir, files = []) {
|
|
921
|
+
return fs.readdir(dir, { withFileTypes: true })
|
|
922
|
+
.then(items => {
|
|
923
|
+
const promises = items.map(item => {
|
|
924
|
+
const fullPath = path.join(dir, item.name);
|
|
925
|
+
|
|
926
|
+
if (['node_modules', '.git', 'dist', 'build'].includes(item.name)) {
|
|
927
|
+
return Promise.resolve();
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
if (item.isDirectory()) {
|
|
931
|
+
return this.findJQueryFiles(fullPath, files);
|
|
932
|
+
} else if (item.isFile() && path.extname(item.name).toLowerCase() === '.js') {
|
|
933
|
+
return fs.readFile(fullPath, 'utf8')
|
|
934
|
+
.then(content => {
|
|
935
|
+
if (this.containsJQuery(content)) {
|
|
936
|
+
files.push(fullPath);
|
|
937
|
+
}
|
|
938
|
+
})
|
|
939
|
+
.catch(() => {});
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
return Promise.resolve();
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
return Promise.all(promises);
|
|
946
|
+
})
|
|
947
|
+
.then(() => files)
|
|
948
|
+
.catch(() => files);
|
|
949
|
+
},
|
|
950
|
+
|
|
951
|
+
containsJQuery(code) {
|
|
952
|
+
return /\$\(|\$\./g.test(code) || code.includes('jQuery');
|
|
953
|
+
},
|
|
954
|
+
|
|
955
|
+
analyzeJQueryFile(code, filePath) {
|
|
956
|
+
const analysis = {
|
|
957
|
+
selectors: this.countMatches(code, /\$\(['"]/g),
|
|
958
|
+
eventHandlers: this.countMatches(code, /\.(on|click|change|submit|keyup|keydown|focus|blur)\s*\(/g),
|
|
959
|
+
domManipulation: this.countMatches(code, /\.(html|text|val|append|prepend|remove)\s*\(/g),
|
|
960
|
+
ajax: this.countMatches(code, /\$\.(ajax|get|post)/g),
|
|
961
|
+
animations: this.countMatches(code, /\.(show|hide|toggle|fade|slide|animate)\s*\(/g),
|
|
962
|
+
cssManipulation: this.countMatches(code, /\.(css|addClass|removeClass|toggleClass)\s*\(/g),
|
|
963
|
+
complexity: 0
|
|
964
|
+
};
|
|
965
|
+
|
|
966
|
+
// Calculate complexity score
|
|
967
|
+
analysis.complexity =
|
|
968
|
+
analysis.selectors * 1 +
|
|
969
|
+
analysis.eventHandlers * 2 +
|
|
970
|
+
analysis.domManipulation * 2 +
|
|
971
|
+
analysis.ajax * 3 +
|
|
972
|
+
analysis.animations * 2 +
|
|
973
|
+
analysis.cssManipulation * 1;
|
|
974
|
+
|
|
975
|
+
return analysis;
|
|
976
|
+
},
|
|
977
|
+
|
|
978
|
+
countMatches(code, regex) {
|
|
979
|
+
const matches = code.match(regex);
|
|
980
|
+
return matches ? matches.length : 0;
|
|
981
|
+
},
|
|
982
|
+
|
|
983
|
+
printBatchAnalysis(analyses) {
|
|
984
|
+
console.log(colorizer.section('Batch Analysis Results'));
|
|
985
|
+
console.log();
|
|
986
|
+
|
|
987
|
+
analyses.forEach(({ file, analysis }) => {
|
|
988
|
+
const fileName = path.basename(file);
|
|
989
|
+
const complexityLevel =
|
|
990
|
+
analysis.complexity > 20 ? colorizer.red('High') :
|
|
991
|
+
analysis.complexity > 10 ? colorizer.yellow('Medium') :
|
|
992
|
+
colorizer.green('Low');
|
|
993
|
+
|
|
994
|
+
console.log(colorizer.bright(fileName));
|
|
995
|
+
console.log(colorizer.cyan(' Selectors: ') + analysis.selectors);
|
|
996
|
+
console.log(colorizer.cyan(' Event Handlers: ') + analysis.eventHandlers);
|
|
997
|
+
console.log(colorizer.cyan(' DOM Manipulation: ') + analysis.domManipulation);
|
|
998
|
+
console.log(colorizer.cyan(' AJAX Calls: ') + analysis.ajax);
|
|
999
|
+
console.log(colorizer.cyan(' Animations: ') + analysis.animations);
|
|
1000
|
+
console.log(colorizer.cyan(' CSS Manipulation: ') + analysis.cssManipulation);
|
|
1001
|
+
console.log(colorizer.cyan(' Complexity: ') + complexityLevel + ' (' + analysis.complexity + ')');
|
|
1002
|
+
console.log();
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
const totals = analyses.reduce((acc, { analysis }) => ({
|
|
1006
|
+
selectors: acc.selectors + analysis.selectors,
|
|
1007
|
+
eventHandlers: acc.eventHandlers + analysis.eventHandlers,
|
|
1008
|
+
domManipulation: acc.domManipulation + analysis.domManipulation,
|
|
1009
|
+
ajax: acc.ajax + analysis.ajax,
|
|
1010
|
+
animations: acc.animations + analysis.animations,
|
|
1011
|
+
cssManipulation: acc.cssManipulation + analysis.cssManipulation
|
|
1012
|
+
}), {
|
|
1013
|
+
selectors: 0,
|
|
1014
|
+
eventHandlers: 0,
|
|
1015
|
+
domManipulation: 0,
|
|
1016
|
+
ajax: 0,
|
|
1017
|
+
animations: 0,
|
|
1018
|
+
cssManipulation: 0
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
console.log(colorizer.section('Totals'));
|
|
1022
|
+
console.log(colorizer.cyan(' Total Files: ') + analyses.length);
|
|
1023
|
+
console.log(colorizer.cyan(' Total Selectors: ') + totals.selectors);
|
|
1024
|
+
console.log(colorizer.cyan(' Total Event Handlers: ') + totals.eventHandlers);
|
|
1025
|
+
console.log(colorizer.cyan(' Total DOM Manipulations: ') + totals.domManipulation);
|
|
1026
|
+
console.log(colorizer.cyan(' Total AJAX Calls: ') + totals.ajax);
|
|
1027
|
+
console.log(colorizer.cyan(' Total Animations: ') + totals.animations);
|
|
1028
|
+
console.log();
|
|
1029
|
+
},
|
|
1030
|
+
|
|
1031
|
+
convertBatch(args) {
|
|
1032
|
+
const inputDir = args[0] || '.';
|
|
1033
|
+
const outputDir = args[1] || './react-components';
|
|
1034
|
+
|
|
1035
|
+
console.log(colorizer.header('Batch jQuery to React Conversion'));
|
|
1036
|
+
console.log(colorizer.separator());
|
|
1037
|
+
console.log(colorizer.cyan('Input Directory: ') + colorizer.bright(inputDir));
|
|
1038
|
+
console.log(colorizer.cyan('Output Directory: ') + colorizer.bright(outputDir));
|
|
1039
|
+
console.log();
|
|
1040
|
+
|
|
1041
|
+
return this.findJQueryFiles(inputDir)
|
|
1042
|
+
.then(files => {
|
|
1043
|
+
if (files.length === 0) {
|
|
1044
|
+
console.log(colorizer.warning('No jQuery files found\n'));
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
console.log(colorizer.info('Found ' + files.length + ' file(s) to convert\n'));
|
|
1049
|
+
|
|
1050
|
+
return fs.mkdir(outputDir, { recursive: true })
|
|
1051
|
+
.then(() => {
|
|
1052
|
+
const promises = files.map(file => {
|
|
1053
|
+
const relativePath = path.relative(inputDir, file);
|
|
1054
|
+
const outputFile = path.join(
|
|
1055
|
+
outputDir,
|
|
1056
|
+
relativePath.replace(/\.js$/, '.jsx')
|
|
1057
|
+
);
|
|
1058
|
+
|
|
1059
|
+
console.log(colorizer.cyan('Converting: ') + relativePath);
|
|
1060
|
+
|
|
1061
|
+
return this.convert([file, outputFile])
|
|
1062
|
+
.catch(err => {
|
|
1063
|
+
console.log(colorizer.warning('Failed: ' + err.message));
|
|
1064
|
+
});
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
return Promise.all(promises);
|
|
1068
|
+
});
|
|
1069
|
+
})
|
|
1070
|
+
.then(() => {
|
|
1071
|
+
console.log(colorizer.success('\nBatch conversion complete!\n'));
|
|
1072
|
+
})
|
|
1073
|
+
.catch(err => {
|
|
1074
|
+
console.log(colorizer.error('Batch conversion failed: ' + err.message + '\n'));
|
|
1075
|
+
});
|
|
1076
|
+
},
|
|
1077
|
+
|
|
1078
|
+
toPascalCase(str) {
|
|
1079
|
+
return str
|
|
1080
|
+
.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '')
|
|
1081
|
+
.replace(/^(.)/, (_, c) => c.toUpperCase())
|
|
1082
|
+
.replace(/[^a-zA-Z0-9]/g, '');
|
|
1083
|
+
},
|
|
1084
|
+
|
|
1085
|
+
toCamelCase(str) {
|
|
1086
|
+
return str
|
|
1087
|
+
.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '')
|
|
1088
|
+
.replace(/^(.)/, (_, c) => c.toLowerCase())
|
|
1089
|
+
.replace(/[^a-zA-Z0-9]/g, '');
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
module.exports = JQueryToReact;
|