codescoop 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,501 @@
1
+ /**
2
+ * LLM Conversion Context Generator
3
+ * Generates structured output optimized for LLM-assisted React/Next.js conversion
4
+ */
5
+
6
+ const path = require('path');
7
+
8
+ /**
9
+ * Analyze jQuery/vanilla JS patterns and suggest React equivalents
10
+ * @param {Array} jsMatches - JS matches from analysis
11
+ * @returns {Object} Detected patterns and suggestions
12
+ */
13
+ function analyzeJSPatterns(jsMatches) {
14
+ const patterns = {
15
+ stateNeeded: [],
16
+ eventHandlers: [],
17
+ effects: [],
18
+ animations: [],
19
+ apiCalls: [],
20
+ domManipulations: []
21
+ };
22
+
23
+ const allContent = jsMatches.map(m => m.content || '').join('\n');
24
+
25
+ // Detect jQuery patterns
26
+ const jqueryPatterns = [
27
+ { regex: /\$\(['"]([\.\#][^'"]+)['"]\)\.click/g, type: 'event', event: 'onClick' },
28
+ { regex: /\$\(['"]([\.\#][^'"]+)['"]\)\.on\(['"]click/g, type: 'event', event: 'onClick' },
29
+ { regex: /\$\(['"]([\.\#][^'"]+)['"]\)\.hover/g, type: 'event', event: 'onMouseEnter/onMouseLeave' },
30
+ { regex: /\$\(['"]([\.\#][^'"]+)['"]\)\.submit/g, type: 'event', event: 'onSubmit' },
31
+ { regex: /\$\(['"]([\.\#][^'"]+)['"]\)\.change/g, type: 'event', event: 'onChange' },
32
+ { regex: /\$\(['"]([\.\#][^'"]+)['"]\)\.keyup/g, type: 'event', event: 'onKeyUp' },
33
+ { regex: /\$\(['"]([\.\#][^'"]+)['"]\)\.keydown/g, type: 'event', event: 'onKeyDown' },
34
+ { regex: /\.addClass\(['"]([^'"]+)['"]\)/g, type: 'state', name: 'classToggle' },
35
+ { regex: /\.removeClass\(['"]([^'"]+)['"]\)/g, type: 'state', name: 'classToggle' },
36
+ { regex: /\.toggleClass\(['"]([^'"]+)['"]\)/g, type: 'state', name: 'classToggle' },
37
+ { regex: /\.show\(\)/g, type: 'state', name: 'isVisible' },
38
+ { regex: /\.hide\(\)/g, type: 'state', name: 'isVisible' },
39
+ { regex: /\.toggle\(\)/g, type: 'state', name: 'isVisible' },
40
+ { regex: /\.slideToggle/g, type: 'animation', name: 'slideToggle', suggestion: 'framer-motion or CSS transitions' },
41
+ { regex: /\.slideDown/g, type: 'animation', name: 'slideDown', suggestion: 'framer-motion AnimatePresence' },
42
+ { regex: /\.slideUp/g, type: 'animation', name: 'slideUp', suggestion: 'framer-motion AnimatePresence' },
43
+ { regex: /\.fadeIn/g, type: 'animation', name: 'fadeIn', suggestion: 'framer-motion or CSS transitions' },
44
+ { regex: /\.fadeOut/g, type: 'animation', name: 'fadeOut', suggestion: 'framer-motion or CSS transitions' },
45
+ { regex: /\.animate\(/g, type: 'animation', name: 'custom', suggestion: 'framer-motion or GSAP' },
46
+ { regex: /\$\.ajax\(/g, type: 'api', suggestion: 'fetch + useEffect or React Query' },
47
+ { regex: /\$\.get\(/g, type: 'api', suggestion: 'fetch + useEffect' },
48
+ { regex: /\$\.post\(/g, type: 'api', suggestion: 'fetch + useEffect' },
49
+ { regex: /\.html\(/g, type: 'dom', warning: 'Use dangerouslySetInnerHTML or restructure' },
50
+ { regex: /\.text\(/g, type: 'dom', suggestion: 'Use state + JSX interpolation' },
51
+ { regex: /\.val\(/g, type: 'state', name: 'inputValue', suggestion: 'Controlled input with useState' },
52
+ { regex: /\.attr\(/g, type: 'dom', suggestion: 'Use props or state' },
53
+ { regex: /\.css\(/g, type: 'dom', suggestion: 'Use inline styles or CSS modules' },
54
+ { regex: /\.append\(/g, type: 'dom', warning: 'Restructure as state-driven rendering' },
55
+ { regex: /\.prepend\(/g, type: 'dom', warning: 'Restructure as state-driven rendering' },
56
+ { regex: /\.remove\(/g, type: 'dom', warning: 'Use conditional rendering' },
57
+ ];
58
+
59
+ // Vanilla JS patterns
60
+ const vanillaPatterns = [
61
+ { regex: /addEventListener\(['"]click['"]/g, type: 'event', event: 'onClick' },
62
+ { regex: /addEventListener\(['"]submit['"]/g, type: 'event', event: 'onSubmit' },
63
+ { regex: /addEventListener\(['"]change['"]/g, type: 'event', event: 'onChange' },
64
+ { regex: /addEventListener\(['"]keyup['"]/g, type: 'event', event: 'onKeyUp' },
65
+ { regex: /addEventListener\(['"]scroll['"]/g, type: 'effect', suggestion: 'useEffect with scroll listener' },
66
+ { regex: /addEventListener\(['"]resize['"]/g, type: 'effect', suggestion: 'useEffect with resize listener or useWindowSize hook' },
67
+ { regex: /classList\.add\(/g, type: 'state', name: 'classToggle' },
68
+ { regex: /classList\.remove\(/g, type: 'state', name: 'classToggle' },
69
+ { regex: /classList\.toggle\(/g, type: 'state', name: 'classToggle' },
70
+ { regex: /\.style\./g, type: 'dom', suggestion: 'Use inline styles object or CSS modules' },
71
+ { regex: /innerHTML/g, type: 'dom', warning: 'Avoid - use JSX or dangerouslySetInnerHTML' },
72
+ { regex: /textContent/g, type: 'dom', suggestion: 'Use state + JSX' },
73
+ { regex: /fetch\(/g, type: 'api', suggestion: 'Keep fetch, wrap in useEffect or use React Query' },
74
+ ];
75
+
76
+ // GSAP patterns
77
+ const gsapPatterns = [
78
+ { regex: /gsap\.to\(/g, type: 'animation', name: 'gsap.to', suggestion: 'Use @gsap/react useGSAP hook' },
79
+ { regex: /gsap\.from\(/g, type: 'animation', name: 'gsap.from', suggestion: 'Use @gsap/react useGSAP hook' },
80
+ { regex: /gsap\.fromTo\(/g, type: 'animation', name: 'gsap.fromTo', suggestion: 'Use @gsap/react useGSAP hook' },
81
+ { regex: /gsap\.timeline\(/g, type: 'animation', name: 'gsap.timeline', suggestion: 'Use @gsap/react useGSAP hook' },
82
+ { regex: /ScrollTrigger/g, type: 'animation', name: 'ScrollTrigger', suggestion: 'Use GSAP ScrollTrigger with useGSAP' },
83
+ ];
84
+
85
+ // Three.js patterns
86
+ const threePatterns = [
87
+ { regex: /THREE\.Scene/g, type: 'library', name: 'Three.js', suggestion: 'Use @react-three/fiber Canvas component' },
88
+ { regex: /THREE\.WebGLRenderer/g, type: 'library', name: 'Three.js', suggestion: 'Use @react-three/fiber Canvas' },
89
+ { regex: /THREE\.PerspectiveCamera/g, type: 'library', name: 'Three.js', suggestion: 'Use @react-three/fiber PerspectiveCamera' },
90
+ ];
91
+
92
+ const allPatterns = [...jqueryPatterns, ...vanillaPatterns, ...gsapPatterns, ...threePatterns];
93
+
94
+ for (const pattern of allPatterns) {
95
+ const matches = allContent.match(pattern.regex);
96
+ if (matches) {
97
+ const entry = {
98
+ pattern: pattern.regex.source,
99
+ count: matches.length,
100
+ ...pattern
101
+ };
102
+ delete entry.regex;
103
+
104
+ switch (pattern.type) {
105
+ case 'event':
106
+ patterns.eventHandlers.push(entry);
107
+ break;
108
+ case 'state':
109
+ patterns.stateNeeded.push(entry);
110
+ break;
111
+ case 'animation':
112
+ patterns.animations.push(entry);
113
+ break;
114
+ case 'api':
115
+ patterns.apiCalls.push(entry);
116
+ break;
117
+ case 'dom':
118
+ patterns.domManipulations.push(entry);
119
+ break;
120
+ case 'effect':
121
+ patterns.effects.push(entry);
122
+ break;
123
+ case 'library':
124
+ patterns.animations.push(entry); // Libraries go under animations for now
125
+ break;
126
+ }
127
+ }
128
+ }
129
+
130
+ // Detect $(document).ready patterns
131
+ if (/\$\(document\)\.ready/g.test(allContent) || /\$\(function/g.test(allContent)) {
132
+ patterns.effects.push({
133
+ pattern: 'document.ready',
134
+ suggestion: 'useEffect(() => { ... }, []) - runs once on mount'
135
+ });
136
+ }
137
+
138
+ // Detect DOMContentLoaded
139
+ if (/DOMContentLoaded/g.test(allContent)) {
140
+ patterns.effects.push({
141
+ pattern: 'DOMContentLoaded',
142
+ suggestion: 'useEffect(() => { ... }, []) - runs once on mount'
143
+ });
144
+ }
145
+
146
+ return patterns;
147
+ }
148
+
149
+ /**
150
+ * Generate suggested React state from patterns
151
+ * @param {Object} patterns - Patterns from analyzeJSPatterns
152
+ * @returns {Array} Suggested state variables
153
+ */
154
+ function generateStateSuggestions(patterns) {
155
+ const stateVars = new Map();
156
+
157
+ // From class toggles
158
+ patterns.stateNeeded.forEach(p => {
159
+ if (p.name === 'classToggle') {
160
+ stateVars.set('isActive', { type: 'boolean', default: 'false', reason: 'Class toggle detected' });
161
+ }
162
+ if (p.name === 'isVisible') {
163
+ stateVars.set('isVisible', { type: 'boolean', default: 'true', reason: 'Show/hide detected' });
164
+ }
165
+ if (p.name === 'inputValue') {
166
+ stateVars.set('inputValue', { type: 'string', default: "''", reason: 'Input value manipulation detected' });
167
+ }
168
+ });
169
+
170
+ // From animations
171
+ patterns.animations.forEach(p => {
172
+ if (p.name === 'slideToggle' || p.name === 'slideDown' || p.name === 'slideUp') {
173
+ stateVars.set('isExpanded', { type: 'boolean', default: 'false', reason: 'Slide animation detected' });
174
+ }
175
+ if (p.name === 'fadeIn' || p.name === 'fadeOut') {
176
+ stateVars.set('isVisible', { type: 'boolean', default: 'true', reason: 'Fade animation detected' });
177
+ }
178
+ });
179
+
180
+ return Array.from(stateVars.entries()).map(([name, info]) => ({
181
+ name,
182
+ ...info
183
+ }));
184
+ }
185
+
186
+ /**
187
+ * Generate suggested dependencies
188
+ * @param {Object} patterns - Patterns from analyzeJSPatterns
189
+ * @param {Object} detectedLibraries - Libraries detected from the analysis
190
+ * @returns {Array} Suggested npm packages
191
+ */
192
+ function generateDependencySuggestions(patterns, detectedLibraries = {}) {
193
+ const deps = new Map();
194
+
195
+ // Animation libraries
196
+ if (patterns.animations.length > 0) {
197
+ const hasGSAP = patterns.animations.some(p => p.name?.includes('gsap'));
198
+ const hasThree = patterns.animations.some(p => p.name === 'Three.js');
199
+
200
+ if (hasGSAP) {
201
+ deps.set('gsap', { version: '^3.12.0', reason: 'GSAP animations detected' });
202
+ deps.set('@gsap/react', { version: '^2.0.0', reason: 'React GSAP integration' });
203
+ } else if (patterns.animations.some(p => p.suggestion?.includes('framer-motion'))) {
204
+ deps.set('framer-motion', { version: '^10.0.0', reason: 'Animations need React-compatible library' });
205
+ }
206
+
207
+ if (hasThree) {
208
+ deps.set('@react-three/fiber', { version: '^8.0.0', reason: 'Three.js detected, use R3F for React' });
209
+ deps.set('@react-three/drei', { version: '^9.0.0', reason: 'Helpful Three.js React utilities' });
210
+ }
211
+ }
212
+
213
+ // API calls
214
+ if (patterns.apiCalls.length > 0) {
215
+ deps.set('@tanstack/react-query', { version: '^5.0.0', reason: 'Better data fetching than raw useEffect' });
216
+ }
217
+
218
+ // From detected libraries
219
+ const libFiles = detectedLibraries.fromFiles || {};
220
+ Object.keys(libFiles).forEach(libName => {
221
+ const lowerName = libName.toLowerCase();
222
+ if (lowerName.includes('swiper')) {
223
+ deps.set('swiper', { version: '^11.0.0', reason: 'Swiper detected, use React-compatible version' });
224
+ }
225
+ if (lowerName.includes('aos')) {
226
+ deps.set('aos', { version: '^2.3.4', reason: 'AOS animations' });
227
+ }
228
+ });
229
+
230
+ return Array.from(deps.entries()).map(([name, info]) => ({
231
+ package: name,
232
+ ...info
233
+ }));
234
+ }
235
+
236
+ /**
237
+ * Clean HTML for React conversion
238
+ * Removes jQuery-specific attributes, normalizes for JSX
239
+ * @param {string} html - Original HTML
240
+ * @returns {Object} Cleaned HTML and notes
241
+ */
242
+ function cleanHTMLForReact(html) {
243
+ const notes = [];
244
+ let cleaned = html;
245
+
246
+ // Replace class with className
247
+ cleaned = cleaned.replace(/\bclass="/g, 'className="');
248
+ notes.push('Replaced `class` with `className`');
249
+
250
+ // Replace for with htmlFor
251
+ cleaned = cleaned.replace(/\bfor="/g, 'htmlFor="');
252
+ if (html.includes('for="')) {
253
+ notes.push('Replaced `for` with `htmlFor`');
254
+ }
255
+
256
+ // Self-close void elements
257
+ const voidElements = ['br', 'hr', 'img', 'input', 'meta', 'link', 'source'];
258
+ voidElements.forEach(tag => {
259
+ const regex = new RegExp(`<${tag}([^>]*)(?<!/)>`, 'gi');
260
+ cleaned = cleaned.replace(regex, `<${tag}$1 />`);
261
+ });
262
+ notes.push('Self-closed void elements');
263
+
264
+ // Note about inline event handlers
265
+ if (/\bon\w+="/i.test(html)) {
266
+ notes.push('⚠️ Inline event handlers detected - convert to JSX handlers');
267
+ }
268
+
269
+ // Note about inline styles
270
+ if (/style="[^"]+"/i.test(html)) {
271
+ notes.push('⚠️ Inline styles detected - convert to style={{ }} objects');
272
+ }
273
+
274
+ // Remove data-* jQuery plugin attributes
275
+ const jqueryDataAttrs = ['data-toggle', 'data-target', 'data-dismiss', 'data-slide', 'data-ride'];
276
+ jqueryDataAttrs.forEach(attr => {
277
+ if (html.includes(attr)) {
278
+ notes.push(`⚠️ Bootstrap/jQuery \`${attr}\` detected - needs custom React handling`);
279
+ }
280
+ });
281
+
282
+ return { cleaned, notes };
283
+ }
284
+
285
+ /**
286
+ * Generate the conversion context markdown
287
+ * @param {Object} analysis - Full analysis object
288
+ * @returns {string} Markdown optimized for LLM conversion
289
+ */
290
+ function generateConversionContext(analysis) {
291
+ const {
292
+ targetInfo,
293
+ cssResults,
294
+ jsResults,
295
+ inlineStyles,
296
+ variableData = {},
297
+ detectedLibraries = {}
298
+ } = analysis;
299
+
300
+ // Analyze JS patterns
301
+ const allJsMatches = [
302
+ ...jsResults.flatMap(r => r.matches || []),
303
+ ...(analysis.inlineScripts || [])
304
+ ];
305
+ const patterns = analyzeJSPatterns(allJsMatches);
306
+ const stateSuggestions = generateStateSuggestions(patterns);
307
+ const dependencies = generateDependencySuggestions(patterns, detectedLibraries);
308
+
309
+ // Clean HTML
310
+ const { cleaned: cleanedHTML, notes: htmlNotes } = cleanHTMLForReact(targetInfo.html);
311
+
312
+ let md = '';
313
+
314
+ // Header
315
+ md += `# React/Next.js Conversion Context\n\n`;
316
+ md += `> Generated for converting \`${targetInfo.selector}\` to React\n\n`;
317
+ md += `---\n\n`;
318
+
319
+ // Quick Start
320
+ md += `## 🚀 Quick Start for LLM\n\n`;
321
+ md += `Paste this entire document to Claude/GPT with the prompt:\n\n`;
322
+ md += `> "Convert this HTML component to a React functional component with TypeScript. `;
323
+ md += `Use the suggested state, implement the event handlers, and apply the CSS as CSS Modules."\n\n`;
324
+ md += `---\n\n`;
325
+
326
+ // Suggested State
327
+ if (stateSuggestions.length > 0) {
328
+ md += `## 📊 Suggested React State\n\n`;
329
+ md += `Based on detected JS patterns, you'll likely need:\n\n`;
330
+ md += `\`\`\`typescript\n`;
331
+ stateSuggestions.forEach(s => {
332
+ md += `const [${s.name}, set${s.name.charAt(0).toUpperCase() + s.name.slice(1)}] = useState<${s.type}>(${s.default}); // ${s.reason}\n`;
333
+ });
334
+ md += `\`\`\`\n\n`;
335
+ }
336
+
337
+ // Event Handlers
338
+ if (patterns.eventHandlers.length > 0) {
339
+ md += `## 🎯 Event Handlers to Implement\n\n`;
340
+ md += `| jQuery/Vanilla Pattern | React Equivalent | Count |\n`;
341
+ md += `|------------------------|------------------|-------|\n`;
342
+ patterns.eventHandlers.forEach(e => {
343
+ md += `| \`${e.pattern.substring(0, 40)}\` | \`${e.event}\` | ${e.count} |\n`;
344
+ });
345
+ md += `\n`;
346
+ }
347
+
348
+ // Animations
349
+ if (patterns.animations.length > 0) {
350
+ md += `## ✨ Animations Detected\n\n`;
351
+ patterns.animations.forEach(a => {
352
+ md += `- **${a.name || a.pattern}** (${a.count}x) → ${a.suggestion}\n`;
353
+ });
354
+ md += `\n`;
355
+ }
356
+
357
+ // DOM Manipulations (Warnings)
358
+ if (patterns.domManipulations.length > 0) {
359
+ md += `## ⚠️ DOM Manipulations to Refactor\n\n`;
360
+ md += `These imperative patterns need to become declarative React:\n\n`;
361
+ patterns.domManipulations.forEach(d => {
362
+ const msg = d.warning || d.suggestion;
363
+ md += `- \`${d.pattern.substring(0, 30)}\` → ${msg}\n`;
364
+ });
365
+ md += `\n`;
366
+ }
367
+
368
+ // Effects
369
+ if (patterns.effects.length > 0) {
370
+ md += `## 🔄 useEffect Patterns Needed\n\n`;
371
+ patterns.effects.forEach(e => {
372
+ md += `- ${e.pattern} → ${e.suggestion}\n`;
373
+ });
374
+ md += `\n`;
375
+ }
376
+
377
+ // Dependencies
378
+ if (dependencies.length > 0) {
379
+ md += `## 📦 Suggested Dependencies\n\n`;
380
+ md += `\`\`\`bash\nnpm install`;
381
+ dependencies.forEach(d => {
382
+ md += ` ${d.package}`;
383
+ });
384
+ md += `\n\`\`\`\n\n`;
385
+ md += `| Package | Version | Reason |\n`;
386
+ md += `|---------|---------|--------|\n`;
387
+ dependencies.forEach(d => {
388
+ md += `| ${d.package} | ${d.version} | ${d.reason} |\n`;
389
+ });
390
+ md += `\n`;
391
+ }
392
+
393
+ md += `---\n\n`;
394
+
395
+ // HTML Section
396
+ md += `## 📄 Component HTML (JSX-Ready)\n\n`;
397
+ if (htmlNotes.length > 0) {
398
+ md += `**Transformations applied:**\n`;
399
+ htmlNotes.forEach(n => md += `- ${n}\n`);
400
+ md += `\n`;
401
+ }
402
+ md += `\`\`\`jsx\n${cleanedHTML}\n\`\`\`\n\n`;
403
+
404
+ md += `---\n\n`;
405
+
406
+ // CSS Variables
407
+ if (variableData && variableData.usedVariables && variableData.usedVariables.length > 0) {
408
+ md += `## 🎨 CSS Variables Required\n\n`;
409
+ const cssVars = variableData.cssVariables || {};
410
+ const scssVars = variableData.scssVariables || {};
411
+
412
+ if (Object.keys(cssVars).length > 0) {
413
+ md += `### CSS Custom Properties\n\n`;
414
+ md += `Add to your global styles or component:\n\n`;
415
+ md += `\`\`\`css\n:root {\n`;
416
+ for (const [name, defs] of Object.entries(cssVars)) {
417
+ if (defs[0]) {
418
+ md += ` ${name}: ${defs[0].value};\n`;
419
+ }
420
+ }
421
+ md += `}\n\`\`\`\n\n`;
422
+ }
423
+
424
+ if (Object.keys(scssVars).length > 0) {
425
+ md += `### SCSS Variables\n\n`;
426
+ md += `\`\`\`scss\n`;
427
+ for (const [name, defs] of Object.entries(scssVars)) {
428
+ if (defs[0]) {
429
+ md += `${name}: ${defs[0].value};\n`;
430
+ }
431
+ }
432
+ md += `\`\`\`\n\n`;
433
+ }
434
+
435
+ // Undefined variables warning
436
+ const undefinedVars = [
437
+ ...(variableData.undefinedCSSVariables || []),
438
+ ...(variableData.undefinedSCSSVariables || [])
439
+ ];
440
+ if (undefinedVars.length > 0) {
441
+ md += `### ⚠️ Variables Used But Not Found\n\n`;
442
+ undefinedVars.forEach(v => md += `- \`${v}\`\n`);
443
+ md += `\n> These may come from a framework or external stylesheet.\n\n`;
444
+ }
445
+
446
+ md += `---\n\n`;
447
+ }
448
+
449
+ // CSS Section
450
+ md += `## 🎨 Component CSS\n\n`;
451
+ md += `Convert to CSS Modules (\`Component.module.css\`) or styled-components:\n\n`;
452
+
453
+ // Collect all CSS
454
+ const allCSS = [];
455
+ cssResults.forEach(r => {
456
+ if (r.matches && r.matches.length > 0) {
457
+ r.matches.forEach(m => {
458
+ allCSS.push(m.content);
459
+ });
460
+ }
461
+ });
462
+ inlineStyles?.forEach(s => {
463
+ allCSS.push(s.content);
464
+ });
465
+
466
+ if (allCSS.length > 0) {
467
+ // Limit in conversion mode too
468
+ const limitedCSS = allCSS.slice(0, 50);
469
+ md += `\`\`\`css\n${limitedCSS.join('\n\n')}\n\`\`\`\n\n`;
470
+ if (allCSS.length > 50) {
471
+ md += `> ⚠️ ${allCSS.length - 50} more CSS rules omitted. Use \`--max-rules\` to adjust.\n\n`;
472
+ }
473
+ }
474
+
475
+ md += `---\n\n`;
476
+
477
+ // Checklist
478
+ md += `## ✅ Conversion Checklist\n\n`;
479
+ md += `- [ ] Create \`Component.tsx\` file\n`;
480
+ md += `- [ ] Create \`Component.module.css\` file\n`;
481
+ md += `- [ ] Implement useState hooks\n`;
482
+ md += `- [ ] Convert event handlers to JSX\n`;
483
+ if (patterns.animations.length > 0) {
484
+ md += `- [ ] Set up animation library (framer-motion/GSAP)\n`;
485
+ }
486
+ if (patterns.apiCalls.length > 0) {
487
+ md += `- [ ] Convert API calls to fetch/React Query\n`;
488
+ }
489
+ md += `- [ ] Test component in isolation\n`;
490
+ md += `- [ ] Integrate into Next.js page/layout\n\n`;
491
+
492
+ return md;
493
+ }
494
+
495
+ module.exports = {
496
+ analyzeJSPatterns,
497
+ generateStateSuggestions,
498
+ generateDependencySuggestions,
499
+ cleanHTMLForReact,
500
+ generateConversionContext
501
+ };