farmon 0.1.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.
Files changed (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/bin/farmon.js +12 -0
  4. package/dist/bin/farmon.js.map +1 -0
  5. package/dist/execute/agents/index.js +19 -0
  6. package/dist/execute/agents/index.js.map +1 -0
  7. package/dist/execute/agents/instruction-classifier-agent.js +16 -0
  8. package/dist/execute/agents/instruction-classifier-agent.js.map +1 -0
  9. package/dist/execute/agents/mutation-agent.js +272 -0
  10. package/dist/execute/agents/mutation-agent.js.map +1 -0
  11. package/dist/execute/agents/query-agent.js +118 -0
  12. package/dist/execute/agents/query-agent.js.map +1 -0
  13. package/dist/execute/helpers/analyzers.js +8 -0
  14. package/dist/execute/helpers/analyzers.js.map +1 -0
  15. package/dist/execute/helpers/ensurers.js +1053 -0
  16. package/dist/execute/helpers/ensurers.js.map +1 -0
  17. package/dist/execute/helpers/finders.js +1454 -0
  18. package/dist/execute/helpers/finders.js.map +1 -0
  19. package/dist/execute/helpers/general.js +3736 -0
  20. package/dist/execute/helpers/general.js.map +1 -0
  21. package/dist/execute/helpers/import-helpers.js +183 -0
  22. package/dist/execute/helpers/import-helpers.js.map +1 -0
  23. package/dist/execute/helpers/parsers.js +840 -0
  24. package/dist/execute/helpers/parsers.js.map +1 -0
  25. package/dist/execute/helpers/prompt-maker.js +1163 -0
  26. package/dist/execute/helpers/prompt-maker.js.map +1 -0
  27. package/dist/execute/helpers/validators.js +40 -0
  28. package/dist/execute/helpers/validators.js.map +1 -0
  29. package/dist/execute/history/history-manager.js +1030 -0
  30. package/dist/execute/history/history-manager.js.map +1 -0
  31. package/dist/execute/history/rollback-handlers.js +2524 -0
  32. package/dist/execute/history/rollback-handlers.js.map +1 -0
  33. package/dist/execute/index.js +44 -0
  34. package/dist/execute/index.js.map +1 -0
  35. package/dist/execute/llm/call.js +103 -0
  36. package/dist/execute/llm/call.js.map +1 -0
  37. package/dist/execute/tasks/ast.js +3819 -0
  38. package/dist/execute/tasks/ast.js.map +1 -0
  39. package/dist/execute/tasks/generators.js +96 -0
  40. package/dist/execute/tasks/generators.js.map +1 -0
  41. package/dist/execute/tasks/index.js +7 -0
  42. package/dist/execute/tasks/index.js.map +1 -0
  43. package/dist/execute/tasks/mutations.js +8139 -0
  44. package/dist/execute/tasks/mutations.js.map +1 -0
  45. package/dist/execute/tasks/query.js +248 -0
  46. package/dist/execute/tasks/query.js.map +1 -0
  47. package/dist/index.js +6 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/providers/index.js +15 -0
  50. package/dist/providers/index.js.map +1 -0
  51. package/dist/providers/ollama.js +40 -0
  52. package/dist/providers/ollama.js.map +1 -0
  53. package/dist/providers/openai-compatible.js +52 -0
  54. package/dist/providers/openai-compatible.js.map +1 -0
  55. package/dist/runtime/inject.js +250 -0
  56. package/dist/runtime/inject.js.map +1 -0
  57. package/dist/schemas/agent/action.schema.js +935 -0
  58. package/dist/schemas/agent/action.schema.js.map +1 -0
  59. package/dist/schemas/agent/index.js +4 -0
  60. package/dist/schemas/agent/index.js.map +1 -0
  61. package/dist/schemas/agent/llm.schema.js +16 -0
  62. package/dist/schemas/agent/llm.schema.js.map +1 -0
  63. package/dist/schemas/agent/planner.schema.js +17 -0
  64. package/dist/schemas/agent/planner.schema.js.map +1 -0
  65. package/dist/schemas/index.js +7 -0
  66. package/dist/schemas/index.js.map +1 -0
  67. package/dist/schemas/project/context.schema.js +2 -0
  68. package/dist/schemas/project/context.schema.js.map +1 -0
  69. package/dist/schemas/project/index.js +2 -0
  70. package/dist/schemas/project/index.js.map +1 -0
  71. package/dist/schemas/runtime/index.js +4 -0
  72. package/dist/schemas/runtime/index.js.map +1 -0
  73. package/dist/schemas/runtime/injector.schema.js +11 -0
  74. package/dist/schemas/runtime/injector.schema.js.map +1 -0
  75. package/dist/schemas/runtime/runtime.schema.js +73 -0
  76. package/dist/schemas/runtime/runtime.schema.js.map +1 -0
  77. package/dist/schemas/runtime/sse.schema.js +15 -0
  78. package/dist/schemas/runtime/sse.schema.js.map +1 -0
  79. package/dist/schemas/system/index.js +2 -0
  80. package/dist/schemas/system/index.js.map +1 -0
  81. package/dist/schemas/system/logger.schema.js +56 -0
  82. package/dist/schemas/system/logger.schema.js.map +1 -0
  83. package/dist/schemas/task/index.js +9 -0
  84. package/dist/schemas/task/index.js.map +1 -0
  85. package/dist/server/app-context.js +254 -0
  86. package/dist/server/app-context.js.map +1 -0
  87. package/dist/server/config.js +22 -0
  88. package/dist/server/config.js.map +1 -0
  89. package/dist/server/error.js +22 -0
  90. package/dist/server/error.js.map +1 -0
  91. package/dist/server/event-bus.js +60 -0
  92. package/dist/server/event-bus.js.map +1 -0
  93. package/dist/server/logger.js +57 -0
  94. package/dist/server/logger.js.map +1 -0
  95. package/dist/server/run.js +265 -0
  96. package/dist/server/run.js.map +1 -0
  97. package/dist/server/sse.js +143 -0
  98. package/dist/server/sse.js.map +1 -0
  99. package/dist/ui/assets/index-C4ydQSAw.css +2 -0
  100. package/dist/ui/assets/index-Dzo7S5xs.js +85 -0
  101. package/dist/ui/favicon.svg +1 -0
  102. package/dist/ui/icons.svg +24 -0
  103. package/dist/ui/index.html +14 -0
  104. package/dist/workers/prettier.js +11 -0
  105. package/dist/workers/prettier.js.map +1 -0
  106. package/package.json +114 -0
@@ -0,0 +1,3819 @@
1
+ /*
2
+
3
+ Pure Tasks: AST/code transforms only: Changes will happen in memmory, but not reflected in actual file system.
4
+ -----------
5
+ insertJSX - Inserts new JSX into a target component.
6
+ replaceJSX - Replaces an existing JSX element with new JSX.
7
+ removeJSX - Removes a JSX element from a component.
8
+ moveJSX - Moves a JSX element to a different location in the JSX tree.
9
+ wrapJSX - Wraps a JSX element with a parent JSX element.
10
+ insertVariable - Inserts a new variable declaration into code.
11
+ updateVariable - Updates an existing variable declaration.
12
+ deleteVariable - Removes a variable declaration from code.
13
+ createFunction - Creates a new function declaration.
14
+ updateFunction - Updates an existing function declaration.
15
+ deleteFunction - Removes a function declaration from code.
16
+ renameCssClass - Renames a CSS class in both CSS and JSX.
17
+
18
+
19
+ */
20
+ // import * as parser from "@babel/parser";
21
+ import traverse from "@babel/traverse";
22
+ import { parse, parseExpression } from "@babel/parser";
23
+ import { generate } from "@babel/generator";
24
+ import t from "@babel/types";
25
+ import helpers from "../../execute/helpers/general.js";
26
+ import utils from "../../execute/helpers/general.js";
27
+ import { ERROR_CODES, } from "../../schemas/index.js";
28
+ import { LoomaError } from "../../server/error.js";
29
+ /**
30
+ * DescriptionForPrompt: Inserts a new variable declaration into code.
31
+ *
32
+ * ------------------------------------------------------------
33
+ * WHAT THIS FUNCTION DOES
34
+ * ------------------------------------------------------------
35
+ *
36
+ * This function inserts variables in:
37
+ *
38
+ * 1) global/module scope
39
+ * OR
40
+ * 2) inside a function/component scope
41
+ *
42
+ * ------------------------------------------------------------
43
+ * EXAMPLES
44
+ * ------------------------------------------------------------
45
+ *
46
+ * GLOBAL SCOPE
47
+ *
48
+ * BEFORE:
49
+ *
50
+ * import React from "react";
51
+ *
52
+ * function App() {}
53
+ *
54
+ * AFTER:
55
+ *
56
+ * import React from "react";
57
+ *
58
+ * const API_URL = "/api";
59
+ *
60
+ * function App() {}
61
+ *
62
+ * ------------------------------------------------------------
63
+ *
64
+ * FUNCTION / COMPONENT SCOPE
65
+ *
66
+ * BEFORE:
67
+ *
68
+ * function App() {
69
+ * return <div />;
70
+ * }
71
+ *
72
+ * AFTER:
73
+ *
74
+ * function App() {
75
+ * const count = 0;
76
+ *
77
+ * return <div />;
78
+ * }
79
+ *
80
+ * ------------------------------------------------------------
81
+ * WHY AST IS IMPORTANT
82
+ * ------------------------------------------------------------
83
+ *
84
+ * Variable insertion is NOT safe using string replacement.
85
+ *
86
+ * Because:
87
+ * - imports may move
88
+ * - formatting changes
89
+ * - nested scopes exist
90
+ * - components/functions vary
91
+ *
92
+ * AST manipulation guarantees:
93
+ * - correct scope
94
+ * - valid syntax
95
+ * - deterministic insertion
96
+ *
97
+ * Useful commands:
98
+ * - add state: insertVariable(count)
99
+ * - store api url: insertVariable(API_URL)
100
+ * - add loading state: insertVariable(loading)
101
+ * - add mock data: insertVariable(mockData)
102
+ * - save form values: insertVariable(formValues)
103
+ *
104
+ * User Command
105
+
106
+ Planner
107
+
108
+ insertVariable
109
+
110
+ updateJSX / updateFunction
111
+ *
112
+ * ------------------------------------------------------------
113
+ * IMPORTANT ARCHITECTURAL CONSTRAINT
114
+ * ------------------------------------------------------------
115
+ *
116
+ * This implementation assumes:
117
+ *
118
+ * - React components use function declarations
119
+ *
120
+ * Example:
121
+ *
122
+ * function Header() {}
123
+ *
124
+ * NOT:
125
+ *
126
+ * const Header = () => {}
127
+ *
128
+ * This simplifies deterministic targeting.
129
+ *
130
+ * ------------------------------------------------------------
131
+ * SUPPORTED VALUE TYPES
132
+ * ------------------------------------------------------------
133
+ *
134
+ * string
135
+ * number
136
+ * boolean
137
+ * object
138
+ * array
139
+ * null
140
+ *
141
+ * ------------------------------------------------------------
142
+ * PARAMS
143
+ * ------------------------------------------------------------
144
+ *
145
+ * @param {Object} params
146
+ *
147
+ * @param {string} params.code
148
+ * Entire source code.
149
+ *
150
+ * @param {string} params.variableName
151
+ * Name of variable to insert.
152
+ *
153
+ * @param {any} params.value
154
+ * Initial value of variable.
155
+ *
156
+ * @param {"global"|"function"} params.scope
157
+ * Where variable should be inserted.
158
+ *
159
+ * @param {string=} params.functionName
160
+ * Required when scope === "function"
161
+ *
162
+ * ------------------------------------------------------------
163
+ * RETURNS
164
+ * ------------------------------------------------------------
165
+ *
166
+ * @returns {{ updatedCode: string }}
167
+ * Updated source code.
168
+ *
169
+ */
170
+ function insertVariable({ code, variableName, value, scope, functionName }, context) {
171
+ // ----------------------------------------------------------
172
+ // STEP 1:
173
+ // Parse source code into AST
174
+ // ----------------------------------------------------------
175
+ const ast = parse(code, {
176
+ sourceType: "module",
177
+ plugins: ["jsx", "typescript"],
178
+ });
179
+ // ----------------------------------------------------------
180
+ // STEP 2:
181
+ // Convert JS value into AST expression
182
+ //
183
+ // Example:
184
+ //
185
+ // "hello" -> StringLiteral
186
+ // 123 -> NumericLiteral
187
+ // ----------------------------------------------------------
188
+ const valueNode = t.valueToNode(value);
189
+ // ----------------------------------------------------------
190
+ // STEP 3:
191
+ // Create variable declaration AST node
192
+ //
193
+ // Example:
194
+ //
195
+ // const count = 0;
196
+ // ----------------------------------------------------------
197
+ const variableDeclaration = t.variableDeclaration("const", [
198
+ t.variableDeclarator(t.identifier(variableName), valueNode),
199
+ ]);
200
+ // ==========================================================
201
+ // GLOBAL SCOPE INSERTION
202
+ // ==========================================================
203
+ if (scope === "global") {
204
+ // --------------------------------------------------------
205
+ // Find last import statement
206
+ //
207
+ // WHY?
208
+ //
209
+ // Variables should appear AFTER imports.
210
+ //
211
+ // GOOD:
212
+ //
213
+ // import React from "react";
214
+ //
215
+ // const API_URL = "...";
216
+ //
217
+ // --------------------------------------------------------
218
+ let insertIndex = 0;
219
+ // --------------------------------------------------------
220
+ // Iterate through top-level statements
221
+ // --------------------------------------------------------
222
+ ast.program.body.forEach((node, index) => {
223
+ // ------------------------------------------------------
224
+ // Track last import position
225
+ // ------------------------------------------------------
226
+ if (t.isImportDeclaration(node)) {
227
+ insertIndex = index + 1;
228
+ }
229
+ });
230
+ // --------------------------------------------------------
231
+ // Insert variable after imports
232
+ // ------------------------------------------------------
233
+ ast.program.body.splice(insertIndex, 0, variableDeclaration);
234
+ }
235
+ // ==========================================================
236
+ // FUNCTION / COMPONENT SCOPE INSERTION
237
+ // ==========================================================
238
+ if (scope === "function") {
239
+ // --------------------------------------------------------
240
+ // Traverse AST searching target function
241
+ // --------------------------------------------------------
242
+ traverse.default(ast, {
243
+ FunctionDeclaration(path) {
244
+ // ----------------------------------------------------
245
+ // Ignore unrelated functions
246
+ // ----------------------------------------------------
247
+ if (path.node.id?.name !== functionName) {
248
+ return;
249
+ }
250
+ // ----------------------------------------------------
251
+ // Get function body statements
252
+ //
253
+ // Example:
254
+ //
255
+ // function App() {
256
+ // BODY HERE
257
+ // }
258
+ // ----------------------------------------------------
259
+ const bodyStatements = path.node.body.body;
260
+ // ----------------------------------------------------
261
+ // Insert variable at beginning of function body
262
+ //
263
+ // BEFORE:
264
+ //
265
+ // function App() {
266
+ // return <div />
267
+ // }
268
+ //
269
+ // AFTER:
270
+ //
271
+ // function App() {
272
+ // const count = 0;
273
+ // return <div />
274
+ // }
275
+ // ----------------------------------------------------
276
+ bodyStatements.unshift(variableDeclaration);
277
+ },
278
+ });
279
+ }
280
+ // ----------------------------------------------------------
281
+ // STEP 4:
282
+ // Generate updated code from AST
283
+ // ----------------------------------------------------------
284
+ return { success: true, updatedCode: generate(ast).code };
285
+ }
286
+ /**
287
+ * DescriptionForPrompt: Updates the value of an existing variable.
288
+ *
289
+ * ------------------------------------------------------------
290
+ * WHAT THIS FUNCTION DOES
291
+ * ------------------------------------------------------------
292
+ *
293
+ * This function searches for a variable declaration and
294
+ * replaces its value.
295
+ *
296
+ * ------------------------------------------------------------
297
+ * EXAMPLES
298
+ * ------------------------------------------------------------
299
+ *
300
+ * BEFORE:
301
+ *
302
+ * const API_URL = "/api";
303
+ *
304
+ * AFTER:
305
+ *
306
+ * const API_URL = "/v2/api";
307
+ *
308
+ * ------------------------------------------------------------
309
+ *
310
+ * BEFORE:
311
+ *
312
+ * const loading = false;
313
+ *
314
+ * AFTER:
315
+ *
316
+ * const loading = true;
317
+ *
318
+ * ------------------------------------------------------------
319
+ *
320
+ * BEFORE:
321
+ *
322
+ * const count = 0;
323
+ *
324
+ * AFTER:
325
+ *
326
+ * const count = 10;
327
+ *
328
+ * ------------------------------------------------------------
329
+ * WHY AST IS IMPORTANT
330
+ * ------------------------------------------------------------
331
+ *
332
+ * Updating variables using string replacement is dangerous.
333
+ *
334
+ * Because:
335
+ * - duplicate variable names may exist
336
+ * - nested scopes may exist
337
+ * - formatting changes break regex logic
338
+ *
339
+ * AST manipulation ensures:
340
+ * - valid syntax
341
+ * - deterministic updates
342
+ * - structure-aware targeting
343
+ *
344
+ *
345
+ * Useful commands:
346
+ * - change api url:updateVariable(API_URL)
347
+ * - set loading true:updateVariable(loading)
348
+ * - change default count to 10:updateVariable(count)
349
+ * - update mock data:updateVariable(mockData)
350
+ * - change theme color:updateVariable(themeColor)
351
+ *
352
+ * User Command
353
+
354
+ Planner
355
+
356
+ updateVariable
357
+
358
+ updateJSX / updateFunction
359
+ *
360
+ * ------------------------------------------------------------
361
+ * IMPORTANT ARCHITECTURAL IDEA
362
+ * ------------------------------------------------------------
363
+ *
364
+ * This function only updates variable declarations.
365
+ *
366
+ * Example:
367
+ *
368
+ * const count = 0;
369
+ *
370
+ * It does NOT update:
371
+ *
372
+ * count = 5;
373
+ *
374
+ * because assignment expressions are a separate concern.
375
+ *
376
+ * ------------------------------------------------------------
377
+ * SUPPORTED VALUE TYPES
378
+ * ------------------------------------------------------------
379
+ *
380
+ * string
381
+ * number
382
+ * boolean
383
+ * object
384
+ * array
385
+ * null
386
+ *
387
+ * ------------------------------------------------------------
388
+ * OPTIONAL LINE TARGETING
389
+ * ------------------------------------------------------------
390
+ *
391
+ * Multiple variables with same name may exist.
392
+ *
393
+ * Example:
394
+ *
395
+ * const data = 1;
396
+ *
397
+ * function App() {
398
+ * const data = 2;
399
+ * }
400
+ *
401
+ * line can be used for deterministic targeting.
402
+ *
403
+ * ------------------------------------------------------------
404
+ * PARAMS
405
+ * ------------------------------------------------------------
406
+ *
407
+ * @param {Object} params
408
+ *
409
+ * @param {string} params.code
410
+ * Entire source code.
411
+ *
412
+ * @param {string} params.variableName
413
+ * Name of variable to update.
414
+ *
415
+ * @param {any} params.newValue
416
+ * New value for variable.
417
+ *
418
+ * @param {number=} params.line
419
+ * Optional declaration line number.
420
+ *
421
+ * ------------------------------------------------------------
422
+ * RETURNS
423
+ * ------------------------------------------------------------
424
+ *
425
+ * @returns {{ updatedCode: string }}
426
+ * Updated source code.
427
+ *
428
+ */
429
+ function updateVariable({ code, variableName, newValue, line }, context) {
430
+ // ----------------------------------------------------------
431
+ // STEP 1:
432
+ // Parse source code into AST
433
+ // ----------------------------------------------------------
434
+ const ast = parse(code, {
435
+ sourceType: "module",
436
+ plugins: ["jsx", "typescript"],
437
+ });
438
+ // ----------------------------------------------------------
439
+ // STEP 2:
440
+ // Convert JS value into AST node
441
+ //
442
+ // Example:
443
+ //
444
+ // "hello" -> StringLiteral
445
+ // 123 -> NumericLiteral
446
+ // ----------------------------------------------------------
447
+ const newValueNode = t.valueToNode(newValue);
448
+ // ----------------------------------------------------------
449
+ // STEP 3:
450
+ // Traverse AST searching variable declarations
451
+ // ----------------------------------------------------------
452
+ traverse.default(ast, {
453
+ VariableDeclarator(path) {
454
+ // ------------------------------------------------------
455
+ // Ignore unrelated variables
456
+ // ------------------------------------------------------
457
+ if (path.node.id.type !== "Identifier") {
458
+ return;
459
+ }
460
+ if (path.node.id.name !== variableName) {
461
+ return;
462
+ }
463
+ // ------------------------------------------------------
464
+ // Optional line targeting
465
+ //
466
+ // If line provided:
467
+ // only update matching declaration line
468
+ // ------------------------------------------------------
469
+ if (line && path.node.loc?.start.line !== line) {
470
+ return;
471
+ }
472
+ // ------------------------------------------------------
473
+ // Replace variable value
474
+ //
475
+ // BEFORE:
476
+ // const count = 0;
477
+ //
478
+ // AFTER:
479
+ // const count = 10;
480
+ // ------------------------------------------------------
481
+ path.node.init = newValueNode;
482
+ },
483
+ });
484
+ // ----------------------------------------------------------
485
+ // STEP 4:
486
+ // Generate updated code from AST
487
+ // ----------------------------------------------------------
488
+ return { success: true, updatedCode: generate(ast).code };
489
+ }
490
+ /**
491
+ * DescriptionForPrompt: Removes a variable declaration from code.
492
+ *
493
+ * ------------------------------------------------------------
494
+ * WHAT THIS FUNCTION DOES
495
+ * ------------------------------------------------------------
496
+ *
497
+ * This function removes variable declarations.
498
+ *
499
+ * Example:
500
+ *
501
+ * BEFORE:
502
+ *
503
+ * const loading = true;
504
+ *
505
+ * AFTER:
506
+ *
507
+ * <removed>
508
+ *
509
+ * ------------------------------------------------------------
510
+ * SUPPORTED DECLARATIONS
511
+ * ------------------------------------------------------------
512
+ *
513
+ * const
514
+ * let
515
+ * var
516
+ *
517
+ * ------------------------------------------------------------
518
+ * IMPORTANT BEHAVIOR
519
+ * ------------------------------------------------------------
520
+ *
521
+ * This function removes:
522
+ *
523
+ * - only the matching variable declarator
524
+ *
525
+ * Example:
526
+ *
527
+ * BEFORE:
528
+ *
529
+ * const a = 1, b = 2;
530
+ *
531
+ * deleteVariable("a")
532
+ *
533
+ * AFTER:
534
+ *
535
+ * const b = 2;
536
+ *
537
+ * ------------------------------------------------------------
538
+ *
539
+ * If all declarators are removed:
540
+ *
541
+ * BEFORE:
542
+ *
543
+ * const a = 1;
544
+ *
545
+ * AFTER:
546
+ *
547
+ * <entire statement removed>
548
+ *
549
+ * ------------------------------------------------------------
550
+ * WHY AST IS IMPORTANT
551
+ * ------------------------------------------------------------
552
+ *
553
+ * Variable deletion using regex/string replacement is fragile.
554
+ *
555
+ * Problems:
556
+ * - multiline declarations
557
+ * - nested scopes
558
+ * - duplicate variable names
559
+ * - comma-separated variables
560
+ *
561
+ * AST guarantees:
562
+ * - syntax-safe deletion
563
+ * - deterministic targeting
564
+ * - scope-aware manipulation
565
+ *
566
+ * Useful commands:
567
+ * - remove loading state: deleteVariable(loading)
568
+ * - delete mock data: deleteVariable(mockData)
569
+ * - remove api url: deleteVariable(API_URL)
570
+ * - clear temporary state: deleteVariable(tempState)
571
+ * - remove unused variable: deleteVariable(variableName)
572
+ *
573
+ * ------------------------------------------------------------
574
+ * OPTIONAL LINE TARGETING
575
+ * ------------------------------------------------------------
576
+ *
577
+ * Multiple variables with same name may exist.
578
+ *
579
+ * Example:
580
+ *
581
+ * const data = 1;
582
+ *
583
+ * function App() {
584
+ * const data = 2;
585
+ * }
586
+ *
587
+ * line allows precise targeting.
588
+ *
589
+ * ------------------------------------------------------------
590
+ * IMPORTANT ARCHITECTURAL IDEA
591
+ * ------------------------------------------------------------
592
+ *
593
+ * This function removes declarations only.
594
+ *
595
+ * It does NOT:
596
+ * - remove usages
597
+ * - remove JSX references
598
+ * - optimize imports
599
+ *
600
+ * Those should be separate cleanup steps.
601
+ *
602
+ * ------------------------------------------------------------
603
+ * PARAMS
604
+ * ------------------------------------------------------------
605
+ *
606
+ * @param {Object} params
607
+ *
608
+ * @param {string} params.code
609
+ * Entire source code.
610
+ *
611
+ * @param {string} params.variableName
612
+ * Variable to remove.
613
+ *
614
+ * @param {number=} params.line
615
+ * Optional declaration line number.
616
+ *
617
+ * ------------------------------------------------------------
618
+ * RETURNS
619
+ * ------------------------------------------------------------
620
+ *
621
+ * @returns {{ updatedCode: string }}
622
+ * Updated source code.
623
+ *
624
+ */
625
+ function deleteVariable({ code, variableName, line }, context) {
626
+ // ----------------------------------------------------------
627
+ // STEP 1:
628
+ // Parse source code into AST
629
+ // ----------------------------------------------------------
630
+ const ast = parse(code, {
631
+ sourceType: "module",
632
+ plugins: ["jsx", "typescript"],
633
+ });
634
+ // ----------------------------------------------------------
635
+ // STEP 2:
636
+ // Traverse AST searching variable declarations
637
+ // ----------------------------------------------------------
638
+ traverse.default(ast, {
639
+ VariableDeclaration(path) {
640
+ // ------------------------------------------------------
641
+ // Filter matching declarators
642
+ //
643
+ // Example:
644
+ //
645
+ // const a = 1, b = 2;
646
+ //
647
+ // removing "a" keeps only "b"
648
+ // ------------------------------------------------------
649
+ const remainingDeclarators = path.node.declarations.filter((declarator) => {
650
+ // ------------------------------------------------
651
+ // Ignore non-Identifier patterns
652
+ //
653
+ // Example:
654
+ // const { a } = obj;
655
+ // ------------------------------------------------
656
+ if (declarator.id.type !== "Identifier") {
657
+ return true;
658
+ }
659
+ // ------------------------------------------------
660
+ // Keep unrelated variables
661
+ // ------------------------------------------------
662
+ if (declarator.id.name !== variableName) {
663
+ return true;
664
+ }
665
+ // ------------------------------------------------
666
+ // Optional line targeting
667
+ //
668
+ // Remove only if line matches
669
+ // ------------------------------------------------
670
+ if (line && declarator.loc?.start.line !== line) {
671
+ return true;
672
+ }
673
+ // ------------------------------------------------
674
+ // Returning false removes declarator
675
+ // ------------------------------------------------
676
+ return false;
677
+ });
678
+ // ------------------------------------------------------
679
+ // If no declarators remain:
680
+ // remove entire variable statement
681
+ //
682
+ // BEFORE:
683
+ //
684
+ // const a = 1;
685
+ //
686
+ // AFTER:
687
+ //
688
+ // <removed>
689
+ // ------------------------------------------------------
690
+ if (remainingDeclarators.length === 0) {
691
+ path.remove();
692
+ return;
693
+ }
694
+ // ------------------------------------------------------
695
+ // Otherwise update remaining declarators
696
+ // ------------------------------------------------------
697
+ path.node.declarations = remainingDeclarators;
698
+ },
699
+ });
700
+ // ----------------------------------------------------------
701
+ // STEP 3:
702
+ // Generate updated code from AST
703
+ // ----------------------------------------------------------
704
+ return { success: true, updatedCode: generate(ast).code };
705
+ }
706
+ /**
707
+ * DescriptionForPrompt: Creates a new function declaration.
708
+ *
709
+ * ------------------------------------------------------------
710
+ * WHAT THIS FUNCTION DOES
711
+ * ------------------------------------------------------------
712
+ *
713
+ * This function creates a standard JavaScript function
714
+ * declaration and inserts it into the file.
715
+ *
716
+ * Example:
717
+ *
718
+ * BEFORE:
719
+ *
720
+ * function App() {
721
+ * return <div />;
722
+ * }
723
+ *
724
+ * AFTER:
725
+ *
726
+ * function formatPrice(price) {
727
+ * return `$${price}`;
728
+ * }
729
+ *
730
+ * function App() {
731
+ * return <div />;
732
+ * }
733
+ *
734
+ * ------------------------------------------------------------
735
+ * WHY AST IS IMPORTANT
736
+ * ------------------------------------------------------------
737
+ *
738
+ * Function creation using string concatenation is fragile.
739
+ *
740
+ * Problems:
741
+ * - invalid syntax
742
+ * - incorrect insertion position
743
+ * - broken formatting
744
+ * - nested scope mistakes
745
+ *
746
+ * AST guarantees:
747
+ * - syntax-safe insertion
748
+ * - deterministic structure
749
+ * - scope awareness
750
+ * - formatting preserved
751
+ *
752
+ * Useful commands:
753
+ * - add submit handler: createFunction(handleSubmit)
754
+ * - create formatter function: createFunction(formatPrice)
755
+ * - add validation logic: createFunction(validateForm)
756
+ * - add api fetch function: createFunction(fetchUsers)
757
+ * - create helper function: createFunction(helper)
758
+ *
759
+ * createFunction
760
+
761
+ insertJSX / updateFunction
762
+
763
+ optimizeImports
764
+ * ------------------------------------------------------------
765
+ * IMPORTANT ARCHITECTURAL CONSTRAINT
766
+ * ------------------------------------------------------------
767
+ *
768
+ * This implementation uses ONLY:
769
+ *
770
+ * function declarations
771
+ *
772
+ * Example:
773
+ *
774
+ * function myFunc() {}
775
+ *
776
+ * NOT:
777
+ *
778
+ * const myFunc = () => {}
779
+ *
780
+ * This keeps architecture deterministic and easy to analyze.
781
+ *
782
+ * ------------------------------------------------------------
783
+ * INSERTION STRATEGY
784
+ * ------------------------------------------------------------
785
+ *
786
+ * Global scope:
787
+ * - inserted after imports
788
+ *
789
+ * Function scope:
790
+ * - inserted inside target function
791
+ *
792
+ * ------------------------------------------------------------
793
+ * FUNCTION BODY
794
+ * ------------------------------------------------------------
795
+ *
796
+ * body should contain raw JavaScript statements.
797
+ *
798
+ * Example:
799
+ *
800
+ * return value * 2;
801
+ *
802
+ * NOT:
803
+ *
804
+ * {
805
+ * return value * 2;
806
+ * }
807
+ *
808
+ * ------------------------------------------------------------
809
+ * PARAMS
810
+ * ------------------------------------------------------------
811
+ *
812
+ * @param {Object} params
813
+ *
814
+ * @param {string} params.code
815
+ * Entire source code.
816
+ *
817
+ * @param {string} params.functionName
818
+ * Name of function to create.
819
+ *
820
+ * @param {string[]} params.params
821
+ * Array of parameter names.
822
+ *
823
+ * Example:
824
+ * ["price", "currency"]
825
+ *
826
+ * @param {string} params.body
827
+ * Raw JavaScript statements.
828
+ *
829
+ * @param {"global"|"function"} params.scope
830
+ * Where function should be inserted.
831
+ *
832
+ * @param {string=} params.parentFunctionName
833
+ * Required if scope === "function"
834
+ *
835
+ * ------------------------------------------------------------
836
+ * RETURNS
837
+ * ------------------------------------------------------------
838
+ *
839
+ * @returns {{ updatedCode: string }}
840
+ * Updated source code.
841
+ *
842
+ */
843
+ function createFunction({ code, functionName, params = [], body = "", scope = "global", parentFunctionName, }) {
844
+ // ----------------------------------------------------------
845
+ // STEP 1:
846
+ // Parse source code into AST
847
+ // ----------------------------------------------------------
848
+ const ast = parse(code, {
849
+ sourceType: "module",
850
+ plugins: ["jsx", "typescript"],
851
+ });
852
+ // ----------------------------------------------------------
853
+ // STEP 2:
854
+ // Convert parameter names into AST identifiers
855
+ //
856
+ // Example:
857
+ //
858
+ // ["price"]
859
+ //
860
+ // becomes:
861
+ //
862
+ // Identifier(price)
863
+ // ----------------------------------------------------------
864
+ const functionParams = params.map((param) => t.identifier(param));
865
+ // ----------------------------------------------------------
866
+ // STEP 3:
867
+ // Parse function body into AST statements
868
+ //
869
+ // WHY?
870
+ //
871
+ // Babel requires valid AST statements inside function body.
872
+ //
873
+ // Example:
874
+ //
875
+ // return value * 2;
876
+ // ----------------------------------------------------------
877
+ const parsedBody = parse(body, {
878
+ sourceType: "module",
879
+ plugins: ["jsx", "typescript"],
880
+ });
881
+ // ----------------------------------------------------------
882
+ // STEP 4:
883
+ // Create FunctionDeclaration AST node
884
+ //
885
+ // Example:
886
+ //
887
+ // function formatPrice(price) {
888
+ // return `$${price}`;
889
+ // }
890
+ // ----------------------------------------------------------
891
+ const functionDeclaration = t.functionDeclaration(t.identifier(functionName), functionParams, t.blockStatement(parsedBody.program.body));
892
+ // ==========================================================
893
+ // GLOBAL SCOPE INSERTION
894
+ // ==========================================================
895
+ if (scope === "global") {
896
+ // --------------------------------------------------------
897
+ // Find insertion point after imports
898
+ // --------------------------------------------------------
899
+ let insertIndex = 0;
900
+ ast.program.body.forEach((node, index) => {
901
+ if (t.isImportDeclaration(node)) {
902
+ insertIndex = index + 1;
903
+ }
904
+ });
905
+ // --------------------------------------------------------
906
+ // Insert function after imports
907
+ // --------------------------------------------------------
908
+ ast.program.body.splice(insertIndex, 0, functionDeclaration);
909
+ }
910
+ // ==========================================================
911
+ // FUNCTION SCOPE INSERTION
912
+ // ==========================================================
913
+ if (scope === "function") {
914
+ // --------------------------------------------------------
915
+ // Traverse AST searching parent function
916
+ // --------------------------------------------------------
917
+ traverse.default(ast, {
918
+ FunctionDeclaration(path) {
919
+ // ----------------------------------------------------
920
+ // Ignore unrelated functions
921
+ // ----------------------------------------------------
922
+ if (path.node.id?.name !== parentFunctionName) {
923
+ return;
924
+ }
925
+ // ----------------------------------------------------
926
+ // Insert child function at top of body
927
+ // ----------------------------------------------------
928
+ path.node.body.body.unshift(functionDeclaration);
929
+ },
930
+ });
931
+ }
932
+ // ----------------------------------------------------------
933
+ // STEP 5:
934
+ // Generate updated code from AST
935
+ // ----------------------------------------------------------
936
+ return { success: true, updatedCode: generate(ast).code };
937
+ }
938
+ /**
939
+ * DescriptionForPrompt: Updates an existing function declaration.
940
+ *
941
+ * ------------------------------------------------------------
942
+ * WHAT THIS FUNCTION DOES
943
+ * ------------------------------------------------------------
944
+ *
945
+ * This function replaces the contents of an existing function.
946
+ *
947
+ * Example:
948
+ *
949
+ * BEFORE:
950
+ *
951
+ * function calculate() {
952
+ * return 1;
953
+ * }
954
+ *
955
+ * AFTER:
956
+ *
957
+ * function calculate() {
958
+ * return 10;
959
+ * }
960
+ *
961
+ * ------------------------------------------------------------
962
+ * IMPORTANT BEHAVIOR
963
+ * ------------------------------------------------------------
964
+ *
965
+ * This function:
966
+ *
967
+ * - preserves function name
968
+ * - preserves parameters
969
+ * - preserves declaration type
970
+ *
971
+ * It ONLY replaces:
972
+ *
973
+ * - function body
974
+ *
975
+ * ------------------------------------------------------------
976
+ * WHY AST IS IMPORTANT
977
+ * ------------------------------------------------------------
978
+ *
979
+ * Function updates using string replacement are dangerous.
980
+ *
981
+ * Problems:
982
+ * - nested braces
983
+ * - multiline formatting
984
+ * - duplicate function names
985
+ * - invalid syntax generation
986
+ *
987
+ * AST guarantees:
988
+ * - structure-aware updates
989
+ * - valid syntax
990
+ * - deterministic replacement
991
+ *
992
+ * Useful commands:
993
+ * - change submit logic: updateFunction(handleSubmit)
994
+ * - update validation function: updateFunction(validateForm)
995
+ * - modify fetch api logic: updateFunction(fetchUsers)
996
+ * - change formatter behavior: updateFunction(formatPrice)
997
+ * - replace helper logic: updateFunction(helper)
998
+ *
999
+ * updateFunction
1000
+
1001
+ updateJSX / optimizeImports
1002
+ * ------------------------------------------------------------
1003
+ * IMPORTANT ARCHITECTURAL CONSTRAINT
1004
+ * ------------------------------------------------------------
1005
+ *
1006
+ * This implementation supports ONLY:
1007
+ *
1008
+ * function declarations
1009
+ *
1010
+ * Example:
1011
+ *
1012
+ * function myFunc() {}
1013
+ *
1014
+ * NOT:
1015
+ *
1016
+ * const myFunc = () => {}
1017
+ *
1018
+ * ------------------------------------------------------------
1019
+ * BODY FORMAT
1020
+ * ------------------------------------------------------------
1021
+ *
1022
+ * body should contain raw JavaScript statements.
1023
+ *
1024
+ * Example:
1025
+ *
1026
+ * return data.map(item => item.name);
1027
+ *
1028
+ * NOT:
1029
+ *
1030
+ * {
1031
+ * return data.map(...);
1032
+ * }
1033
+ *
1034
+ * ------------------------------------------------------------
1035
+ * OPTIONAL LINE TARGETING
1036
+ * ------------------------------------------------------------
1037
+ *
1038
+ * Multiple functions with same name may exist.
1039
+ *
1040
+ * line targeting allows deterministic selection.
1041
+ *
1042
+ * ------------------------------------------------------------
1043
+ * PARAMS
1044
+ * ------------------------------------------------------------
1045
+ *
1046
+ * @param {Object} params
1047
+ *
1048
+ * @param {string} params.code
1049
+ * Entire source code.
1050
+ *
1051
+ * @param {string} params.functionName
1052
+ * Function to update.
1053
+ *
1054
+ * @param {string} params.newBody
1055
+ * New raw JavaScript statements.
1056
+ *
1057
+ * @param {number=} params.line
1058
+ * Optional function declaration line.
1059
+ *
1060
+ * ------------------------------------------------------------
1061
+ * RETURNS
1062
+ * ------------------------------------------------------------
1063
+ *
1064
+ * @returns {{ updatedCode: string }}
1065
+ * Updated source code.
1066
+ *
1067
+ */
1068
+ function updateFunction({ code, functionName, newBody, line, }) {
1069
+ // ----------------------------------------------------------
1070
+ // STEP 1:
1071
+ // Parse source code into AST
1072
+ // ----------------------------------------------------------
1073
+ const ast = parse(code, {
1074
+ sourceType: "module",
1075
+ plugins: ["jsx", "typescript"],
1076
+ });
1077
+ // ----------------------------------------------------------
1078
+ // STEP 2:
1079
+ // Parse new function body into AST statements
1080
+ //
1081
+ // Example:
1082
+ //
1083
+ // return 10;
1084
+ // ----------------------------------------------------------
1085
+ const parsedBody = parse(newBody, {
1086
+ sourceType: "module",
1087
+ plugins: ["jsx", "typescript"],
1088
+ });
1089
+ // ----------------------------------------------------------
1090
+ // STEP 3:
1091
+ // Traverse AST searching function declarations
1092
+ // ----------------------------------------------------------
1093
+ traverse.default(ast, {
1094
+ FunctionDeclaration(path) {
1095
+ // ------------------------------------------------------
1096
+ // Ignore anonymous functions
1097
+ // ------------------------------------------------------
1098
+ if (!path.node.id) {
1099
+ return;
1100
+ }
1101
+ // ------------------------------------------------------
1102
+ // Ignore unrelated functions
1103
+ // ------------------------------------------------------
1104
+ if (path.node.id.name !== functionName) {
1105
+ return;
1106
+ }
1107
+ // ------------------------------------------------------
1108
+ // Optional precise line targeting
1109
+ // ------------------------------------------------------
1110
+ if (line && path.node.loc?.start?.line !== line) {
1111
+ return;
1112
+ }
1113
+ // ------------------------------------------------------
1114
+ // Replace function body
1115
+ //
1116
+ // BEFORE:
1117
+ //
1118
+ // function test() {
1119
+ // return 1;
1120
+ // }
1121
+ //
1122
+ // AFTER:
1123
+ //
1124
+ // function test() {
1125
+ // return 10;
1126
+ // }
1127
+ // ------------------------------------------------------
1128
+ path.node.body = t.blockStatement(parsedBody.program.body);
1129
+ },
1130
+ });
1131
+ // ----------------------------------------------------------
1132
+ // STEP 4:
1133
+ // Generate updated source code from AST
1134
+ // ----------------------------------------------------------
1135
+ return { success: true, updatedCode: generate(ast).code };
1136
+ }
1137
+ /**
1138
+ * DescriptionForPrompt: Removes a function declaration from code.
1139
+ *
1140
+ * ------------------------------------------------------------
1141
+ * WHAT THIS FUNCTION DOES
1142
+ * ------------------------------------------------------------
1143
+ *
1144
+ * This function removes an entire function declaration.
1145
+ *
1146
+ * Example:
1147
+ *
1148
+ * BEFORE:
1149
+ *
1150
+ * function formatPrice(price) {
1151
+ * return `$${price}`;
1152
+ * }
1153
+ *
1154
+ * AFTER:
1155
+ *
1156
+ * <removed>
1157
+ *
1158
+ * ------------------------------------------------------------
1159
+ * IMPORTANT BEHAVIOR
1160
+ * ------------------------------------------------------------
1161
+ *
1162
+ * This function removes:
1163
+ *
1164
+ * - the complete function declaration
1165
+ * - function name
1166
+ * - parameters
1167
+ * - body
1168
+ *
1169
+ * ------------------------------------------------------------
1170
+ * WHY AST IS IMPORTANT
1171
+ * ------------------------------------------------------------
1172
+ *
1173
+ * Function deletion using regex/string replacement is fragile.
1174
+ *
1175
+ * Problems:
1176
+ * - nested braces
1177
+ * - multiline functions
1178
+ * - formatting variations
1179
+ * - duplicate function names
1180
+ *
1181
+ * AST guarantees:
1182
+ * - syntax-safe removal
1183
+ * - deterministic targeting
1184
+ * - structure-aware deletion
1185
+ *
1186
+ * Useful commands:
1187
+ * - remove submit handler: deleteFunction(handleSubmit)
1188
+ * - delete helper function: deleteFunction(helper)
1189
+ * - remove validation logic: deleteFunction(validateForm)
1190
+ * - remove formatter: deleteFunction(formatPrice)
1191
+ * - delete fetch function: deleteFunction(fetchUsers)
1192
+ *
1193
+ * deleteFunction
1194
+
1195
+ remove usages
1196
+
1197
+ optimizeImports
1198
+ * ------------------------------------------------------------
1199
+ * IMPORTANT ARCHITECTURAL CONSTRAINT
1200
+ * ------------------------------------------------------------
1201
+ *
1202
+ * This implementation supports ONLY:
1203
+ *
1204
+ * function declarations
1205
+ *
1206
+ * Example:
1207
+ *
1208
+ * function myFunc() {}
1209
+ *
1210
+ * NOT:
1211
+ *
1212
+ * const myFunc = () => {}
1213
+ *
1214
+ * ------------------------------------------------------------
1215
+ * OPTIONAL LINE TARGETING
1216
+ * ------------------------------------------------------------
1217
+ *
1218
+ * Multiple functions with same name may exist.
1219
+ *
1220
+ * Example:
1221
+ *
1222
+ * function helper() {}
1223
+ *
1224
+ * function helper() {}
1225
+ *
1226
+ * line targeting allows deterministic selection.
1227
+ *
1228
+ * ------------------------------------------------------------
1229
+ * IMPORTANT ARCHITECTURAL IDEA
1230
+ * ------------------------------------------------------------
1231
+ *
1232
+ * This function ONLY removes function declarations.
1233
+ *
1234
+ * It does NOT:
1235
+ * - remove function calls
1236
+ * - remove JSX usage
1237
+ * - remove imports
1238
+ * - optimize imports
1239
+ *
1240
+ * Those should happen in separate cleanup passes.
1241
+ *
1242
+ * ------------------------------------------------------------
1243
+ * PARAMS
1244
+ * ------------------------------------------------------------
1245
+ *
1246
+ * @param {Object} params
1247
+ *
1248
+ * @param {string} params.code
1249
+ * Entire source code.
1250
+ *
1251
+ * @param {string} params.functionName
1252
+ * Name of function to remove.
1253
+ *
1254
+ * @param {number=} params.line
1255
+ * Optional function declaration line.
1256
+ *
1257
+ * ------------------------------------------------------------
1258
+ * RETURNS
1259
+ * ------------------------------------------------------------
1260
+ *
1261
+ * @returns {{ updatedCode: string }}
1262
+ * Updated source code.
1263
+ *
1264
+ */
1265
+ function deleteFunction({ code, functionName, line, }) {
1266
+ // ----------------------------------------------------------
1267
+ // STEP 1:
1268
+ // Parse source code into AST
1269
+ // ----------------------------------------------------------
1270
+ const ast = parse(code, {
1271
+ sourceType: "module",
1272
+ plugins: ["jsx", "typescript"],
1273
+ });
1274
+ // ----------------------------------------------------------
1275
+ // STEP 2:
1276
+ // Traverse AST searching function declarations
1277
+ // ----------------------------------------------------------
1278
+ traverse.default(ast, {
1279
+ FunctionDeclaration(path) {
1280
+ // ------------------------------------------------------
1281
+ // Ignore anonymous functions
1282
+ // ------------------------------------------------------
1283
+ if (!path.node.id) {
1284
+ return;
1285
+ }
1286
+ // ------------------------------------------------------
1287
+ // Ignore unrelated functions
1288
+ // ------------------------------------------------------
1289
+ if (path.node.id.name !== functionName) {
1290
+ return;
1291
+ }
1292
+ // ------------------------------------------------------
1293
+ // Optional precise line targeting
1294
+ //
1295
+ // Useful when duplicate names exist
1296
+ // ------------------------------------------------------
1297
+ if (line && path.node.loc?.start?.line !== line) {
1298
+ return;
1299
+ }
1300
+ // ------------------------------------------------------
1301
+ // Remove entire function declaration
1302
+ //
1303
+ // BEFORE:
1304
+ //
1305
+ // function helper() {}
1306
+ //
1307
+ // AFTER:
1308
+ //
1309
+ // <removed>
1310
+ // ------------------------------------------------------
1311
+ path.remove();
1312
+ },
1313
+ });
1314
+ // ----------------------------------------------------------
1315
+ // STEP 3:
1316
+ // Generate updated source code from AST
1317
+ // ----------------------------------------------------------
1318
+ return { success: true, updatedCode: generate(ast).code };
1319
+ }
1320
+ /**
1321
+ * DescriptionForPrompt: Inserts new JSX into a target component.
1322
+ *
1323
+ * ------------------------------------------------------------
1324
+ * WHAT THIS FUNCTION DOES
1325
+ * ------------------------------------------------------------
1326
+ *
1327
+ * This function inserts JSX elements into:
1328
+ *
1329
+ * - a component's returned JSX
1330
+ * - a target JSX element
1331
+ *
1332
+ * Example:
1333
+ *
1334
+ * BEFORE:
1335
+ *
1336
+ * function Header() {
1337
+ * return (
1338
+ * <header>
1339
+ * <h1>Logo</h1>
1340
+ * </header>
1341
+ * );
1342
+ * }
1343
+ *
1344
+ * AFTER:
1345
+ *
1346
+ * function Header() {
1347
+ * return (
1348
+ * <header>
1349
+ * <h1>Logo</h1>
1350
+ * <button>Sign Up</button>
1351
+ * </header>
1352
+ * );
1353
+ * }
1354
+ *
1355
+ * ------------------------------------------------------------
1356
+ * WHY THIS FUNCTION EXISTS
1357
+ * ------------------------------------------------------------
1358
+ *
1359
+ * In UI generation systems,
1360
+ * users frequently say:
1361
+ *
1362
+ * - "add button"
1363
+ * - "add search input"
1364
+ * - "add hero section"
1365
+ * - "insert navbar"
1366
+ *
1367
+ * These commands usually mean:
1368
+ *
1369
+ * "insert JSX into existing UI"
1370
+ *
1371
+ * NOT:
1372
+ *
1373
+ * "replace entire component"
1374
+ *
1375
+ * ------------------------------------------------------------
1376
+ * WHY AST IS IMPORTANT
1377
+ * ------------------------------------------------------------
1378
+ *
1379
+ * JSX insertion using string replacement is extremely fragile.
1380
+ *
1381
+ * Problems:
1382
+ * - nested JSX
1383
+ * - formatting changes
1384
+ * - self-closing tags
1385
+ * - conditional rendering
1386
+ * - fragments
1387
+ *
1388
+ * AST guarantees:
1389
+ * - valid JSX structure
1390
+ * - deterministic insertion
1391
+ * - syntax-safe manipulation
1392
+ *
1393
+ * Useful commands:
1394
+ * - add sign up button: insertJSX(button)
1395
+ * - add logo in header: insertJSX(img/logo)
1396
+ * - insert search bar: insertJSX(input)
1397
+ * - add navigation links: insertJSX(nav links)
1398
+ * - add hero section: insertJSX(section)
1399
+ *
1400
+ * insertJSX
1401
+
1402
+ ensureImport
1403
+
1404
+ optimizeImports
1405
+ * ------------------------------------------------------------
1406
+ * IMPORTANT ARCHITECTURAL CONSTRAINT
1407
+ * ------------------------------------------------------------
1408
+ *
1409
+ * This implementation assumes:
1410
+ *
1411
+ * - React components use function declarations
1412
+ *
1413
+ * Example:
1414
+ *
1415
+ * function Header() {}
1416
+ *
1417
+ * NOT:
1418
+ *
1419
+ * const Header = () => {}
1420
+ *
1421
+ * ------------------------------------------------------------
1422
+ * INSERTION STRATEGY
1423
+ * ------------------------------------------------------------
1424
+ *
1425
+ * This function inserts JSX:
1426
+ *
1427
+ * - inside a target JSX element
1428
+ *
1429
+ * Example:
1430
+ *
1431
+ * targetElement = "header"
1432
+ *
1433
+ * JSX gets inserted INSIDE:
1434
+ *
1435
+ * <header>
1436
+ * HERE
1437
+ * </header>
1438
+ *
1439
+ * ------------------------------------------------------------
1440
+ * SUPPORTED TARGETS
1441
+ * ------------------------------------------------------------
1442
+ *
1443
+ * HTML tags:
1444
+ * - div
1445
+ * - header
1446
+ * - main
1447
+ * - section
1448
+ *
1449
+ * React components:
1450
+ * - Layout
1451
+ * - Sidebar
1452
+ * - Card
1453
+ *
1454
+ * ------------------------------------------------------------
1455
+ * JSX FORMAT
1456
+ * ------------------------------------------------------------
1457
+ *
1458
+ * jsx should contain valid JSX ONLY.
1459
+ *
1460
+ * Example:
1461
+ *
1462
+ * <button>Login</button>
1463
+ *
1464
+ * NOT:
1465
+ *
1466
+ * return <button />
1467
+ *
1468
+ * ------------------------------------------------------------
1469
+ * IMPORTANT LIMITATION
1470
+ * ------------------------------------------------------------
1471
+ *
1472
+ * This function currently inserts:
1473
+ *
1474
+ * - at end of target children
1475
+ *
1476
+ * Future improvements:
1477
+ * - before element
1478
+ * - after element
1479
+ * - prepend
1480
+ * - replace child
1481
+ *
1482
+ * ------------------------------------------------------------
1483
+ * PARAMS
1484
+ * ------------------------------------------------------------
1485
+ *
1486
+ * @param {Object} params
1487
+ *
1488
+ * @param {string} params.code
1489
+ * Entire source code like this not just jsx
1490
+ * function Header() {
1491
+ * return (
1492
+ * <header>
1493
+ * <h1>Logo</h1>
1494
+ * </header>
1495
+ * );
1496
+ * }
1497
+ *
1498
+ * @param {string} params.componentName
1499
+ * Component containing JSX.
1500
+ *
1501
+ * @param {string} params.targetElement
1502
+ * JSX element where content should be inserted.
1503
+ *
1504
+ * Example:
1505
+ * "header"
1506
+ *
1507
+ * @param {string} params.jsx
1508
+ * Only JSX to insert.
1509
+ * <button>Login</button>
1510
+ *
1511
+ * ------------------------------------------------------------
1512
+ * RETURNS
1513
+ * ------------------------------------------------------------
1514
+ *
1515
+ * @returns {{ updatedCode: string }}
1516
+ * Updated source code.
1517
+ *
1518
+ */
1519
+ function insertJSX({ code, componentName, targetElement, jsx, position }, context) {
1520
+ // const updatedCode = await formatCode(code);
1521
+ // return { success: true, updatedCode };
1522
+ // ----------------------------------------------------------
1523
+ // STEP 1:
1524
+ // Parse source code into AST
1525
+ // ----------------------------------------------------------
1526
+ const ast = parse(code, {
1527
+ sourceType: "module",
1528
+ plugins: ["jsx", "typescript"],
1529
+ });
1530
+ // ----------------------------------------------------------
1531
+ // STEP 2:
1532
+ // Parse JSX snippet into AST node
1533
+ //
1534
+ // Example:
1535
+ //
1536
+ // <button>Login</button>
1537
+ // ----------------------------------------------------------
1538
+ const jsxNode = parseExpression(jsx, {
1539
+ plugins: ["jsx"],
1540
+ });
1541
+ // ----------------------------------------------------------
1542
+ // STEP 3:
1543
+ // Traverse AST searching target component
1544
+ // ----------------------------------------------------------
1545
+ traverse.default(ast, {
1546
+ FunctionDeclaration(path) {
1547
+ // ------------------------------------------------------
1548
+ // Ignore unrelated components
1549
+ // ------------------------------------------------------
1550
+ if (path.node.id?.name !== componentName) {
1551
+ return;
1552
+ }
1553
+ // ------------------------------------------------------
1554
+ // Traverse inside component body
1555
+ // ------------------------------------------------------
1556
+ path.traverse({
1557
+ JSXElement(jsxPath) {
1558
+ // --------------------------------------------------
1559
+ // Get opening tag name
1560
+ //
1561
+ // Example:
1562
+ //
1563
+ // <header>
1564
+ // ^
1565
+ // --------------------------------------------------
1566
+ const openingElement = jsxPath.node.openingElement;
1567
+ // --------------------------------------------------
1568
+ // Ignore unsupported tag types
1569
+ // --------------------------------------------------
1570
+ if (!t.isJSXIdentifier(openingElement.name)) {
1571
+ return;
1572
+ }
1573
+ // --------------------------------------------------
1574
+ // Ignore unrelated JSX elements
1575
+ // --------------------------------------------------
1576
+ if (!helpers.matchesSelector({
1577
+ openingElement,
1578
+ selector: targetElement,
1579
+ })) {
1580
+ return;
1581
+ }
1582
+ // --------------------------------------------------
1583
+ // Insert JSX according to requested position
1584
+ //
1585
+ // first:
1586
+ //
1587
+ // <header>
1588
+ // <button />
1589
+ // <h1 />
1590
+ // </header>
1591
+ //
1592
+ // last:
1593
+ //
1594
+ // <header>
1595
+ // <h1 />
1596
+ // <button />
1597
+ // </header>
1598
+ // --------------------------------------------------
1599
+ if (position === "first") {
1600
+ jsxPath.node.children.unshift(t.jsxExpressionContainer(jsxNode), t.jsxText("\n"));
1601
+ }
1602
+ else {
1603
+ jsxPath.node.children.push(t.jsxText("\n"), t.jsxExpressionContainer(jsxNode));
1604
+ }
1605
+ // --------------------------------------------------
1606
+ // Stop traversal after insertion
1607
+ // --------------------------------------------------
1608
+ jsxPath.stop();
1609
+ },
1610
+ });
1611
+ },
1612
+ });
1613
+ // ----------------------------------------------------------
1614
+ // STEP 4:
1615
+ // Generate updated source code from AST
1616
+ // ----------------------------------------------------------
1617
+ const updatedCode = utils.formatCode(generate(ast).code);
1618
+ return { success: true, updatedCode };
1619
+ }
1620
+ /**
1621
+ * DescriptionForPrompt: Replaces an existing JSX element with new JSX.
1622
+ *
1623
+ * ------------------------------------------------------------
1624
+ * WHAT THIS FUNCTION DOES
1625
+ * ------------------------------------------------------------
1626
+ *
1627
+ * This function searches for a JSX element and completely
1628
+ * replaces it with new JSX.
1629
+ *
1630
+ * Example:
1631
+ *
1632
+ * BEFORE:
1633
+ *
1634
+ * <button>Login</button>
1635
+ *
1636
+ * AFTER:
1637
+ *
1638
+ * <button className="primary">
1639
+ * Sign Up
1640
+ * </button>
1641
+ *
1642
+ * ------------------------------------------------------------
1643
+ * IMPORTANT DIFFERENCE
1644
+ * ------------------------------------------------------------
1645
+ *
1646
+ * insertJSX
1647
+ * → inserts INSIDE existing JSX
1648
+ *
1649
+ * replaceJSX
1650
+ * → completely replaces existing JSX node
1651
+ *
1652
+ * ------------------------------------------------------------
1653
+ * EXAMPLE
1654
+ * ------------------------------------------------------------
1655
+ *
1656
+ * BEFORE:
1657
+ *
1658
+ * function Header() {
1659
+ * return (
1660
+ * <header>
1661
+ * <button>Login</button>
1662
+ * </header>
1663
+ * );
1664
+ * }
1665
+ *
1666
+ * AFTER:
1667
+ *
1668
+ * function Header() {
1669
+ * return (
1670
+ * <header>
1671
+ * <button className="primary">
1672
+ * Sign Up
1673
+ * </button>
1674
+ * </header>
1675
+ * );
1676
+ * }
1677
+ *
1678
+ * ------------------------------------------------------------
1679
+ * WHY THIS FUNCTION EXISTS
1680
+ * ------------------------------------------------------------
1681
+ *
1682
+ * Users frequently give commands like:
1683
+ *
1684
+ * - "replace login button"
1685
+ * - "change navbar into sidebar"
1686
+ * - "replace div with section"
1687
+ * - "make button outlined"
1688
+ *
1689
+ * These commands usually mean:
1690
+ *
1691
+ * "replace existing JSX element"
1692
+ *
1693
+ * NOT:
1694
+ *
1695
+ * "insert new JSX"
1696
+ *
1697
+ * ------------------------------------------------------------
1698
+ * WHY AST IS IMPORTANT
1699
+ * ------------------------------------------------------------
1700
+ *
1701
+ * JSX replacement using string replacement is fragile.
1702
+ *
1703
+ * Problems:
1704
+ * - nested JSX
1705
+ * - duplicate elements
1706
+ * - multiline formatting
1707
+ * - invalid syntax generation
1708
+ * - conditional rendering
1709
+ *
1710
+ * AST guarantees:
1711
+ * - valid JSX
1712
+ * - deterministic replacement
1713
+ * - structure-aware manipulation
1714
+ *
1715
+ * ------------------------------------------------------------
1716
+ * IMPORTANT ARCHITECTURAL CONSTRAINT
1717
+ * ------------------------------------------------------------
1718
+ *
1719
+ * This implementation assumes:
1720
+ *
1721
+ * - React components use function declarations
1722
+ *
1723
+ * Example:
1724
+ *
1725
+ * function Header() {}
1726
+ *
1727
+ * NOT:
1728
+ *
1729
+ * const Header = () => {}
1730
+ *
1731
+ * ------------------------------------------------------------
1732
+ * REPLACEMENT STRATEGY
1733
+ * ------------------------------------------------------------
1734
+ *
1735
+ * This function replaces:
1736
+ *
1737
+ * - first matching JSX element
1738
+ *
1739
+ * Example:
1740
+ *
1741
+ * targetElement = "button"
1742
+ *
1743
+ * FIRST:
1744
+ *
1745
+ * <button />
1746
+ *
1747
+ * gets replaced.
1748
+ *
1749
+ * ------------------------------------------------------------
1750
+ * SUPPORTED TARGETS
1751
+ * ------------------------------------------------------------
1752
+ *
1753
+ * HTML tags:
1754
+ * - div
1755
+ * - button
1756
+ * - header
1757
+ * - section
1758
+ *
1759
+ * React components:
1760
+ * - Card
1761
+ * - Sidebar
1762
+ * - Layout
1763
+ *
1764
+ * ------------------------------------------------------------
1765
+ * JSX FORMAT
1766
+ * ------------------------------------------------------------
1767
+ *
1768
+ * newJSX should contain valid JSX ONLY.
1769
+ *
1770
+ * Example:
1771
+ *
1772
+ * <button>Save</button>
1773
+ *
1774
+ * NOT:
1775
+ *
1776
+ * return <button />
1777
+ *
1778
+ * Useful commands:
1779
+ * - replace login button with signup button: replaceJSX(button)
1780
+ * - change div to section: replaceJSX(div)
1781
+ * - replace navbar with sidebar: replaceJSX(Navbar)
1782
+ * - make button outlined: replaceJSX(button)
1783
+ * - replace image with video: replaceJSX(img)
1784
+ *
1785
+ * replaceJSX
1786
+
1787
+ ensureImport
1788
+
1789
+ optimizeImports
1790
+ * ------------------------------------------------------------
1791
+ * IMPORTANT LIMITATION
1792
+ * ------------------------------------------------------------
1793
+ *
1794
+ * Current implementation:
1795
+ *
1796
+ * - replaces first matching JSX node only
1797
+ *
1798
+ * Future improvements:
1799
+ * - replace nth match
1800
+ * - replace by attribute
1801
+ * - replace by text content
1802
+ * - replace multiple nodes
1803
+ *
1804
+ * ------------------------------------------------------------
1805
+ * PARAMS
1806
+ * ------------------------------------------------------------
1807
+ *
1808
+ * @param {Object} params
1809
+ *
1810
+ * @param {string} params.code
1811
+ * Entire source code.
1812
+ *
1813
+ * @param {string} params.componentName
1814
+ * Component containing JSX.
1815
+ *
1816
+ * @param {string} params.targetElement
1817
+ * JSX element to replace.
1818
+ *
1819
+ * Example:
1820
+ * "button"
1821
+ *
1822
+ * @param {string} params.newJSX
1823
+ * Replacement JSX.
1824
+ *
1825
+ * ------------------------------------------------------------
1826
+ * RETURNS
1827
+ * ------------------------------------------------------------
1828
+ *
1829
+ * @returns {{ updatedCode: string }}
1830
+ * Updated source code.
1831
+ *
1832
+ */
1833
+ function replaceJSX({ code, componentName, targetElement, newJSX }, context) {
1834
+ // ----------------------------------------------------------
1835
+ // STEP 1:
1836
+ // Parse source code into AST
1837
+ // ----------------------------------------------------------
1838
+ const ast = parse(code, {
1839
+ sourceType: "module",
1840
+ plugins: ["jsx", "typescript"],
1841
+ });
1842
+ // ----------------------------------------------------------
1843
+ // STEP 2:
1844
+ // Parse replacement JSX into AST node
1845
+ //
1846
+ // Example:
1847
+ //
1848
+ // <button>Save</button>
1849
+ // ----------------------------------------------------------
1850
+ const replacementNode = parseExpression(newJSX, {
1851
+ plugins: ["jsx"],
1852
+ });
1853
+ // ----------------------------------------------------------
1854
+ // STEP 3:
1855
+ // Track whether replacement already happened
1856
+ //
1857
+ // WHY?
1858
+ //
1859
+ // Prevent replacing multiple nodes accidentally.
1860
+ // ----------------------------------------------------------
1861
+ let replaced = false;
1862
+ // ----------------------------------------------------------
1863
+ // STEP 4:
1864
+ // Traverse AST searching component
1865
+ // ----------------------------------------------------------
1866
+ traverse.default(ast, {
1867
+ FunctionDeclaration(path) {
1868
+ // ------------------------------------------------------
1869
+ // Ignore unrelated components
1870
+ // ------------------------------------------------------
1871
+ if (path.node.id?.name !== componentName) {
1872
+ return;
1873
+ }
1874
+ // ------------------------------------------------------
1875
+ // Traverse JSX inside component
1876
+ // ------------------------------------------------------
1877
+ path.traverse({
1878
+ JSXElement(jsxPath) {
1879
+ // --------------------------------------------------
1880
+ // Stop if replacement already done
1881
+ // --------------------------------------------------
1882
+ if (replaced) {
1883
+ return;
1884
+ }
1885
+ // --------------------------------------------------
1886
+ // Get opening JSX tag
1887
+ //
1888
+ // Example:
1889
+ //
1890
+ // <button>
1891
+ // ^
1892
+ // --------------------------------------------------
1893
+ const openingElement = jsxPath.node.openingElement;
1894
+ // --------------------------------------------------
1895
+ // Ignore unsupported tag types
1896
+ // --------------------------------------------------
1897
+ if (!t.isJSXIdentifier(openingElement.name)) {
1898
+ return;
1899
+ }
1900
+ // --------------------------------------------------
1901
+ // Ignore unrelated JSX elements
1902
+ // --------------------------------------------------
1903
+ if (!helpers.matchesSelector({
1904
+ openingElement,
1905
+ selector: targetElement,
1906
+ })) {
1907
+ return;
1908
+ }
1909
+ // --------------------------------------------------
1910
+ // Replace JSX node
1911
+ //
1912
+ // BEFORE:
1913
+ //
1914
+ // <button>Login</button>
1915
+ //
1916
+ // AFTER:
1917
+ //
1918
+ // <button>Save</button>
1919
+ // --------------------------------------------------
1920
+ jsxPath.replaceWith(replacementNode);
1921
+ // --------------------------------------------------
1922
+ // Mark replacement completed
1923
+ // --------------------------------------------------
1924
+ replaced = true;
1925
+ // --------------------------------------------------
1926
+ // Stop traversal for performance/safety
1927
+ // --------------------------------------------------
1928
+ jsxPath.stop();
1929
+ },
1930
+ });
1931
+ },
1932
+ });
1933
+ // ----------------------------------------------------------
1934
+ // STEP 5:
1935
+ // Generate updated source code from AST
1936
+ // ----------------------------------------------------------
1937
+ return { success: true, updatedCode: generate(ast).code };
1938
+ }
1939
+ /**
1940
+ * DescriptionForPrompt: Removes a JSX element from a component.
1941
+ *
1942
+ * ------------------------------------------------------------
1943
+ * WHAT THIS FUNCTION DOES
1944
+ * ------------------------------------------------------------
1945
+ *
1946
+ * This function searches for a JSX element and removes it
1947
+ * completely from the JSX tree.
1948
+ *
1949
+ * Example:
1950
+ *
1951
+ * BEFORE:
1952
+ *
1953
+ * <header>
1954
+ * <Logo />
1955
+ * <button>Login</button>
1956
+ * </header>
1957
+ *
1958
+ * AFTER:
1959
+ *
1960
+ * <header>
1961
+ * <Logo />
1962
+ * </header>
1963
+ *
1964
+ * ------------------------------------------------------------
1965
+ * IMPORTANT DIFFERENCE
1966
+ * ------------------------------------------------------------
1967
+ *
1968
+ * insertJSX
1969
+ * → adds JSX
1970
+ *
1971
+ * replaceJSX
1972
+ * → swaps JSX
1973
+ *
1974
+ * removeJSX
1975
+ * → deletes JSX
1976
+ *
1977
+ * ------------------------------------------------------------
1978
+ * WHY THIS FUNCTION EXISTS
1979
+ * ------------------------------------------------------------
1980
+ *
1981
+ * Users frequently give commands like:
1982
+ *
1983
+ * - "remove login button"
1984
+ * - "delete hero section"
1985
+ * - "remove image"
1986
+ * - "remove sidebar"
1987
+ * - "hide footer"
1988
+ *
1989
+ * These commands usually mean:
1990
+ *
1991
+ * "remove JSX element from UI tree"
1992
+ *
1993
+ * NOT:
1994
+ *
1995
+ * "delete component file"
1996
+ *
1997
+ * ------------------------------------------------------------
1998
+ * WHY AST IS IMPORTANT
1999
+ * ------------------------------------------------------------
2000
+ *
2001
+ * JSX removal using string replacement is fragile.
2002
+ *
2003
+ * Problems:
2004
+ * - nested JSX
2005
+ * - multiline formatting
2006
+ * - duplicate elements
2007
+ * - invalid JSX after deletion
2008
+ * - conditional rendering
2009
+ *
2010
+ * AST guarantees:
2011
+ * - syntax-safe deletion
2012
+ * - deterministic traversal
2013
+ * - structure-aware removal
2014
+ *
2015
+ * ------------------------------------------------------------
2016
+ * IMPORTANT ARCHITECTURAL CONSTRAINT
2017
+ * ------------------------------------------------------------
2018
+ *
2019
+ * This implementation assumes:
2020
+ *
2021
+ * - React components use function declarations
2022
+ *
2023
+ * Example:
2024
+ *
2025
+ * function Header() {}
2026
+ *
2027
+ * NOT:
2028
+ *
2029
+ * const Header = () => {}
2030
+ *
2031
+ * ------------------------------------------------------------
2032
+ * REMOVAL STRATEGY
2033
+ * ------------------------------------------------------------
2034
+ *
2035
+ * This function removes:
2036
+ *
2037
+ * - first matching JSX element
2038
+ *
2039
+ * Example:
2040
+ *
2041
+ * targetElement = "button"
2042
+ *
2043
+ * FIRST:
2044
+ *
2045
+ * <button />
2046
+ *
2047
+ * gets removed.
2048
+ *
2049
+ * ------------------------------------------------------------
2050
+ * SUPPORTED TARGETS
2051
+ * ------------------------------------------------------------
2052
+ *
2053
+ * HTML tags:
2054
+ * - div
2055
+ * - button
2056
+ * - img
2057
+ * - section
2058
+ *
2059
+ * React components:
2060
+ * - Sidebar
2061
+ * - Card
2062
+ * - Navbar
2063
+ *
2064
+ * ------------------------------------------------------------
2065
+ * IMPORTANT LIMITATION
2066
+ * ------------------------------------------------------------
2067
+ *
2068
+ * Current implementation:
2069
+ *
2070
+ * - removes first matching JSX node only
2071
+ *
2072
+ * Future improvements:
2073
+ * - remove nth match
2074
+ * - remove by className
2075
+ * - remove by text content
2076
+ * - remove multiple nodes
2077
+ *
2078
+ * Useful commands:
2079
+ * - remove login button: removeJSX(button)
2080
+ * - delete hero section: removeJSX(section)
2081
+ * - remove sidebar: removeJSX(Sidebar)
2082
+ * - hide footer links: removeJSX(footer)
2083
+ * - remove profile image: removeJSX(img)
2084
+ * ------------------------------------------------------------
2085
+ * IMPORTANT ARCHITECTURAL IDEA
2086
+ * ------------------------------------------------------------
2087
+ *
2088
+ * removeJSX should usually be followed by:
2089
+ *
2090
+ * - removeImport
2091
+ * - optimizeImports
2092
+ *
2093
+ * if deleted JSX used imported components.
2094
+ *
2095
+ * ------------------------------------------------------------
2096
+ * PARAMS
2097
+ * ------------------------------------------------------------
2098
+ *
2099
+ * @param {Object} params
2100
+ *
2101
+ * @param {string} params.code
2102
+ * Entire source code.
2103
+ *
2104
+ * @param {string} params.componentName
2105
+ * Component containing JSX.
2106
+ *
2107
+ * @param {string} params.targetElement
2108
+ * JSX element to remove.
2109
+ *
2110
+ * Example:
2111
+ * "button"
2112
+ *
2113
+ * ------------------------------------------------------------
2114
+ * RETURNS
2115
+ * ------------------------------------------------------------
2116
+ *
2117
+ * @returns {{ updatedCode: string }}
2118
+ * Updated source code.
2119
+ *
2120
+ */
2121
+ function removeJSX({ code, componentName, targetElement }, context) {
2122
+ // ----------------------------------------------------------
2123
+ // STEP 1:
2124
+ // Parse source code into AST
2125
+ // ----------------------------------------------------------
2126
+ const ast = parse(code, {
2127
+ sourceType: "module",
2128
+ plugins: ["jsx", "typescript"],
2129
+ });
2130
+ // ----------------------------------------------------------
2131
+ // STEP 2:
2132
+ // Track whether removal already happened
2133
+ //
2134
+ // WHY?
2135
+ //
2136
+ // Prevent accidental removal of multiple elements.
2137
+ // ----------------------------------------------------------
2138
+ let removed = false;
2139
+ // ----------------------------------------------------------
2140
+ // STEP 3:
2141
+ // Traverse AST searching target component
2142
+ // ----------------------------------------------------------
2143
+ traverse.default(ast, {
2144
+ FunctionDeclaration(path) {
2145
+ // ------------------------------------------------------
2146
+ // Ignore unrelated components
2147
+ // ------------------------------------------------------
2148
+ if (path.node.id?.name !== componentName) {
2149
+ return;
2150
+ }
2151
+ // ------------------------------------------------------
2152
+ // Traverse JSX inside component
2153
+ // ------------------------------------------------------
2154
+ path.traverse({
2155
+ JSXElement(jsxPath) {
2156
+ // --------------------------------------------------
2157
+ // Stop if removal already completed
2158
+ // --------------------------------------------------
2159
+ if (removed) {
2160
+ return;
2161
+ }
2162
+ // --------------------------------------------------
2163
+ // Get opening JSX element
2164
+ //
2165
+ // Example:
2166
+ //
2167
+ // <button>
2168
+ // ^
2169
+ // --------------------------------------------------
2170
+ const openingElement = jsxPath.node.openingElement;
2171
+ // --------------------------------------------------
2172
+ // Ignore unsupported tag types
2173
+ // --------------------------------------------------
2174
+ if (!t.isJSXIdentifier(openingElement.name)) {
2175
+ return;
2176
+ }
2177
+ // --------------------------------------------------
2178
+ // Ignore unrelated JSX elements
2179
+ // --------------------------------------------------
2180
+ // if (openingElement.name.name !== targetElement) {
2181
+ // return;
2182
+ // }
2183
+ if (!helpers.matchesSelector({
2184
+ openingElement,
2185
+ selector: targetElement,
2186
+ })) {
2187
+ return;
2188
+ }
2189
+ // --------------------------------------------------
2190
+ // Remove JSX node
2191
+ //
2192
+ // BEFORE:
2193
+ //
2194
+ // <button>Login</button>
2195
+ //
2196
+ // AFTER:
2197
+ //
2198
+ // <removed>
2199
+ // --------------------------------------------------
2200
+ jsxPath.remove();
2201
+ // --------------------------------------------------
2202
+ // Mark removal completed
2203
+ // --------------------------------------------------
2204
+ removed = true;
2205
+ // --------------------------------------------------
2206
+ // Stop traversal for performance/safety
2207
+ // --------------------------------------------------
2208
+ jsxPath.stop();
2209
+ },
2210
+ });
2211
+ },
2212
+ });
2213
+ // ----------------------------------------------------------
2214
+ // STEP 4:
2215
+ // Generate updated source code from AST
2216
+ // ----------------------------------------------------------
2217
+ return { success: true, updatedCode: generate(ast).code };
2218
+ }
2219
+ /**
2220
+ * DescriptionForPrompt: Wraps a JSX element with a parent JSX element.
2221
+ *
2222
+ * ------------------------------------------------------------
2223
+ * WHAT THIS FUNCTION DOES
2224
+ * ------------------------------------------------------------
2225
+ *
2226
+ * This function takes an existing JSX element and wraps it
2227
+ * inside a new parent JSX element.
2228
+ *
2229
+ * Example:
2230
+ *
2231
+ * BEFORE:
2232
+ *
2233
+ * <button>Login</button>
2234
+ *
2235
+ * AFTER:
2236
+ *
2237
+ * <div className="container">
2238
+ * <button>Login</button>
2239
+ * </div>
2240
+ *
2241
+ * ------------------------------------------------------------
2242
+ * IMPORTANT DIFFERENCE
2243
+ * ------------------------------------------------------------
2244
+ *
2245
+ * insertJSX
2246
+ * → adds new JSX
2247
+ *
2248
+ * replaceJSX
2249
+ * → replaces JSX
2250
+ *
2251
+ * removeJSX
2252
+ * → deletes JSX
2253
+ *
2254
+ * wrapJSX
2255
+ * → nests existing JSX inside another JSX
2256
+ *
2257
+ * ------------------------------------------------------------
2258
+ * WHY THIS FUNCTION EXISTS
2259
+ * ------------------------------------------------------------
2260
+ *
2261
+ * Users frequently give commands like:
2262
+ *
2263
+ * - "wrap button in div"
2264
+ * - "put card inside container"
2265
+ * - "wrap navbar in header"
2266
+ * - "add layout wrapper"
2267
+ * - "center this section"
2268
+ *
2269
+ * These commands usually mean:
2270
+ *
2271
+ * "preserve existing JSX but add parent wrapper"
2272
+ *
2273
+ * ------------------------------------------------------------
2274
+ * WHY AST IS IMPORTANT
2275
+ * ------------------------------------------------------------
2276
+ *
2277
+ * JSX wrapping using string replacement is fragile.
2278
+ *
2279
+ * Problems:
2280
+ * - nested JSX
2281
+ * - malformed closing tags
2282
+ * - multiline formatting
2283
+ * - fragments
2284
+ * - conditional rendering
2285
+ *
2286
+ * AST guarantees:
2287
+ * - valid JSX structure
2288
+ * - syntax-safe wrapping
2289
+ * - deterministic nesting
2290
+ *
2291
+ * ------------------------------------------------------------
2292
+ * IMPORTANT ARCHITECTURAL CONSTRAINT
2293
+ * ------------------------------------------------------------
2294
+ *
2295
+ * This implementation assumes:
2296
+ *
2297
+ * - React components use function declarations
2298
+ *
2299
+ * Example:
2300
+ *
2301
+ * function Header() {}
2302
+ *
2303
+ * NOT:
2304
+ *
2305
+ * const Header = () => {}
2306
+ *
2307
+ * ------------------------------------------------------------
2308
+ * WRAPPING STRATEGY
2309
+ * ------------------------------------------------------------
2310
+ *
2311
+ * This function:
2312
+ *
2313
+ * - finds first matching JSX element
2314
+ * - inserts that JSX inside wrapper element
2315
+ *
2316
+ * Example:
2317
+ *
2318
+ * targetElement = "button"
2319
+ *
2320
+ * wrapperJSX =
2321
+ *
2322
+ * <div className="container"></div>
2323
+ *
2324
+ * RESULT:
2325
+ *
2326
+ * <div className="container">
2327
+ * <button />
2328
+ * </div>
2329
+ *
2330
+ *
2331
+ * Useful commands:
2332
+ * - wrap button in div: wrapJSX(button)
2333
+ * - put navbar inside header: wrapJSX(Navbar)
2334
+ * - wrap hero section in container: wrapJSX(section)
2335
+ * - add layout wrapper: wrapJSX(main)
2336
+ * - center this card: wrapJSX(Card)
2337
+ *
2338
+ * wrapJSX
2339
+
2340
+ ensureImport
2341
+
2342
+ optimizeImports
2343
+ *
2344
+ * ------------------------------------------------------------
2345
+ * IMPORTANT LIMITATION
2346
+ * ------------------------------------------------------------
2347
+ *
2348
+ * Current implementation:
2349
+ *
2350
+ * - wraps first matching node only
2351
+ *
2352
+ * Future improvements:
2353
+ * - wrap multiple nodes
2354
+ * - wrap by className
2355
+ * - wrap sibling groups
2356
+ * - wrap conditional JSX
2357
+ *
2358
+ * ------------------------------------------------------------
2359
+ * WRAPPER FORMAT
2360
+ * ------------------------------------------------------------
2361
+ *
2362
+ * wrapperJSX should be:
2363
+ *
2364
+ * valid empty JSX container
2365
+ *
2366
+ * Example:
2367
+ *
2368
+ * <div className="wrapper"></div>
2369
+ *
2370
+ * NOT:
2371
+ *
2372
+ * <div>
2373
+ * Something
2374
+ * </div>
2375
+ *
2376
+ * because children will be injected automatically.
2377
+ *
2378
+ * ------------------------------------------------------------
2379
+ * PARAMS
2380
+ * ------------------------------------------------------------
2381
+ *
2382
+ * @param {Object} params
2383
+ *
2384
+ * @param {string} params.code
2385
+ * Entire source code.
2386
+ *
2387
+ * @param {string} params.componentName
2388
+ * Component containing JSX.
2389
+ *
2390
+ * @param {string} params.targetElement
2391
+ * JSX element to wrap.
2392
+ *
2393
+ * Example:
2394
+ * "button"
2395
+ *
2396
+ * @param {string} params.wrapperJSX
2397
+ * Parent wrapper JSX.
2398
+ *
2399
+ * Example:
2400
+ * '<div className="container"></div>'
2401
+ *
2402
+ * ------------------------------------------------------------
2403
+ * RETURNS
2404
+ * ------------------------------------------------------------
2405
+ *
2406
+ * @returns {{ updatedCode: string }}
2407
+ * Updated source code.
2408
+ *
2409
+ */
2410
+ function wrapJSX({ code, componentName, targetElement, wrapperJSX }, context) {
2411
+ // ----------------------------------------------------------
2412
+ // STEP 1:
2413
+ // Parse source code into AST
2414
+ // ----------------------------------------------------------
2415
+ const ast = parse(code, {
2416
+ sourceType: "module",
2417
+ plugins: ["jsx", "typescript"],
2418
+ });
2419
+ // ----------------------------------------------------------
2420
+ // STEP 2:
2421
+ // Parse wrapper JSX into AST node
2422
+ //
2423
+ // Example:
2424
+ //
2425
+ // <div className="wrapper"></div>
2426
+ // ----------------------------------------------------------
2427
+ const wrapperNode = parseExpression(wrapperJSX, {
2428
+ plugins: ["jsx"],
2429
+ });
2430
+ // ----------------------------------------------------------
2431
+ // STEP 3:
2432
+ // Ensure wrapper is JSX element
2433
+ // ----------------------------------------------------------
2434
+ if (!t.isJSXElement(wrapperNode)) {
2435
+ throw new LoomaError(ERROR_CODES.INVALID_JSX, "wrapperJSX must be a valid JSX element");
2436
+ }
2437
+ // ----------------------------------------------------------
2438
+ // STEP 4:
2439
+ // Track whether wrapping already happened
2440
+ // ----------------------------------------------------------
2441
+ let wrapped = false;
2442
+ // ----------------------------------------------------------
2443
+ // STEP 5:
2444
+ // Traverse AST searching target component
2445
+ // ----------------------------------------------------------
2446
+ traverse.default(ast, {
2447
+ FunctionDeclaration(path) {
2448
+ // ------------------------------------------------------
2449
+ // Ignore unrelated components
2450
+ // ------------------------------------------------------
2451
+ if (path.node.id?.name !== componentName) {
2452
+ return;
2453
+ }
2454
+ // ------------------------------------------------------
2455
+ // Traverse JSX inside component
2456
+ // ------------------------------------------------------
2457
+ path.traverse({
2458
+ JSXElement(jsxPath) {
2459
+ // --------------------------------------------------
2460
+ // Stop if wrapping already completed
2461
+ // --------------------------------------------------
2462
+ if (wrapped) {
2463
+ return;
2464
+ }
2465
+ // --------------------------------------------------
2466
+ // Get opening JSX tag
2467
+ // --------------------------------------------------
2468
+ const openingElement = jsxPath.node.openingElement;
2469
+ // --------------------------------------------------
2470
+ // Ignore unsupported tag types
2471
+ // --------------------------------------------------
2472
+ if (!t.isJSXIdentifier(openingElement.name)) {
2473
+ return;
2474
+ }
2475
+ // --------------------------------------------------
2476
+ // Ignore unrelated JSX elements
2477
+ // --------------------------------------------------
2478
+ if (!helpers.matchesSelector({
2479
+ openingElement,
2480
+ selector: targetElement,
2481
+ })) {
2482
+ return;
2483
+ }
2484
+ // --------------------------------------------------
2485
+ // Clone wrapper node
2486
+ //
2487
+ // WHY?
2488
+ //
2489
+ // Prevent AST reference mutation issues.
2490
+ // --------------------------------------------------
2491
+ const wrapperClone = t.cloneNode(wrapperNode, true);
2492
+ // --------------------------------------------------
2493
+ // Inject existing JSX inside wrapper
2494
+ //
2495
+ // BEFORE:
2496
+ //
2497
+ // <div className="wrapper"></div>
2498
+ //
2499
+ // AFTER:
2500
+ //
2501
+ // <div className="wrapper">
2502
+ // <button />
2503
+ // </div>
2504
+ // --------------------------------------------------
2505
+ wrapperClone.children = [
2506
+ t.jsxText("\n"),
2507
+ jsxPath.node,
2508
+ t.jsxText("\n"),
2509
+ ];
2510
+ // --------------------------------------------------
2511
+ // Replace original JSX with wrapped JSX
2512
+ // --------------------------------------------------
2513
+ jsxPath.replaceWith(wrapperClone);
2514
+ // --------------------------------------------------
2515
+ // Mark wrapping completed
2516
+ // --------------------------------------------------
2517
+ wrapped = true;
2518
+ // --------------------------------------------------
2519
+ // Stop traversal for safety/performance
2520
+ // --------------------------------------------------
2521
+ jsxPath.stop();
2522
+ },
2523
+ });
2524
+ },
2525
+ });
2526
+ // ----------------------------------------------------------
2527
+ // STEP 6:
2528
+ // Generate updated source code from AST
2529
+ // ----------------------------------------------------------
2530
+ return { success: true, updatedCode: generate(ast).code };
2531
+ }
2532
+ /**
2533
+ * DescriptionForPrompt: Moves a JSX element to a different location in the JSX tree.
2534
+ *
2535
+ * ------------------------------------------------------------
2536
+ * WHAT THIS FUNCTION DOES
2537
+ * ------------------------------------------------------------
2538
+ *
2539
+ * This function:
2540
+ *
2541
+ * 1. Finds a JSX element
2542
+ * 2. Removes it from current location
2543
+ * 3. Inserts it inside another JSX element
2544
+ *
2545
+ * Example:
2546
+ *
2547
+ * BEFORE:
2548
+ *
2549
+ * <header>
2550
+ * <button>Login</button>
2551
+ * </header>
2552
+ *
2553
+ * <footer></footer>
2554
+ *
2555
+ * AFTER:
2556
+ *
2557
+ * <header></header>
2558
+ *
2559
+ * <footer>
2560
+ * <button>Login</button>
2561
+ * </footer>
2562
+ *
2563
+ * ------------------------------------------------------------
2564
+ * IMPORTANT DIFFERENCE
2565
+ * ------------------------------------------------------------
2566
+ *
2567
+ * insertJSX
2568
+ * → creates new JSX
2569
+ *
2570
+ * replaceJSX
2571
+ * → swaps JSX
2572
+ *
2573
+ * removeJSX
2574
+ * → deletes JSX
2575
+ *
2576
+ * wrapJSX
2577
+ * → nests JSX
2578
+ *
2579
+ * moveJSX
2580
+ * → relocates existing JSX
2581
+ *
2582
+ * ------------------------------------------------------------
2583
+ * WHY THIS FUNCTION EXISTS
2584
+ * ------------------------------------------------------------
2585
+ *
2586
+ * Users frequently give commands like:
2587
+ *
2588
+ * - "move login button to footer"
2589
+ * - "put navbar inside sidebar"
2590
+ * - "move search bar to header"
2591
+ * - "shift button below form"
2592
+ * - "move logo to left section"
2593
+ *
2594
+ * These commands usually mean:
2595
+ *
2596
+ * "preserve existing JSX but change location"
2597
+ *
2598
+ * ------------------------------------------------------------
2599
+ * WHY AST IS IMPORTANT
2600
+ * ------------------------------------------------------------
2601
+ *
2602
+ * JSX movement using string replacement is fragile.
2603
+ *
2604
+ * Problems:
2605
+ * - nested JSX
2606
+ * - invalid nesting
2607
+ * - multiline formatting
2608
+ * - duplicate elements
2609
+ * - broken parent structure
2610
+ *
2611
+ * AST guarantees:
2612
+ * - syntax-safe movement
2613
+ * - deterministic relocation
2614
+ * - structure-aware manipulation
2615
+ *
2616
+ * ------------------------------------------------------------
2617
+ * IMPORTANT ARCHITECTURAL CONSTRAINT
2618
+ * ------------------------------------------------------------
2619
+ *
2620
+ * This implementation assumes:
2621
+ *
2622
+ * - React components use function declarations
2623
+ *
2624
+ * Example:
2625
+ *
2626
+ * function Header() {}
2627
+ *
2628
+ * NOT:
2629
+ *
2630
+ * const Header = () => {}
2631
+ *
2632
+ * ------------------------------------------------------------
2633
+ * MOVEMENT STRATEGY
2634
+ * ------------------------------------------------------------
2635
+ *
2636
+ * This function:
2637
+ *
2638
+ * - removes first matching JSX node
2639
+ * - inserts it inside destination element
2640
+ *
2641
+ * Example:
2642
+ *
2643
+ * sourceElement = "button"
2644
+ * destinationElement = "footer"
2645
+ *
2646
+ * RESULT:
2647
+ *
2648
+ * <footer>
2649
+ * <button />
2650
+ * </footer>
2651
+ *
2652
+ * Useful commands:
2653
+ * - move login button to footer: moveJSX(button → footer)
2654
+ * - move search bar into header: moveJSX(input → header)
2655
+ * - put navbar inside sidebar: moveJSX(Navbar → Sidebar)
2656
+ * - move logo to left section: moveJSX(Logo → div)
2657
+ * - shift button below form: moveJSX(button → form)
2658
+ *
2659
+ * moveJSX
2660
+
2661
+ optimizeImports
2662
+ * ------------------------------------------------------------
2663
+ * IMPORTANT LIMITATION
2664
+ * ------------------------------------------------------------
2665
+ *
2666
+ * Current implementation:
2667
+ *
2668
+ * - moves first matching node only
2669
+ *
2670
+ * Future improvements:
2671
+ * - move nth node
2672
+ * - move before/after sibling
2673
+ * - move by className
2674
+ * - reorder sibling elements
2675
+ *
2676
+ * ------------------------------------------------------------
2677
+ * PARAMS
2678
+ * ------------------------------------------------------------
2679
+ *
2680
+ * @param {Object} params
2681
+ *
2682
+ * @param {string} params.code
2683
+ * Entire source code.
2684
+ *
2685
+ * @param {string} params.componentName
2686
+ * Component containing JSX.
2687
+ *
2688
+ * @param {string} params.sourceElement
2689
+ * JSX element to move.
2690
+ *
2691
+ * Example:
2692
+ * "button"
2693
+ *
2694
+ * @param {string} params.destinationElement
2695
+ * JSX destination parent.
2696
+ *
2697
+ * Example:
2698
+ * "footer"
2699
+ *
2700
+ * ------------------------------------------------------------
2701
+ * RETURNS
2702
+ * ------------------------------------------------------------
2703
+ *
2704
+ * @returns {{ updatedCode: string }}
2705
+ * Updated source code.
2706
+ *
2707
+ */
2708
+ function moveJSX({ code, componentName, sourceElement, destinationElement }, context) {
2709
+ // ----------------------------------------------------------
2710
+ // STEP 1:
2711
+ // Parse source code into AST
2712
+ // ----------------------------------------------------------
2713
+ const ast = parse(code, {
2714
+ sourceType: "module",
2715
+ plugins: ["jsx", "typescript"],
2716
+ });
2717
+ // ----------------------------------------------------------
2718
+ // STEP 2:
2719
+ // Store JSX node that will be moved
2720
+ // ----------------------------------------------------------
2721
+ let nodeToMove = null;
2722
+ // ----------------------------------------------------------
2723
+ // STEP 3:
2724
+ // Track movement completion
2725
+ // ----------------------------------------------------------
2726
+ let moved = false;
2727
+ // ----------------------------------------------------------
2728
+ // STEP 4:
2729
+ // Traverse AST searching target component
2730
+ // ----------------------------------------------------------
2731
+ traverse.default(ast, {
2732
+ FunctionDeclaration(path) {
2733
+ // ------------------------------------------------------
2734
+ // Ignore unrelated components
2735
+ // ------------------------------------------------------
2736
+ if (path.node.id?.name !== componentName) {
2737
+ return;
2738
+ }
2739
+ // ======================================================
2740
+ // PHASE 1:
2741
+ // FIND + REMOVE SOURCE ELEMENT
2742
+ // ======================================================
2743
+ path.traverse({
2744
+ JSXElement(jsxPath) {
2745
+ // --------------------------------------------------
2746
+ // Stop if source already found
2747
+ // --------------------------------------------------
2748
+ if (nodeToMove) {
2749
+ return;
2750
+ }
2751
+ // --------------------------------------------------
2752
+ // Get opening JSX element
2753
+ // --------------------------------------------------
2754
+ const openingElement = jsxPath.node.openingElement;
2755
+ // --------------------------------------------------
2756
+ // Ignore unsupported tag types
2757
+ // --------------------------------------------------
2758
+ if (!t.isJSXIdentifier(openingElement.name)) {
2759
+ return;
2760
+ }
2761
+ // --------------------------------------------------
2762
+ // Ignore unrelated JSX elements
2763
+ // --------------------------------------------------
2764
+ if (openingElement.name.name !== sourceElement) {
2765
+ return;
2766
+ }
2767
+ // --------------------------------------------------
2768
+ // Clone node before removal
2769
+ //
2770
+ // WHY?
2771
+ //
2772
+ // Removing node destroys original reference.
2773
+ // --------------------------------------------------
2774
+ nodeToMove = t.cloneNode(jsxPath.node, true);
2775
+ // --------------------------------------------------
2776
+ // Remove original JSX node
2777
+ // --------------------------------------------------
2778
+ jsxPath.remove();
2779
+ },
2780
+ });
2781
+ // ======================================================
2782
+ // PHASE 2:
2783
+ // INSERT INTO DESTINATION
2784
+ // ======================================================
2785
+ if (!nodeToMove) {
2786
+ return;
2787
+ }
2788
+ path.traverse({
2789
+ JSXElement(jsxPath) {
2790
+ // --------------------------------------------------
2791
+ // Stop if movement already completed
2792
+ // --------------------------------------------------
2793
+ if (moved) {
2794
+ return;
2795
+ }
2796
+ // --------------------------------------------------
2797
+ // Get opening JSX element
2798
+ // --------------------------------------------------
2799
+ const openingElement = jsxPath.node.openingElement;
2800
+ // --------------------------------------------------
2801
+ // Ignore unsupported tag types
2802
+ // --------------------------------------------------
2803
+ if (!t.isJSXIdentifier(openingElement.name)) {
2804
+ return;
2805
+ }
2806
+ // --------------------------------------------------
2807
+ // Ignore unrelated destination elements
2808
+ // --------------------------------------------------
2809
+ if (openingElement.name.name !== destinationElement) {
2810
+ return;
2811
+ }
2812
+ // --------------------------------------------------
2813
+ // Insert moved JSX inside destination
2814
+ //
2815
+ // BEFORE:
2816
+ //
2817
+ // <footer></footer>
2818
+ //
2819
+ // AFTER:
2820
+ //
2821
+ // <footer>
2822
+ // <button />
2823
+ // </footer>
2824
+ // --------------------------------------------------
2825
+ if (nodeToMove) {
2826
+ jsxPath.node.children.push(t.jsxText("\n"), nodeToMove);
2827
+ }
2828
+ // --------------------------------------------------
2829
+ // Mark movement completed
2830
+ // --------------------------------------------------
2831
+ moved = true;
2832
+ // --------------------------------------------------
2833
+ // Stop traversal for safety/performance
2834
+ // --------------------------------------------------
2835
+ jsxPath.stop();
2836
+ },
2837
+ });
2838
+ },
2839
+ });
2840
+ // ----------------------------------------------------------
2841
+ // STEP 5:
2842
+ // Generate updated source code from AST
2843
+ // ----------------------------------------------------------
2844
+ return { success: true, updatedCode: generate(ast).code };
2845
+ }
2846
+ /**
2847
+ * DescriptionForPrompt: Inserts new CSS styles into a component style file.
2848
+ *
2849
+ * ------------------------------------------------------------
2850
+ * WHAT THIS FUNCTION DOES
2851
+ * ------------------------------------------------------------
2852
+ *
2853
+ * This function appends new css styles into:
2854
+ *
2855
+ * Component.css
2856
+ *
2857
+ * Example:
2858
+ *
2859
+ * BEFORE:
2860
+ *
2861
+ * .header {
2862
+ * padding: 16px;
2863
+ * }
2864
+ *
2865
+ * AFTER:
2866
+ *
2867
+ * .header {
2868
+ * padding: 16px;
2869
+ * }
2870
+ *
2871
+ * .header-title {
2872
+ * color: red;
2873
+ * }
2874
+ *
2875
+ * ------------------------------------------------------------
2876
+ * WHY THIS FUNCTION EXISTS
2877
+ * ------------------------------------------------------------
2878
+ *
2879
+ * Looma frequently needs to:
2880
+ *
2881
+ * - add new styles
2882
+ * - add responsive rules
2883
+ * - add hover effects
2884
+ * - add utility classes
2885
+ * - add animations
2886
+ *
2887
+ * Instead of replacing entire css file,
2888
+ * this function safely appends styles.
2889
+ *
2890
+ * Useful in commands like:
2891
+
2892
+ make header red
2893
+ add hover effect
2894
+ add responsive styles
2895
+ add animation
2896
+ style this button
2897
+ increase card padding
2898
+ make navbar sticky
2899
+
2900
+ Usually called after:
2901
+
2902
+ ensureStyleFile()
2903
+
2904
+ Usually used before:
2905
+
2906
+ optimizeStyles()
2907
+ formatStyles()
2908
+ *
2909
+ * ------------------------------------------------------------
2910
+ * IMPORTANT DESIGN DECISION
2911
+ * ------------------------------------------------------------
2912
+ *
2913
+ * This function:
2914
+ *
2915
+ * ONLY inserts styles.
2916
+ *
2917
+ * It does NOT:
2918
+ *
2919
+ * - update existing styles
2920
+ * - remove styles
2921
+ * - optimize styles
2922
+ * - merge selectors
2923
+ *
2924
+ * Those should be handled by separate tasks.
2925
+ *
2926
+ * ------------------------------------------------------------
2927
+ * DEPENDENCY
2928
+ * ------------------------------------------------------------
2929
+ *
2930
+ * This function assumes:
2931
+ *
2932
+ * ensureStyleFile()
2933
+ *
2934
+ * has already been executed.
2935
+ *
2936
+ * Meaning:
2937
+ *
2938
+ * css file already exists.
2939
+ *
2940
+ * ------------------------------------------------------------
2941
+ * PARAMS
2942
+ * ------------------------------------------------------------
2943
+ *
2944
+ * @param {Object} params
2945
+ *
2946
+ * @param {string} params.cssPath
2947
+ * Path of css file.
2948
+ *
2949
+ * Example:
2950
+ *
2951
+ * "./src/components/Header/Header.css"
2952
+ *
2953
+ * @param {string} params.styles
2954
+ * CSS styles to insert.
2955
+ *
2956
+ * Example:
2957
+ *
2958
+ * `
2959
+ * .header-title {
2960
+ * color: red;
2961
+ * }
2962
+ * `
2963
+ *
2964
+ * @param {boolean} [params.addNewLine=true]
2965
+ * Whether to insert spacing before styles.
2966
+ *
2967
+ * ------------------------------------------------------------
2968
+ * RETURNS
2969
+ * ------------------------------------------------------------
2970
+ *
2971
+ * @returns {Object}
2972
+ *
2973
+ * {
2974
+ * inserted: boolean,
2975
+ * cssPath: string
2976
+ * }
2977
+ *
2978
+ */
2979
+ // function insertStyles({ cssPath, styles, addNewLine = true }) {
2980
+ // // ----------------------------------------------------------
2981
+ // // STEP 1:
2982
+ // // Validate css file existence
2983
+ // // ----------------------------------------------------------
2984
+ // if (!fs.existsSync(cssPath)) {
2985
+ // throw new Error(`CSS file does not exist: ${cssPath}`);
2986
+ // }
2987
+ // // ----------------------------------------------------------
2988
+ // // STEP 2:
2989
+ // // Read existing css content
2990
+ // // ----------------------------------------------------------
2991
+ // const existingCss = fs.readFileSync(cssPath, "utf8");
2992
+ // // ----------------------------------------------------------
2993
+ // // STEP 3:
2994
+ // // Prepare final styles content
2995
+ // //
2996
+ // // Adds spacing before inserted styles
2997
+ // // for readability.
2998
+ // // ----------------------------------------------------------
2999
+ // const finalStyles = addNewLine ? `\n\n${styles}` : styles;
3000
+ // // ----------------------------------------------------------
3001
+ // // STEP 4:
3002
+ // // Append styles to existing css
3003
+ // // ----------------------------------------------------------
3004
+ // const updatedCss = existingCss + finalStyles;
3005
+ // // ----------------------------------------------------------
3006
+ // // STEP 5:
3007
+ // // Write updated css back to file
3008
+ // // ----------------------------------------------------------
3009
+ // fs.writeFileSync(cssPath, updatedCss, "utf8");
3010
+ // // ----------------------------------------------------------
3011
+ // // STEP 6:
3012
+ // // Return operation result
3013
+ // // ----------------------------------------------------------
3014
+ // return {
3015
+ // success: true,
3016
+ // cssPath,
3017
+ // };
3018
+ // }
3019
+ /**
3020
+ * Removes imports from a source/module.
3021
+ *
3022
+ * ------------------------------------------------------------
3023
+ * WHAT THIS FUNCTION DOES
3024
+ * ------------------------------------------------------------
3025
+ *
3026
+ * This function removes:
3027
+ *
3028
+ * - default imports
3029
+ * - named imports
3030
+ * - namespace imports
3031
+ *
3032
+ * from a specific source.
3033
+ *
3034
+ * ------------------------------------------------------------
3035
+ * IMPORTANT BEHAVIOR
3036
+ * ------------------------------------------------------------
3037
+ *
3038
+ * This function intelligently handles:
3039
+ *
3040
+ * 1) removing only a specific named import
3041
+ *
3042
+ * BEFORE:
3043
+ * import React, { useState, useEffect } from "react";
3044
+ *
3045
+ * AFTER:
3046
+ * import React, { useEffect } from "react";
3047
+ *
3048
+ * ------------------------------------------------------------
3049
+ *
3050
+ * 2) removing entire import declaration if empty
3051
+ *
3052
+ * BEFORE:
3053
+ * import { useState } from "react";
3054
+ *
3055
+ * AFTER:
3056
+ * <removed entirely>
3057
+ *
3058
+ * ------------------------------------------------------------
3059
+ *
3060
+ * 3) removing default import
3061
+ *
3062
+ * BEFORE:
3063
+ * import React from "react";
3064
+ *
3065
+ * AFTER:
3066
+ * <removed entirely>
3067
+ *
3068
+ * ------------------------------------------------------------
3069
+ *
3070
+ * 4) removing namespace import
3071
+ *
3072
+ * BEFORE:
3073
+ * import * as React from "react";
3074
+ *
3075
+ * AFTER:
3076
+ * <removed entirely>
3077
+ *
3078
+ * ------------------------------------------------------------
3079
+ * WHY AST IS IMPORTANT
3080
+ * ------------------------------------------------------------
3081
+ *
3082
+ * Imports are syntax structures.
3083
+ *
3084
+ * Using string replacement is fragile because:
3085
+ * - formatting changes break logic
3086
+ * - multiline imports become difficult
3087
+ * - commas/braces become error-prone
3088
+ *
3089
+ * AST manipulation is:
3090
+ * - deterministic
3091
+ * - formatting-safe
3092
+ * - syntax-aware
3093
+ *
3094
+ * ------------------------------------------------------------
3095
+ * EXAMPLES
3096
+ * ------------------------------------------------------------
3097
+ *
3098
+ * removeImport({
3099
+ * code,
3100
+ * source: "react",
3101
+ * importName: "useState",
3102
+ * importType: "named"
3103
+ * });
3104
+ *
3105
+ * ------------------------------------------------------------
3106
+ *
3107
+ * removeImport({
3108
+ * code,
3109
+ * source: "react",
3110
+ * importType: "default"
3111
+ * });
3112
+ *
3113
+ * ------------------------------------------------------------
3114
+ * PARAMS
3115
+ * ------------------------------------------------------------
3116
+ *
3117
+ * @param {Object} params
3118
+ *
3119
+ * @param {string} params.code
3120
+ * Entire source code.
3121
+ *
3122
+ * @param {string} params.source
3123
+ * Import source/package.
3124
+ *
3125
+ * Example:
3126
+ * "react"
3127
+ * "./Header"
3128
+ *
3129
+ * @param {string=} params.importName
3130
+ * Required only for named imports.
3131
+ *
3132
+ * Example:
3133
+ * "useState"
3134
+ *
3135
+ * @param {"default"|"named"|"namespace"} params.importType
3136
+ * Type of import to remove.
3137
+ *
3138
+ * ------------------------------------------------------------
3139
+ * RETURNS
3140
+ * ------------------------------------------------------------
3141
+ *
3142
+ * @returns {{updatedCode: string}}
3143
+ * Updated source code.
3144
+ *
3145
+ */
3146
+ function removeImport({ code, source, importName, importType }, context) {
3147
+ // ----------------------------------------------------------
3148
+ // STEP 1:
3149
+ // Parse source code into AST
3150
+ //
3151
+ // sourceType: "module"
3152
+ // enables ES module parsing
3153
+ // ----------------------------------------------------------
3154
+ const ast = parse(code, {
3155
+ sourceType: "module",
3156
+ plugins: ["jsx", "typescript"],
3157
+ });
3158
+ // ----------------------------------------------------------
3159
+ // STEP 2:
3160
+ // Traverse AST looking for import declarations
3161
+ // ----------------------------------------------------------
3162
+ traverse.default(ast, {
3163
+ ImportDeclaration(path) {
3164
+ // ------------------------------------------------------
3165
+ // Ignore unrelated imports
3166
+ //
3167
+ // Example:
3168
+ //
3169
+ // import React from "react"
3170
+ //
3171
+ // source.value = "react"
3172
+ // ------------------------------------------------------
3173
+ if (path.node.source.value !== source) {
3174
+ return;
3175
+ }
3176
+ // ------------------------------------------------------
3177
+ // Get all import specifiers
3178
+ //
3179
+ // Example:
3180
+ //
3181
+ // import React, { useState } from "react"
3182
+ //
3183
+ // specifiers:
3184
+ // - ImportDefaultSpecifier
3185
+ // - ImportSpecifier
3186
+ // ------------------------------------------------------
3187
+ let specifiers = path.node.specifiers;
3188
+ // ======================================================
3189
+ // REMOVE DEFAULT IMPORT
3190
+ // ======================================================
3191
+ if (importType === "default") {
3192
+ // ----------------------------------------------------
3193
+ // Remove ImportDefaultSpecifier
3194
+ // ----------------------------------------------------
3195
+ specifiers = specifiers.filter((specifier) => !t.isImportDefaultSpecifier(specifier));
3196
+ }
3197
+ // ======================================================
3198
+ // REMOVE NAMED IMPORT
3199
+ // ======================================================
3200
+ if (importType === "named") {
3201
+ // ----------------------------------------------------
3202
+ // Remove matching named import
3203
+ //
3204
+ // BEFORE:
3205
+ // import { useState, useEffect }
3206
+ //
3207
+ // AFTER:
3208
+ // import { useEffect }
3209
+ // ----------------------------------------------------
3210
+ specifiers = specifiers.filter((specifier) => {
3211
+ // keep non-named specifiers
3212
+ if (!t.isImportSpecifier(specifier)) {
3213
+ return true;
3214
+ }
3215
+ // remove matching named import
3216
+ return (t.isIdentifier(specifier.imported) &&
3217
+ specifier.imported.name !== importName);
3218
+ });
3219
+ }
3220
+ // ======================================================
3221
+ // REMOVE NAMESPACE IMPORT
3222
+ // ======================================================
3223
+ if (importType === "namespace") {
3224
+ // ----------------------------------------------------
3225
+ // Remove namespace specifier
3226
+ //
3227
+ // import * as React from "react"
3228
+ // ----------------------------------------------------
3229
+ specifiers = specifiers.filter((specifier) => !t.isImportNamespaceSpecifier(specifier));
3230
+ }
3231
+ // ------------------------------------------------------
3232
+ // STEP 3:
3233
+ // If no specifiers remain:
3234
+ // remove entire import declaration
3235
+ //
3236
+ // Example:
3237
+ //
3238
+ // import React from "react"
3239
+ //
3240
+ // becomes:
3241
+ // <removed>
3242
+ // ------------------------------------------------------
3243
+ if (specifiers.length === 0) {
3244
+ path.remove();
3245
+ return;
3246
+ }
3247
+ // ------------------------------------------------------
3248
+ // STEP 4:
3249
+ // Otherwise update remaining specifiers
3250
+ // ------------------------------------------------------
3251
+ path.node.specifiers = specifiers;
3252
+ },
3253
+ });
3254
+ // ----------------------------------------------------------
3255
+ // STEP 5:
3256
+ // Generate updated source code from AST
3257
+ // ----------------------------------------------------------
3258
+ return { success: true, updatedCode: generate(ast).code };
3259
+ }
3260
+ /**
3261
+ * Ensures that an import exists in a file.
3262
+ *
3263
+ * ------------------------------------------------------------
3264
+ * WHAT THIS FUNCTION DOES
3265
+ * ------------------------------------------------------------
3266
+ *
3267
+ * This function guarantees that a specific import exists.
3268
+ *
3269
+ * If import already exists:
3270
+ * - enrich existing import declaration
3271
+ *
3272
+ * If import does not exist:
3273
+ * - create a new import declaration
3274
+ *
3275
+ * ------------------------------------------------------------
3276
+ * Useful commands
3277
+ * ------------------------------------------------------------
3278
+ * - add state: ensureImport(useState)
3279
+ * - add routing: ensureImport(BrowserRouter)
3280
+ * - add header component: ensureImport(Header)
3281
+ * - add animation: ensureImport(motion)
3282
+ * - use useEffect: ensureImport(useEffect)
3283
+ * - add icons: ensureImport(Menu)
3284
+ *
3285
+ * ------------------------------------------------------------
3286
+ * WHY "ENSURE" IS IMPORTANT
3287
+ * ------------------------------------------------------------
3288
+ *
3289
+ * A naive system blindly inserts imports:
3290
+ *
3291
+ * import React from "react";
3292
+ * import { useState } from "react";
3293
+ *
3294
+ * Over time this causes:
3295
+ * - duplicate imports
3296
+ * - fragmented imports
3297
+ * - unstable formatting
3298
+ *
3299
+ * ensureImport prevents this problem.
3300
+ *
3301
+ * ------------------------------------------------------------
3302
+ * EXAMPLE
3303
+ * ------------------------------------------------------------
3304
+ *
3305
+ * ensureImport({
3306
+ * code,
3307
+ * source: "react",
3308
+ * importName: "useState",
3309
+ * importType: "named"
3310
+ * });
3311
+ *
3312
+ * RESULT:
3313
+ *
3314
+ * import React, { useState } from "react";
3315
+ *
3316
+ * ------------------------------------------------------------
3317
+ * SUPPORTED IMPORT TYPES
3318
+ * ------------------------------------------------------------
3319
+ *
3320
+ * default:
3321
+ * import React from "react";
3322
+ *
3323
+ * named:
3324
+ * import { useState } from "react";
3325
+ *
3326
+ * namespace:
3327
+ * import * as React from "react";
3328
+ *
3329
+ * ------------------------------------------------------------
3330
+ * PARAMS
3331
+ * ------------------------------------------------------------
3332
+ *
3333
+ * @param {Object} params
3334
+ *
3335
+ * @param {string} params.code
3336
+ * Entire source code of file.
3337
+ *
3338
+ * @param {string} params.source
3339
+ * Import source/package.
3340
+ *
3341
+ * Example:
3342
+ * "react"
3343
+ * "./Header"
3344
+ *
3345
+ * @param {string} params.importName
3346
+ * Name of imported symbol.
3347
+ *
3348
+ * Example:
3349
+ * "useState"
3350
+ * "React"
3351
+ *
3352
+ * @param {"default"|"named"|"namespace"} params.importType
3353
+ * Type of import.
3354
+ *
3355
+ * @param {string=} params.alias
3356
+ * Optional alias name.
3357
+ *
3358
+ * Example:
3359
+ * import { useState as customState }
3360
+ *
3361
+ * ------------------------------------------------------------
3362
+ * RETURNS
3363
+ * ------------------------------------------------------------
3364
+ *
3365
+ * @returns {{updatedCode: string}}
3366
+ * Updated source code.
3367
+ *
3368
+ */
3369
+ function ensureImport({ code, source, importName, importType, alias }, context) {
3370
+ // ----------------------------------------------------------
3371
+ // STEP 1:
3372
+ // Parse source code into AST
3373
+ //
3374
+ // WHY?
3375
+ //
3376
+ // AST lets us manipulate imports safely instead of using
3377
+ // fragile string replacement.
3378
+ // ----------------------------------------------------------
3379
+ const ast = parse(code, {
3380
+ sourceType: "module",
3381
+ plugins: ["jsx", "typescript"],
3382
+ });
3383
+ // ----------------------------------------------------------
3384
+ // STEP 2:
3385
+ // Track whether matching source import already exists
3386
+ //
3387
+ // Example:
3388
+ //
3389
+ // import React from "react"
3390
+ //
3391
+ // sourceExists -> true
3392
+ // ----------------------------------------------------------
3393
+ let sourceExists = false;
3394
+ // ----------------------------------------------------------
3395
+ // STEP 3:
3396
+ // Traverse AST to search existing imports
3397
+ // ----------------------------------------------------------
3398
+ traverse.default(ast, {
3399
+ ImportDeclaration(path) {
3400
+ // ------------------------------------------------------
3401
+ // Compare import source
3402
+ //
3403
+ // Example:
3404
+ //
3405
+ // import React from "react"
3406
+ // ^^^^^^^
3407
+ // ------------------------------------------------------
3408
+ if (path.node.source.value === source) {
3409
+ sourceExists = true;
3410
+ }
3411
+ },
3412
+ });
3413
+ // ----------------------------------------------------------
3414
+ // STEP 4:
3415
+ // If source import already exists:
3416
+ // enrich existing import declaration
3417
+ //
3418
+ // Example:
3419
+ //
3420
+ // BEFORE:
3421
+ // import React from "react";
3422
+ //
3423
+ // AFTER:
3424
+ // import React, { useState } from "react";
3425
+ // ----------------------------------------------------------
3426
+ if (sourceExists) {
3427
+ let modified = false;
3428
+ traverse.default(ast, {
3429
+ ImportDeclaration(path) {
3430
+ if (path.node.source.value !== source)
3431
+ return;
3432
+ const specifiers = path.node.specifiers;
3433
+ // =========================
3434
+ // DEFAULT IMPORT
3435
+ // import React from "react"
3436
+ // =========================
3437
+ if (importType === "default") {
3438
+ const hasDefault = specifiers.some((s) => t.isImportDefaultSpecifier(s));
3439
+ if (!hasDefault) {
3440
+ specifiers.unshift(t.importDefaultSpecifier(t.identifier(alias || importName)));
3441
+ modified = true;
3442
+ }
3443
+ }
3444
+ // =========================
3445
+ // NAMED IMPORT
3446
+ // import { useState } from "react"
3447
+ // =========================
3448
+ if (importType === "named") {
3449
+ // namespace import already covers all
3450
+ const hasNamespace = specifiers.some((s) => t.isImportNamespaceSpecifier(s));
3451
+ if (hasNamespace)
3452
+ return;
3453
+ const hasNamed = specifiers.some((s) => t.isImportSpecifier(s) &&
3454
+ t.isIdentifier(s.imported) &&
3455
+ s.imported.name === importName);
3456
+ if (!hasNamed) {
3457
+ specifiers.push(t.importSpecifier(t.identifier(alias || importName), t.identifier(importName)));
3458
+ modified = true;
3459
+ }
3460
+ }
3461
+ // =========================
3462
+ // NAMESPACE IMPORT
3463
+ // import * as React from "react"
3464
+ // =========================
3465
+ if (importType === "namespace") {
3466
+ const hasNamespace = specifiers.some((s) => t.isImportNamespaceSpecifier(s));
3467
+ if (!hasNamespace) {
3468
+ path.node.specifiers = [
3469
+ t.importNamespaceSpecifier(t.identifier(alias || importName)),
3470
+ ];
3471
+ modified = true;
3472
+ }
3473
+ }
3474
+ },
3475
+ });
3476
+ if (!modified)
3477
+ return { success: true, updatedCode: code };
3478
+ return { success: true, updatedCode: generate(ast).code };
3479
+ }
3480
+ // ----------------------------------------------------------
3481
+ // STEP 5:
3482
+ // Create import specifier based on import type
3483
+ //
3484
+ // Different import types require different AST nodes.
3485
+ // ----------------------------------------------------------
3486
+ let specifier;
3487
+ // ----------------------------------------------------------
3488
+ // DEFAULT IMPORT
3489
+ //
3490
+ // import React from "react"
3491
+ // ----------------------------------------------------------
3492
+ if (importType === "default") {
3493
+ specifier = t.importDefaultSpecifier(t.identifier(alias || importName));
3494
+ }
3495
+ // ----------------------------------------------------------
3496
+ // NAMED IMPORT
3497
+ //
3498
+ // import { useState } from "react"
3499
+ // ----------------------------------------------------------
3500
+ if (importType === "named") {
3501
+ specifier = t.importSpecifier(t.identifier(alias || importName), t.identifier(importName));
3502
+ }
3503
+ // ----------------------------------------------------------
3504
+ // NAMESPACE IMPORT
3505
+ //
3506
+ // import * as React from "react"
3507
+ // ----------------------------------------------------------
3508
+ if (importType === "namespace") {
3509
+ specifier = t.importNamespaceSpecifier(t.identifier(alias || importName));
3510
+ }
3511
+ // ----------------------------------------------------------
3512
+ // STEP 6:
3513
+ // Create new ImportDeclaration AST node
3514
+ // ----------------------------------------------------------
3515
+ const importDeclaration = t.importDeclaration([specifier], t.stringLiteral(source));
3516
+ // ----------------------------------------------------------
3517
+ // STEP 7:
3518
+ // Insert import at top of file
3519
+ //
3520
+ // unshift():
3521
+ // inserts at beginning of array
3522
+ // ----------------------------------------------------------
3523
+ ast.program.body.unshift(importDeclaration);
3524
+ // ----------------------------------------------------------
3525
+ // STEP 8:
3526
+ // Generate updated code from AST
3527
+ // ----------------------------------------------------------
3528
+ return { success: true, updatedCode: generate(ast).code };
3529
+ }
3530
+ /**
3531
+ * Removes unused imports from a file.
3532
+ *
3533
+ * ------------------------------------------------------------
3534
+ * WHAT THIS FUNCTION DOES
3535
+ * ------------------------------------------------------------
3536
+ *
3537
+ * This function analyzes the entire file and removes imports
3538
+ * that are not used anywhere in the code.
3539
+ *
3540
+ * It supports:
3541
+ *
3542
+ * - default imports
3543
+ * - named imports
3544
+ * - namespace imports
3545
+ *
3546
+ * ------------------------------------------------------------
3547
+ * EXAMPLE
3548
+ * ------------------------------------------------------------
3549
+ *
3550
+ * BEFORE:
3551
+ *
3552
+ * import React from "react";
3553
+ * import { useState, useEffect } from "react";
3554
+ *
3555
+ * function App() {
3556
+ * const [count] = useState(0);
3557
+ * return <div>{count}</div>;
3558
+ * }
3559
+ *
3560
+ * AFTER:
3561
+ *
3562
+ * import { useState } from "react";
3563
+ *
3564
+ * function App() {
3565
+ * const [count] = useState(0);
3566
+ * return <div>{count}</div>;
3567
+ * }
3568
+ *
3569
+ * ------------------------------------------------------------
3570
+ * WHY THIS FUNCTION IS IMPORTANT
3571
+ * ------------------------------------------------------------
3572
+ *
3573
+ * During automatic code generation:
3574
+ *
3575
+ * - components get deleted
3576
+ * - hooks get removed
3577
+ * - JSX gets replaced
3578
+ *
3579
+ * which leaves behind:
3580
+ *
3581
+ * - dead imports
3582
+ * - duplicate imports
3583
+ * - stale imports
3584
+ *
3585
+ * optimizeImports cleans the file afterward.
3586
+ *
3587
+ * Useful commands:
3588
+ * - clear it: removes stale imports after JSX cleanup
3589
+ * - remove header: removes unused Header import
3590
+ * - remove useEffect: removes dead hook imports
3591
+ * - replace navbar: cleans obsolete imports afterward
3592
+ * - delete chart section: removes unused chart imports
3593
+ *
3594
+ * ------------------------------------------------------------
3595
+ * IMPORTANT ARCHITECTURAL IDEA
3596
+ * ------------------------------------------------------------
3597
+ *
3598
+ * This function should usually run AFTER:
3599
+ *
3600
+ * - removeJSX
3601
+ * - deleteComponent
3602
+ * - updateFunction
3603
+ * - removeImport
3604
+ * - replaceJSX
3605
+ *
3606
+ * It acts as a cleanup/sanitization pass.
3607
+ *
3608
+ * ------------------------------------------------------------
3609
+ * IMPORTANT LIMITATION
3610
+ * ------------------------------------------------------------
3611
+ *
3612
+ * This is a lightweight AST-based optimizer.
3613
+ *
3614
+ * It checks identifier usage inside the current file only.
3615
+ *
3616
+ * It does NOT:
3617
+ * - understand runtime usage
3618
+ * - understand dynamic imports
3619
+ * - understand string-based references
3620
+ * - understand reflection/meta-programming
3621
+ *
3622
+ * ------------------------------------------------------------
3623
+ * PARAMS
3624
+ * ------------------------------------------------------------
3625
+ *
3626
+ * @param {Object} params
3627
+ *
3628
+ * @param {string} params.code
3629
+ * Entire source code.
3630
+ *
3631
+ * ------------------------------------------------------------
3632
+ * RETURNS
3633
+ * ------------------------------------------------------------
3634
+ *
3635
+ * @returns {{updatedCode: string}}
3636
+ * Updated optimized code.
3637
+ *
3638
+ */
3639
+ function optimizeImports({ code }, context) {
3640
+ // ----------------------------------------------------------
3641
+ // STEP 1:
3642
+ // Parse code into AST
3643
+ //
3644
+ // WHY?
3645
+ //
3646
+ // AST lets us safely analyze:
3647
+ // - imports
3648
+ // - identifiers
3649
+ // - JSX usage
3650
+ // ----------------------------------------------------------
3651
+ const ast = parse(code, {
3652
+ sourceType: "module",
3653
+ plugins: ["jsx", "typescript"],
3654
+ });
3655
+ // ----------------------------------------------------------
3656
+ // STEP 2:
3657
+ // Store all identifiers actually used in file
3658
+ //
3659
+ // Example:
3660
+ //
3661
+ // useState
3662
+ // Header
3663
+ // React
3664
+ // ----------------------------------------------------------
3665
+ const usedIdentifiers = new Set();
3666
+ // ----------------------------------------------------------
3667
+ // STEP 3:
3668
+ // Traverse AST and collect identifier usage
3669
+ // ----------------------------------------------------------
3670
+ traverse.default(ast, {
3671
+ Identifier(path) {
3672
+ // ------------------------------------------------------
3673
+ // Ignore identifiers inside import declarations
3674
+ //
3675
+ // Example:
3676
+ //
3677
+ // import React from "react"
3678
+ //
3679
+ // "React" here should NOT count as usage
3680
+ // ------------------------------------------------------
3681
+ if (path.parent.type === "ImportSpecifier") {
3682
+ return;
3683
+ }
3684
+ if (path.parent.type === "ImportDefaultSpecifier") {
3685
+ return;
3686
+ }
3687
+ if (path.parent.type === "ImportNamespaceSpecifier") {
3688
+ return;
3689
+ }
3690
+ // ------------------------------------------------------
3691
+ // Add identifier name to used set
3692
+ // ------------------------------------------------------
3693
+ usedIdentifiers.add(path.node.name);
3694
+ },
3695
+ // --------------------------------------------------------
3696
+ // JSX identifiers are separate from normal identifiers
3697
+ //
3698
+ // Example:
3699
+ //
3700
+ // <Header />
3701
+ //
3702
+ // Header is JSXIdentifier
3703
+ // --------------------------------------------------------
3704
+ JSXIdentifier(path) {
3705
+ // ------------------------------------------------------
3706
+ // Ignore native HTML tags
3707
+ //
3708
+ // div
3709
+ // button
3710
+ // span
3711
+ // ------------------------------------------------------
3712
+ const isNativeTag = path.node.name[0] === path.node.name[0].toLowerCase();
3713
+ if (isNativeTag) {
3714
+ return;
3715
+ }
3716
+ // ------------------------------------------------------
3717
+ // Track JSX component usage
3718
+ // ------------------------------------------------------
3719
+ usedIdentifiers.add(path.node.name);
3720
+ },
3721
+ });
3722
+ // ----------------------------------------------------------
3723
+ // STEP 4:
3724
+ // Traverse imports and remove unused specifiers
3725
+ // ----------------------------------------------------------
3726
+ traverse.default(ast, {
3727
+ ImportDeclaration(path) {
3728
+ // ------------------------------------------------------
3729
+ // Filter import specifiers
3730
+ // ------------------------------------------------------
3731
+ const remainingSpecifiers = path.node.specifiers.filter((specifier) => {
3732
+ // ==================================================
3733
+ // DEFAULT IMPORT
3734
+ // ==================================================
3735
+ if (t.isImportDefaultSpecifier(specifier)) {
3736
+ return usedIdentifiers.has(specifier.local.name);
3737
+ }
3738
+ // ==================================================
3739
+ // NAMED IMPORT
3740
+ // ==================================================
3741
+ if (t.isImportSpecifier(specifier)) {
3742
+ return usedIdentifiers.has(specifier.local.name);
3743
+ }
3744
+ // ==================================================
3745
+ // NAMESPACE IMPORT
3746
+ // ==================================================
3747
+ if (t.isImportNamespaceSpecifier(specifier)) {
3748
+ return usedIdentifiers.has(specifier.local.name);
3749
+ }
3750
+ return true;
3751
+ });
3752
+ // ------------------------------------------------------
3753
+ // If no imports remain:
3754
+ // remove entire import declaration
3755
+ // ------------------------------------------------------
3756
+ if (remainingSpecifiers.length === 0) {
3757
+ path.remove();
3758
+ return;
3759
+ }
3760
+ // ------------------------------------------------------
3761
+ // Otherwise update remaining imports
3762
+ // ------------------------------------------------------
3763
+ path.node.specifiers = remainingSpecifiers;
3764
+ },
3765
+ });
3766
+ // ----------------------------------------------------------
3767
+ // STEP 5:
3768
+ // Generate updated source code from AST
3769
+ // ----------------------------------------------------------
3770
+ return { success: true, updatedCode: generate(ast).code };
3771
+ }
3772
+ function updateImportSource({ code, oldSource, newSource }) {
3773
+ // ----------------------------------------------------------
3774
+ // STEP 1:
3775
+ // Parse code into AST
3776
+ //
3777
+ // WHY?
3778
+ //
3779
+ // AST lets us safely analyze:
3780
+ // - imports
3781
+ // - identifiers
3782
+ // - JSX usage
3783
+ // ----------------------------------------------------------
3784
+ const ast = parse(code, {
3785
+ sourceType: "module",
3786
+ plugins: ["jsx", "typescript"],
3787
+ });
3788
+ traverse.default(ast, {
3789
+ ImportDeclaration(path) {
3790
+ if (path.node.source.value === oldSource) {
3791
+ path.node.source.value = newSource;
3792
+ }
3793
+ },
3794
+ });
3795
+ return { success: true, updatedCode: generate(ast).code };
3796
+ }
3797
+ export default {
3798
+ insertJSX,
3799
+ replaceJSX,
3800
+ removeJSX,
3801
+ moveJSX,
3802
+ wrapJSX,
3803
+ // expose only when planner becomes mature enough to use it directly
3804
+ insertVariable,
3805
+ updateVariable,
3806
+ deleteVariable,
3807
+ removeImport,
3808
+ ensureImport,
3809
+ optimizeImports,
3810
+ updateImportSource,
3811
+ // createFunction,
3812
+ // updateFunction,
3813
+ // deleteFunction,
3814
+ // do not expose
3815
+ // generateClassNames,
3816
+ // syncComponentStyles,
3817
+ // resolveCssClassConflicts,
3818
+ };
3819
+ //# sourceMappingURL=ast.js.map