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,1454 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import traverse from "@babel/traverse";
4
+ import t from "@babel/types";
5
+ import { ERROR_CODES } from "../../schemas/index.js";
6
+ import { LoomaError } from "../../server/error.js";
7
+ /*
8
+
9
+ const importPath = findImportDeclaration({
10
+ ast,
11
+ source: "react",
12
+ });
13
+
14
+ if (importPath) {
15
+ console.log(importPath.node);
16
+ }
17
+
18
+ */
19
+ function findImportDeclaration({ ast, source }) {
20
+ let importDeclarationPath = null;
21
+ traverse.default(ast, {
22
+ ImportDeclaration(path) {
23
+ if (path.node.source.value === source) {
24
+ importDeclarationPath = path;
25
+ path.stop();
26
+ }
27
+ },
28
+ });
29
+ return importDeclarationPath;
30
+ }
31
+ /**
32
+ * Finds the deepest AST node that exists at a specific line number.
33
+ *
34
+ * ------------------------------------------------------------
35
+ * WHAT THIS FUNCTION DOES
36
+ * ------------------------------------------------------------
37
+ *
38
+ * This function searches an AST and returns the node
39
+ * that belongs to a given source-code line.
40
+ *
41
+ * Example:
42
+ *
43
+ * CODE:
44
+ *
45
+ * 1 | import React from 'react';
46
+ * 2 |
47
+ * 3 | function Header() {
48
+ * 4 | return <div>Hello</div>;
49
+ * 5 | }
50
+ *
51
+ * Calling:
52
+ *
53
+ * findNodeByLine({ ast, line: 4 })
54
+ *
55
+ * may return:
56
+ *
57
+ * JSXElement node
58
+ *
59
+ * ------------------------------------------------------------
60
+ * WHY THIS FUNCTION EXISTS
61
+ * ------------------------------------------------------------
62
+ *
63
+ * In AI-powered code editing systems,
64
+ * users often refer to code by location.
65
+ *
66
+ * Examples:
67
+ *
68
+ * - "replace code at line 20"
69
+ * - "insert variable near line 15"
70
+ * - "wrap JSX around line 42"
71
+ * - "delete component at line 10"
72
+ *
73
+ * AST traversal alone is not enough.
74
+ *
75
+ * We also need:
76
+ *
77
+ * source-code location mapping.
78
+ *
79
+ * ------------------------------------------------------------
80
+ * WHAT IS NODE LOCATION?
81
+ * ------------------------------------------------------------
82
+ *
83
+ * Babel AST nodes contain:
84
+ *
85
+ * node.loc.start.line
86
+ * node.loc.end.line
87
+ *
88
+ * Example:
89
+ *
90
+ * function Header() {}
91
+ *
92
+ * may internally store:
93
+ *
94
+ * start.line = 3
95
+ * end.line = 5
96
+ *
97
+ * ------------------------------------------------------------
98
+ * IMPORTANT ARCHITECTURAL IDEA
99
+ * ------------------------------------------------------------
100
+ *
101
+ * findNodeByLine is:
102
+ *
103
+ * INTERNAL ONLY
104
+ *
105
+ * WHY?
106
+ *
107
+ * Users never explicitly ask:
108
+ *
109
+ * "find AST node by line"
110
+ *
111
+ * Instead:
112
+ *
113
+ * higher-level primitives internally use it.
114
+ *
115
+ * ------------------------------------------------------------
116
+ * WHY THIS FUNCTION IS IMPORTANT
117
+ * ------------------------------------------------------------
118
+ *
119
+ * Many editing primitives need precise targeting.
120
+ *
121
+ * Example:
122
+ *
123
+ * insertVariable()
124
+ *
125
+ * may need:
126
+ *
127
+ * "insert near line 20"
128
+ *
129
+ * Example:
130
+ *
131
+ * replaceJSX()
132
+ *
133
+ * may need:
134
+ *
135
+ * "replace JSX around line 55"
136
+ *
137
+ * ------------------------------------------------------------
138
+ * HOW MATCHING WORKS
139
+ * ------------------------------------------------------------
140
+ *
141
+ * A node matches when:
142
+ *
143
+ * targetLine >= start.line
144
+ * AND
145
+ * targetLine <= end.line
146
+ *
147
+ * Example:
148
+ *
149
+ * function Header() {
150
+ * return <div />;
151
+ * }
152
+ *
153
+ * covers:
154
+ *
155
+ * lines 3 → 5
156
+ *
157
+ * ------------------------------------------------------------
158
+ * DEEPEST NODE STRATEGY
159
+ * ------------------------------------------------------------
160
+ *
161
+ * Many nodes overlap same line.
162
+ *
163
+ * Example:
164
+ *
165
+ * function Header() {
166
+ * return <div>Hello</div>;
167
+ * }
168
+ *
169
+ * line 4 belongs to:
170
+ *
171
+ * - FunctionDeclaration
172
+ * - ReturnStatement
173
+ * - JSXElement
174
+ * - JSXText
175
+ *
176
+ * This function returns:
177
+ *
178
+ * deepest matching node.
179
+ *
180
+ * WHY?
181
+ *
182
+ * Deepest node is usually most precise target.
183
+ *
184
+ * Useful in
185
+ * - insertVariable
186
+ * - updateVariable
187
+ * - deleteVariable
188
+ * - insertJSX
189
+ * - replaceJSX
190
+ * - removeJSX
191
+ * - wrapJSX
192
+ * - moveJSX
193
+ * - updateFunction
194
+ * - deleteFunction
195
+ * - updateComponent
196
+ * - deleteComponent
197
+ * - codeReplace
198
+ *
199
+ *
200
+ * High Level Primitive
201
+
202
+ parseAST
203
+
204
+ findNodeByLine
205
+
206
+ AST modification
207
+
208
+ generateCode
209
+ * ------------------------------------------------------------
210
+ * IMPORTANT LIMITATION
211
+ * ------------------------------------------------------------
212
+ *
213
+ * Current implementation:
214
+ *
215
+ * - returns single deepest node
216
+ *
217
+ * Future improvements:
218
+ *
219
+ * - return node path
220
+ * - filter node types
221
+ * - nearest sibling lookup
222
+ * - parent traversal helpers
223
+ *
224
+ * ------------------------------------------------------------
225
+ * PARAMS
226
+ * ------------------------------------------------------------
227
+ *
228
+ * @param {Object} params
229
+ *
230
+ * @param {object} params.ast
231
+ * Babel AST object.
232
+ *
233
+ * @param {number} params.line
234
+ * Target source-code line number.
235
+ *
236
+ * ------------------------------------------------------------
237
+ * RETURNS
238
+ * ------------------------------------------------------------
239
+ *
240
+ * @returns {object|null}
241
+ * Matching AST node or null.
242
+ *
243
+ */
244
+ function findNodeByLine({ ast, line, }) {
245
+ // ----------------------------------------------------------
246
+ // STEP 1:
247
+ // Store best matching node
248
+ //
249
+ // Initially:
250
+ //
251
+ // no node found.
252
+ // ----------------------------------------------------------
253
+ let matchedNode = null;
254
+ // ----------------------------------------------------------
255
+ // STEP 2:
256
+ // Store current deepest node depth
257
+ //
258
+ // WHY?
259
+ //
260
+ // Multiple nodes may overlap same line.
261
+ //
262
+ // We prefer deepest node.
263
+ // ----------------------------------------------------------
264
+ let deepestDepth = -1;
265
+ // ----------------------------------------------------------
266
+ // STEP 3:
267
+ // Traverse entire AST
268
+ // ----------------------------------------------------------
269
+ traverse.default(ast, {
270
+ enter(path) {
271
+ // ------------------------------------------------------
272
+ // Ignore nodes without source location
273
+ //
274
+ // Some synthetic/generated nodes may not
275
+ // contain loc information.
276
+ // ------------------------------------------------------
277
+ if (!path.node.loc) {
278
+ return;
279
+ }
280
+ // ------------------------------------------------------
281
+ // Extract node line range
282
+ // ------------------------------------------------------
283
+ const startLine = path.node.loc.start.line;
284
+ const endLine = path.node.loc.end.line;
285
+ // ------------------------------------------------------
286
+ // Check whether target line exists inside
287
+ // current node range
288
+ //
289
+ // Example:
290
+ //
291
+ // line 5 matches:
292
+ //
293
+ // start=3
294
+ // end=8
295
+ // ------------------------------------------------------
296
+ const isMatching = line >= startLine && line <= endLine;
297
+ // ------------------------------------------------------
298
+ // Ignore unrelated nodes
299
+ // ------------------------------------------------------
300
+ if (!isMatching) {
301
+ return;
302
+ }
303
+ // ------------------------------------------------------
304
+ // Calculate current traversal depth
305
+ //
306
+ // WHY?
307
+ //
308
+ // Deeper nodes are usually more precise.
309
+ // ------------------------------------------------------
310
+ const currentDepth = path.getAncestry().length;
311
+ // ------------------------------------------------------
312
+ // Replace current match if:
313
+ //
314
+ // - node is deeper
315
+ // ------------------------------------------------------
316
+ if (currentDepth > deepestDepth) {
317
+ matchedNode = path.node;
318
+ deepestDepth = currentDepth;
319
+ }
320
+ },
321
+ });
322
+ // ----------------------------------------------------------
323
+ // STEP 4:
324
+ // Return best matching node
325
+ // ----------------------------------------------------------
326
+ return matchedNode;
327
+ }
328
+ /**
329
+ * Finds a React component in AST using component name.
330
+ *
331
+ * ------------------------------------------------------------
332
+ * WHAT THIS FUNCTION DOES
333
+ * ------------------------------------------------------------
334
+ *
335
+ * This function searches AST and returns the component node
336
+ * matching a specific component name.
337
+ *
338
+ * Example:
339
+ *
340
+ * CODE:
341
+ *
342
+ * function Header() {
343
+ * return <div>Header</div>;
344
+ * }
345
+ *
346
+ * Calling:
347
+ *
348
+ * findComponentInJsx({
349
+ * ast,
350
+ * componentName: "Header"
351
+ * })
352
+ *
353
+ * returns:
354
+ *
355
+ * FunctionDeclaration node
356
+ *
357
+ * ------------------------------------------------------------
358
+ * WHY THIS FUNCTION EXISTS
359
+ * ------------------------------------------------------------
360
+ *
361
+ * In AI-powered frontend systems,
362
+ * many operations target components by name.
363
+ *
364
+ * Examples:
365
+ *
366
+ * - "update Header component"
367
+ * - "delete Navbar component"
368
+ * - "wrap Sidebar component"
369
+ * - "extract Footer component"
370
+ *
371
+ * AST traversal alone is not enough.
372
+ *
373
+ * We need:
374
+ *
375
+ * semantic component lookup.
376
+ *
377
+ * Usefull in
378
+ * - updateComponent
379
+ * - deleteComponent
380
+ * - renameComponent
381
+ * - wrapComponent
382
+ * - splitComponent
383
+ * - mergeComponents
384
+ * - extractComponent
385
+ * - insertJSX
386
+ * - replaceJSX
387
+ * - removeJSX
388
+ * - moveJSX
389
+
390
+ parseAST
391
+
392
+ findComponentInJsx
393
+
394
+ AST modification
395
+
396
+ generateCode
397
+ * ------------------------------------------------------------
398
+ * IMPORTANT ARCHITECTURAL IDEA
399
+ * ------------------------------------------------------------
400
+ *
401
+ * findComponentInJsx is:
402
+ *
403
+ * INTERNAL ONLY
404
+ *
405
+ * WHY?
406
+ *
407
+ * Users never directly ask:
408
+ *
409
+ * "find AST component node"
410
+ *
411
+ * Instead:
412
+ *
413
+ * higher-level primitives internally use it.
414
+ *
415
+ * ------------------------------------------------------------
416
+ * SUPPORTED COMPONENT TYPES
417
+ * ------------------------------------------------------------
418
+ *
419
+ * Current implementation supports:
420
+ *
421
+ * 1) Function declarations
422
+ *
423
+ * function Header() {}
424
+ *
425
+ * 2) Arrow function components
426
+ *
427
+ * const Header = () => {}
428
+ *
429
+ * 3) Function expression components
430
+ *
431
+ * const Header = function() {}
432
+ *
433
+ * ------------------------------------------------------------
434
+ * HOW COMPONENT DETECTION WORKS
435
+ * ------------------------------------------------------------
436
+ *
437
+ * Current heuristic:
438
+ *
439
+ * - component name must start with uppercase letter
440
+ * - must be function-based
441
+ *
442
+ * WHY?
443
+ *
444
+ * React components conventionally use:
445
+ *
446
+ * PascalCase
447
+ *
448
+ * ------------------------------------------------------------
449
+ * IMPORTANT LIMITATION
450
+ * ------------------------------------------------------------
451
+ *
452
+ * Current implementation:
453
+ *
454
+ * - does not verify JSX return existence
455
+ *
456
+ * Meaning:
457
+ *
458
+ * utility functions with uppercase names
459
+ * may also match.
460
+ *
461
+ * Future improvements:
462
+ *
463
+ * - verify JSX return
464
+ * - support memo()
465
+ * - support forwardRef()
466
+ * - support lazy()
467
+ * - support class components
468
+ *
469
+ * ------------------------------------------------------------
470
+ * RETURN VALUE
471
+ * ------------------------------------------------------------
472
+ *
473
+ * Returns:
474
+ *
475
+ * Babel NodePath
476
+ *
477
+ * instead of plain node.
478
+ *
479
+ * WHY?
480
+ *
481
+ * NodePath provides:
482
+ *
483
+ * - parent traversal
484
+ * - replacement APIs
485
+ * - removal APIs
486
+ * - insertion APIs
487
+ *
488
+ * which are essential for transformations.
489
+ *
490
+ * ------------------------------------------------------------
491
+ * PARAMS
492
+ * ------------------------------------------------------------
493
+ *
494
+ * @param {Object} params
495
+ *
496
+ * @param {object} params.ast
497
+ * Babel AST object.
498
+ *
499
+ * @param {string} params.componentName
500
+ * Component name to search.
501
+ *
502
+ * Example:
503
+ * "Header"
504
+ *
505
+ * ------------------------------------------------------------
506
+ * RETURNS
507
+ * ------------------------------------------------------------
508
+ *
509
+ * @returns {NodePath|null}
510
+ * Matching component path or null.
511
+ *
512
+ */
513
+ function findComponentInJsx({ ast, componentName }, context) {
514
+ // ----------------------------------------------------------
515
+ // STEP 1:
516
+ // Store matching component path
517
+ //
518
+ // Initially:
519
+ //
520
+ // no component found.
521
+ // ----------------------------------------------------------
522
+ let matchedPath = null;
523
+ // ----------------------------------------------------------
524
+ // STEP 2:
525
+ // Traverse entire AST
526
+ // ----------------------------------------------------------
527
+ traverse.default(ast, {
528
+ // --------------------------------------------------------
529
+ // CASE 1:
530
+ // Function declaration component
531
+ //
532
+ // Example:
533
+ //
534
+ // function Header() {}
535
+ // --------------------------------------------------------
536
+ FunctionDeclaration(path) {
537
+ // ------------------------------------------------------
538
+ // Extract function name
539
+ // ------------------------------------------------------
540
+ const functionName = path.node.id?.name;
541
+ // ------------------------------------------------------
542
+ // Ignore anonymous functions
543
+ // ------------------------------------------------------
544
+ if (!functionName) {
545
+ return;
546
+ }
547
+ // ------------------------------------------------------
548
+ // Check whether name matches target
549
+ // ------------------------------------------------------
550
+ if (functionName === componentName) {
551
+ matchedPath = path;
552
+ // ----------------------------------------------------
553
+ // Stop traversal once component found
554
+ // ----------------------------------------------------
555
+ path.stop();
556
+ }
557
+ },
558
+ // --------------------------------------------------------
559
+ // CASE 2:
560
+ // Variable declaration components
561
+ //
562
+ // Examples:
563
+ //
564
+ // const Header = () => {}
565
+ //
566
+ // const Header = function() {}
567
+ // --------------------------------------------------------
568
+ VariableDeclarator(path) {
569
+ // ------------------------------------------------------
570
+ // Extract variable name
571
+ // ------------------------------------------------------
572
+ const variableName = t.isIdentifier(path.node.id)
573
+ ? path.node.id.name
574
+ : undefined;
575
+ // ------------------------------------------------------
576
+ // Ignore unrelated variables
577
+ // ------------------------------------------------------
578
+ if (variableName !== componentName) {
579
+ return;
580
+ }
581
+ // ------------------------------------------------------
582
+ // Extract assigned value
583
+ // ------------------------------------------------------
584
+ const init = path.node.init;
585
+ // ------------------------------------------------------
586
+ // Check whether assigned value is:
587
+ //
588
+ // - ArrowFunctionExpression
589
+ // OR
590
+ // - FunctionExpression
591
+ // ------------------------------------------------------
592
+ const isFunctionComponent = t.isArrowFunctionExpression(init) || t.isFunctionExpression(init);
593
+ // ------------------------------------------------------
594
+ // Ignore non-function assignments
595
+ // ------------------------------------------------------
596
+ if (!isFunctionComponent) {
597
+ return;
598
+ }
599
+ // ------------------------------------------------------
600
+ // Store matching path
601
+ // ------------------------------------------------------
602
+ matchedPath = path;
603
+ // ------------------------------------------------------
604
+ // Stop traversal once component found
605
+ // ------------------------------------------------------
606
+ path.stop();
607
+ },
608
+ });
609
+ // ----------------------------------------------------------
610
+ // STEP 3:
611
+ // Return matching component path
612
+ // ----------------------------------------------------------
613
+ return matchedPath;
614
+ }
615
+ /**
616
+ * Finds a JSX element inside AST.
617
+ *
618
+ * ------------------------------------------------------------
619
+ * WHAT THIS FUNCTION DOES
620
+ * ------------------------------------------------------------
621
+ *
622
+ * This function searches AST and returns a JSX element
623
+ * matching specific conditions.
624
+ *
625
+ * Example:
626
+ *
627
+ * CODE:
628
+ *
629
+ * <div>
630
+ * <Header />
631
+ * </div>
632
+ *
633
+ * Calling:
634
+ *
635
+ * findJSXElement({
636
+ * ast,
637
+ * elementName: "Header"
638
+ * })
639
+ *
640
+ * returns:
641
+ *
642
+ * JSXElement path for:
643
+ *
644
+ * <Header />
645
+ *
646
+ * ------------------------------------------------------------
647
+ * WHY THIS FUNCTION EXISTS
648
+ * ------------------------------------------------------------
649
+ *
650
+ * In AI-powered frontend systems,
651
+ * many UI operations target JSX elements.
652
+ *
653
+ * Examples:
654
+ *
655
+ * - "wrap button with div"
656
+ * - "remove Header component usage"
657
+ * - "move sidebar below navbar"
658
+ * - "replace card JSX"
659
+ * - "insert modal inside layout"
660
+ *
661
+ * Raw string manipulation becomes unreliable.
662
+ *
663
+ * We need:
664
+ *
665
+ * semantic JSX lookup.
666
+ *
667
+ * Useful in:
668
+ * - insertJSX
669
+ * - replaceJSX
670
+ * - removeJSX
671
+ * - wrapJSX
672
+ * - moveJSX
673
+ * - splitComponent
674
+ * - mergeComponents
675
+ * - extractComponent
676
+ *
677
+ parseAST
678
+
679
+ findJSXElement
680
+
681
+ JSX modification
682
+
683
+ generateCode
684
+ * ------------------------------------------------------------
685
+ * IMPORTANT ARCHITECTURAL IDEA
686
+ * ------------------------------------------------------------
687
+ *
688
+ * findJSXElement is:
689
+ *
690
+ * INTERNAL ONLY
691
+ *
692
+ * WHY?
693
+ *
694
+ * Users never directly ask:
695
+ *
696
+ * "find JSX AST node"
697
+ *
698
+ * Instead:
699
+ *
700
+ * higher-level primitives internally use it.
701
+ *
702
+ * ------------------------------------------------------------
703
+ * WHAT COUNTS AS JSX ELEMENT?
704
+ * ------------------------------------------------------------
705
+ *
706
+ * Examples:
707
+ *
708
+ * <div />
709
+ * <Header />
710
+ * <Button></Button>
711
+ *
712
+ * All are:
713
+ *
714
+ * JSXElement nodes.
715
+ *
716
+ * ------------------------------------------------------------
717
+ * HOW MATCHING WORKS
718
+ * ------------------------------------------------------------
719
+ *
720
+ * Matching is based on:
721
+ *
722
+ * openingElement.name
723
+ *
724
+ * Example:
725
+ *
726
+ * <Header />
727
+ *
728
+ * name becomes:
729
+ *
730
+ * "Header"
731
+ *
732
+ * ------------------------------------------------------------
733
+ * SUPPORTED ELEMENT TYPES
734
+ * ------------------------------------------------------------
735
+ *
736
+ * Current implementation supports:
737
+ *
738
+ * - HTML elements
739
+ * - React components
740
+ *
741
+ * Examples:
742
+ *
743
+ * div
744
+ * span
745
+ * Header
746
+ * Sidebar
747
+ *
748
+ * ------------------------------------------------------------
749
+ * IMPORTANT LIMITATION
750
+ * ------------------------------------------------------------
751
+ *
752
+ * Current implementation:
753
+ *
754
+ * - supports simple JSX names only
755
+ *
756
+ * NOT:
757
+ *
758
+ * <UI.Header />
759
+ *
760
+ * because:
761
+ *
762
+ * JSXMemberExpression
763
+ * is more complex.
764
+ *
765
+ * Future improvements:
766
+ *
767
+ * - support member expressions
768
+ * - support nested filters
769
+ * - support parent matching
770
+ * - support attribute matching
771
+ * - support nth occurrence selection
772
+ *
773
+ * ------------------------------------------------------------
774
+ * RETURN VALUE
775
+ * ------------------------------------------------------------
776
+ *
777
+ * Returns:
778
+ *
779
+ * Babel NodePath
780
+ *
781
+ * instead of plain node.
782
+ *
783
+ * WHY?
784
+ *
785
+ * NodePath enables:
786
+ *
787
+ * - replaceWith()
788
+ * - remove()
789
+ * - insertBefore()
790
+ * - insertAfter()
791
+ *
792
+ * essential for JSX transformations.
793
+ *
794
+ * ------------------------------------------------------------
795
+ * FIRST MATCH STRATEGY
796
+ * ------------------------------------------------------------
797
+ *
798
+ * Current implementation:
799
+ *
800
+ * - returns first matching JSX element
801
+ *
802
+ * WHY?
803
+ *
804
+ * Simpler deterministic behavior.
805
+ *
806
+ * Future improvement:
807
+ *
808
+ * - return all matches
809
+ * - return exact occurrence
810
+ *
811
+ * ------------------------------------------------------------
812
+ * PARAMS
813
+ * ------------------------------------------------------------
814
+ *
815
+ * @param {Object} params
816
+ *
817
+ * @param {object} params.ast
818
+ * Babel AST object.
819
+ *
820
+ * @param {string} params.elementName
821
+ * JSX tag/component name.
822
+ *
823
+ * Example:
824
+ * "Header"
825
+ *
826
+ * ------------------------------------------------------------
827
+ * RETURNS
828
+ * ------------------------------------------------------------
829
+ *
830
+ * @returns {NodePath|null}
831
+ * Matching JSX path or null.
832
+ *
833
+ */
834
+ function findJSXElement({ ast, elementName, }) {
835
+ // ----------------------------------------------------------
836
+ // STEP 1:
837
+ // Store matching JSX path
838
+ //
839
+ // Initially:
840
+ //
841
+ // no JSX element found.
842
+ // ----------------------------------------------------------
843
+ let matchedPath = null;
844
+ // ----------------------------------------------------------
845
+ // STEP 2:
846
+ // Traverse entire AST
847
+ // ----------------------------------------------------------
848
+ traverse.default(ast, {
849
+ JSXElement(path) {
850
+ // ------------------------------------------------------
851
+ // Extract opening element
852
+ //
853
+ // Example:
854
+ //
855
+ // <Header />
856
+ //
857
+ // openingElement = Header
858
+ // ------------------------------------------------------
859
+ const openingElement = path.node.openingElement;
860
+ // ------------------------------------------------------
861
+ // Extract JSX tag name node
862
+ // ------------------------------------------------------
863
+ const nameNode = openingElement.name;
864
+ // ------------------------------------------------------
865
+ // Ignore complex JSX names
866
+ //
867
+ // Example:
868
+ //
869
+ // <UI.Header />
870
+ //
871
+ // because it becomes:
872
+ //
873
+ // JSXMemberExpression
874
+ // ------------------------------------------------------
875
+ if (!t.isJSXIdentifier(nameNode)) {
876
+ return;
877
+ }
878
+ // ------------------------------------------------------
879
+ // Extract actual JSX tag name
880
+ //
881
+ // Example:
882
+ //
883
+ // <Header />
884
+ //
885
+ // becomes:
886
+ //
887
+ // "Header"
888
+ // ------------------------------------------------------
889
+ const currentElementName = nameNode.name;
890
+ // ------------------------------------------------------
891
+ // Ignore unrelated JSX elements
892
+ // ------------------------------------------------------
893
+ if (currentElementName !== elementName) {
894
+ return;
895
+ }
896
+ // ------------------------------------------------------
897
+ // Store matching JSX path
898
+ // ------------------------------------------------------
899
+ matchedPath = path;
900
+ // ------------------------------------------------------
901
+ // Stop traversal once found
902
+ // ------------------------------------------------------
903
+ path.stop();
904
+ },
905
+ });
906
+ // ----------------------------------------------------------
907
+ // STEP 3:
908
+ // Return matching JSX path
909
+ // ----------------------------------------------------------
910
+ return matchedPath;
911
+ }
912
+ /**
913
+ * Finds a component directory inside a project.
914
+ *
915
+ * ------------------------------------------------------------
916
+ * WHAT THIS FUNCTION DOES
917
+ * ------------------------------------------------------------
918
+ *
919
+ * This function recursively searches
920
+ * component directories and returns:
921
+ *
922
+ * - matching component path
923
+ * - component metadata
924
+ *
925
+ * Example:
926
+ *
927
+ * Searching:
928
+ *
929
+ * "Header"
930
+ *
931
+ * may return:
932
+ *
933
+ * "./src/components/layout/Header"
934
+ *
935
+ * ------------------------------------------------------------
936
+ * WHY THIS FUNCTION EXISTS
937
+ * ------------------------------------------------------------
938
+ *
939
+ * Runtime AI editing requires:
940
+ *
941
+ * deterministic component lookup.
942
+ *
943
+ * User commands usually reference:
944
+ *
945
+ * logical component names.
946
+ *
947
+ * Example:
948
+ *
949
+ * - "update header"
950
+ * - "make navbar red"
951
+ * - "extract footer"
952
+ *
953
+ * But the backend needs:
954
+ *
955
+ * actual filesystem location.
956
+ *
957
+ * This function acts as:
958
+ *
959
+ * component resolution layer.
960
+ *
961
+ * ------------------------------------------------------------
962
+ * IMPORTANT ARCHITECTURAL DECISION
963
+ * ------------------------------------------------------------
964
+ *
965
+ * Looma should NEVER rely on:
966
+ *
967
+ * fragile path guessing.
968
+ *
969
+ * Component discovery must be:
970
+ *
971
+ * - recursive
972
+ * - deterministic
973
+ * - architecture-aware
974
+ *
975
+ * Eventually this should integrate with:
976
+ *
977
+ * component registry system.
978
+ *
979
+ * ------------------------------------------------------------
980
+ * IMPORTANT NOTE
981
+ * ------------------------------------------------------------
982
+ *
983
+ * Current implementation:
984
+ *
985
+ * performs filesystem traversal.
986
+ *
987
+ * Production version should preferably use:
988
+ *
989
+ * in-memory component registry
990
+ *
991
+ * because:
992
+ *
993
+ * recursive disk traversal becomes expensive
994
+ * in very large projects.
995
+ *
996
+ * ------------------------------------------------------------
997
+ * DEPENDENCIES
998
+ * ------------------------------------------------------------
999
+ *
1000
+ * This function complements:
1001
+ *
1002
+ * - createComponent()
1003
+ * - updateComponent()
1004
+ * - moveComponent()
1005
+ * - renameComponent()
1006
+ * - extractComponent()
1007
+ * - ensureComponentStructure()
1008
+ * - normalizeComponent()
1009
+ *
1010
+ * ------------------------------------------------------------
1011
+ * WHERE THIS FUNCTION IS USEFUL
1012
+ * ------------------------------------------------------------
1013
+ *
1014
+ * Useful in commands like:
1015
+ *
1016
+ * - "update header"
1017
+ * - "delete navbar"
1018
+ * - "move card component"
1019
+ * - "extract footer"
1020
+ * - "rename sidebar"
1021
+ * - "find hero section"
1022
+ *
1023
+ * Usually executed BEFORE:
1024
+ *
1025
+ * - component mutations
1026
+ * - JSX mutations
1027
+ * - CSS synchronization
1028
+ *
1029
+ * ------------------------------------------------------------
1030
+ * PARAMS
1031
+ * ------------------------------------------------------------
1032
+ *
1033
+ * @param {Object} params
1034
+ *
1035
+ * @param {string} params.rootDirectory
1036
+ * Root directory to start searching from.
1037
+ *
1038
+ * Example:
1039
+ *
1040
+ * "./src/components"
1041
+ *
1042
+ * @param {string} params.componentName
1043
+ * Component name to find.
1044
+ *
1045
+ * Example:
1046
+ *
1047
+ * "Header"
1048
+ *
1049
+ * @param {boolean} [params.caseSensitive=false]
1050
+ * Whether search should be case sensitive.
1051
+ *
1052
+ * @param {boolean} [params.returnFirst=true]
1053
+ * Whether search should stop at first match.
1054
+ *
1055
+ * ------------------------------------------------------------
1056
+ * RETURNS
1057
+ * ------------------------------------------------------------
1058
+ *
1059
+ * @returns {Object|null}
1060
+ *
1061
+ * {
1062
+ * componentName,
1063
+ * componentPath,
1064
+ * jsxPath,
1065
+ * cssPath,
1066
+ * exists
1067
+ * }
1068
+ *
1069
+ * OR
1070
+ *
1071
+ * null
1072
+ *
1073
+ */
1074
+ function findComponentDirectory({
1075
+ // rootDirectory,
1076
+ componentName, caseSensitive = false, returnFirst = true, }, context) {
1077
+ // ----------------------------------------------------------
1078
+ // STEP 1:
1079
+ // Resolve absolute root directory
1080
+ // ----------------------------------------------------------
1081
+ const absoluteRootDirectory = context.project.root;
1082
+ // ----------------------------------------------------------
1083
+ // STEP 2:
1084
+ // Validate root directory existence
1085
+ // ----------------------------------------------------------
1086
+ if (!fs.existsSync(absoluteRootDirectory)) {
1087
+ throw new LoomaError(ERROR_CODES.DIRECTORY_NOT_FOUND, `Root directory does not exist: ${absoluteRootDirectory}`, {
1088
+ payload: {
1089
+ componentName,
1090
+ caseSensitive: false,
1091
+ returnFirst: true,
1092
+ },
1093
+ });
1094
+ }
1095
+ // ----------------------------------------------------------
1096
+ // STEP 3:
1097
+ // Store search result
1098
+ // ----------------------------------------------------------
1099
+ let foundComponent = null;
1100
+ // ----------------------------------------------------------
1101
+ // STEP 4:
1102
+ // Recursive directory traversal function
1103
+ // ----------------------------------------------------------
1104
+ function traverseDirectory(currentDirectory) {
1105
+ // --------------------------------------------------------
1106
+ // Stop traversal if component already found
1107
+ // --------------------------------------------------------
1108
+ if (foundComponent && returnFirst) {
1109
+ return;
1110
+ }
1111
+ // --------------------------------------------------------
1112
+ // Read current directory entries
1113
+ // --------------------------------------------------------
1114
+ const entries = fs.readdirSync(currentDirectory, {
1115
+ withFileTypes: true,
1116
+ });
1117
+ // --------------------------------------------------------
1118
+ // Traverse directory entries
1119
+ // --------------------------------------------------------
1120
+ for (const entry of entries) {
1121
+ // ------------------------------------------------------
1122
+ // Build absolute entry path
1123
+ // ------------------------------------------------------
1124
+ const entryPath = path.join(currentDirectory, entry.name);
1125
+ // ------------------------------------------------------
1126
+ // Skip non-directories
1127
+ // ------------------------------------------------------
1128
+ if (!entry.isDirectory()) {
1129
+ continue;
1130
+ }
1131
+ // ------------------------------------------------------
1132
+ // Normalize names for comparison
1133
+ // ------------------------------------------------------
1134
+ const normalizedEntryName = caseSensitive
1135
+ ? entry.name
1136
+ : entry.name.toLowerCase();
1137
+ const normalizedTargetName = caseSensitive
1138
+ ? componentName
1139
+ : componentName.toLowerCase();
1140
+ // ------------------------------------------------------
1141
+ // Check component match
1142
+ // ------------------------------------------------------
1143
+ if (normalizedEntryName === normalizedTargetName) {
1144
+ // ----------------------------------------------------
1145
+ // Build potential JSX file path
1146
+ // ----------------------------------------------------
1147
+ const jsxPath = path.join(entryPath, `${entry.name}.jsx`);
1148
+ // ----------------------------------------------------
1149
+ // Build potential CSS file path
1150
+ // ----------------------------------------------------
1151
+ const cssPath = path.join(entryPath, `${entry.name}.css`);
1152
+ // ----------------------------------------------------
1153
+ // Store component metadata
1154
+ // ----------------------------------------------------
1155
+ foundComponent = {
1156
+ componentName: entry.name,
1157
+ componentPath: entryPath,
1158
+ jsxPath,
1159
+ cssPath,
1160
+ exists: true,
1161
+ };
1162
+ // ----------------------------------------------------
1163
+ // Stop traversal if configured
1164
+ // ----------------------------------------------------
1165
+ if (returnFirst) {
1166
+ return;
1167
+ }
1168
+ }
1169
+ // ------------------------------------------------------
1170
+ // Continue recursive traversal
1171
+ // ------------------------------------------------------
1172
+ traverseDirectory(entryPath);
1173
+ }
1174
+ }
1175
+ // ----------------------------------------------------------
1176
+ // STEP 5:
1177
+ // Start recursive traversal
1178
+ // ----------------------------------------------------------
1179
+ traverseDirectory(absoluteRootDirectory);
1180
+ // ----------------------------------------------------------
1181
+ // STEP 6:
1182
+ // Return found component
1183
+ // ----------------------------------------------------------
1184
+ if (!foundComponent) {
1185
+ throw new LoomaError(ERROR_CODES.COMPONENT_NOT_FOUND, `Component ${componentName} not found`);
1186
+ }
1187
+ return { success: true, foundComponent };
1188
+ }
1189
+ /**
1190
+ * Finds CSS selectors inside a parsed CSS AST.
1191
+ *
1192
+ * ------------------------------------------------------------
1193
+ * WHAT THIS FUNCTION DOES
1194
+ * ------------------------------------------------------------
1195
+ *
1196
+ * This function searches CSS AST
1197
+ * and returns matching selectors.
1198
+ *
1199
+ * Example:
1200
+ *
1201
+ * CSS:
1202
+ *
1203
+ * .header {
1204
+ * color: red;
1205
+ * }
1206
+ *
1207
+ * .header__title {
1208
+ * font-size: 20px;
1209
+ * }
1210
+ *
1211
+ * Searching:
1212
+ *
1213
+ * ".header"
1214
+ *
1215
+ * returns:
1216
+ *
1217
+ * matching PostCSS rule nodes.
1218
+ *
1219
+ * ------------------------------------------------------------
1220
+ * WHY THIS FUNCTION EXISTS
1221
+ * ------------------------------------------------------------
1222
+ *
1223
+ * Most advanced CSS operations require:
1224
+ *
1225
+ * locating selectors safely.
1226
+ *
1227
+ * Example operations:
1228
+ *
1229
+ * - update selector styles
1230
+ * - remove selector
1231
+ * - rename selector
1232
+ * - analyze declarations
1233
+ * - optimize css
1234
+ *
1235
+ * Regex becomes unreliable
1236
+ * for large CSS systems.
1237
+ *
1238
+ * AST selector traversal is safer.
1239
+ *
1240
+ * ------------------------------------------------------------
1241
+ * IMPORTANT ARCHITECTURAL DECISION
1242
+ * ------------------------------------------------------------
1243
+ *
1244
+ * This function works on:
1245
+ *
1246
+ * CSS AST
1247
+ *
1248
+ * NOT raw css strings.
1249
+ *
1250
+ * This enables:
1251
+ *
1252
+ * deterministic CSS tooling.
1253
+ *
1254
+ * ------------------------------------------------------------
1255
+ * DEPENDENCIES
1256
+ * ------------------------------------------------------------
1257
+ *
1258
+ * This function assumes:
1259
+ *
1260
+ * - parseCSS()
1261
+ *
1262
+ * already exists.
1263
+ *
1264
+ * It complements:
1265
+ *
1266
+ * - updateStyles()
1267
+ * - removeStyles()
1268
+ * - renameCssClass()
1269
+ * - syncComponentStyles()
1270
+ *
1271
+ * ------------------------------------------------------------
1272
+ * WHERE THIS FUNCTION IS USEFUL
1273
+ * ------------------------------------------------------------
1274
+ *
1275
+ * Useful in commands like:
1276
+ *
1277
+ * - "make button red"
1278
+ * - "remove card shadow"
1279
+ * - "rename css class"
1280
+ * - "find responsive styles"
1281
+ * - "update navbar styles"
1282
+ * - "cleanup unused css"
1283
+ *
1284
+ * Usually executed BEFORE:
1285
+ *
1286
+ * - css mutations
1287
+ * - selector updates
1288
+ * - declaration analysis
1289
+ *
1290
+ * Example usage:
1291
+
1292
+ const { ast } = parseCSS({
1293
+ cssPath: "./Header.css",
1294
+ });
1295
+
1296
+ const matches =
1297
+ findCssSelector({
1298
+ ast,
1299
+ selector: ".header",
1300
+ });
1301
+
1302
+ console.log(matches);
1303
+
1304
+ or
1305
+
1306
+ findCssSelector({
1307
+ ast,
1308
+ selector: /^\.header/,
1309
+ });
1310
+ * ------------------------------------------------------------
1311
+ * IMPORTANT NOTE
1312
+ * ------------------------------------------------------------
1313
+ *
1314
+ * This function ONLY finds selectors.
1315
+ *
1316
+ * It does NOT:
1317
+ *
1318
+ * - modify css
1319
+ * - remove selectors
1320
+ * - insert styles
1321
+ * - write files
1322
+ *
1323
+ * ------------------------------------------------------------
1324
+ * REQUIRED PACKAGE
1325
+ * ------------------------------------------------------------
1326
+ *
1327
+ * npm install postcss
1328
+ *
1329
+ * ------------------------------------------------------------
1330
+ * PARAMS
1331
+ * ------------------------------------------------------------
1332
+ *
1333
+ * @param {Object} params
1334
+ *
1335
+ * @param {Object} params.ast
1336
+ * Parsed PostCSS AST.
1337
+ *
1338
+ * Usually returned from:
1339
+ *
1340
+ * parseCSS()
1341
+ *
1342
+ * @param {string|RegExp} params.selector
1343
+ * Selector to search.
1344
+ *
1345
+ * Example:
1346
+ *
1347
+ * ".header"
1348
+ *
1349
+ * OR
1350
+ *
1351
+ * /^\.header/
1352
+ *
1353
+ * @param {boolean} [params.findAll=true]
1354
+ * Whether to return all matches.
1355
+ *
1356
+ * ------------------------------------------------------------
1357
+ * RETURNS
1358
+ * ------------------------------------------------------------
1359
+ *
1360
+ * @returns {Object[]}
1361
+ *
1362
+ * Array of matching PostCSS rule nodes.
1363
+ *
1364
+ */
1365
+ function findCssSelector({ ast, selector, findAll = true, }) {
1366
+ // ----------------------------------------------------------
1367
+ // STEP 1:
1368
+ // Validate AST existence
1369
+ // ----------------------------------------------------------
1370
+ if (!ast) {
1371
+ throw new LoomaError(ERROR_CODES.INTERNAL_ERROR, "CSS AST is required");
1372
+ }
1373
+ // ----------------------------------------------------------
1374
+ // STEP 2:
1375
+ // Store matching selectors
1376
+ // ----------------------------------------------------------
1377
+ const matches = [];
1378
+ // ----------------------------------------------------------
1379
+ // STEP 3:
1380
+ // Traverse all CSS rules
1381
+ //
1382
+ // Example:
1383
+ //
1384
+ // .header {}
1385
+ // .card {}
1386
+ // ----------------------------------------------------------
1387
+ ast.walkRules((rule) => {
1388
+ // --------------------------------------------------------
1389
+ // Extract selector text
1390
+ //
1391
+ // Example:
1392
+ //
1393
+ // ".header"
1394
+ // --------------------------------------------------------
1395
+ const ruleSelector = rule.selector;
1396
+ // --------------------------------------------------------
1397
+ // Handle string selector search
1398
+ // --------------------------------------------------------
1399
+ if (typeof selector === "string") {
1400
+ // ------------------------------------------------------
1401
+ // Exact selector match
1402
+ // ------------------------------------------------------
1403
+ if (ruleSelector === selector) {
1404
+ matches.push(rule);
1405
+ // ----------------------------------------------------
1406
+ // Stop early if only first match needed
1407
+ // ----------------------------------------------------
1408
+ if (!findAll) {
1409
+ return false;
1410
+ }
1411
+ }
1412
+ }
1413
+ // --------------------------------------------------------
1414
+ // Handle RegExp selector search
1415
+ // --------------------------------------------------------
1416
+ else if (selector instanceof RegExp) {
1417
+ // ------------------------------------------------------
1418
+ // Match regex against selector
1419
+ // ------------------------------------------------------
1420
+ if (selector.test(ruleSelector)) {
1421
+ matches.push(rule);
1422
+ // ----------------------------------------------------
1423
+ // Stop early if only first match needed
1424
+ // ----------------------------------------------------
1425
+ if (!findAll) {
1426
+ return false;
1427
+ }
1428
+ }
1429
+ }
1430
+ });
1431
+ // ----------------------------------------------------------
1432
+ // STEP 4:
1433
+ // Return matching selectors
1434
+ // ----------------------------------------------------------
1435
+ if (matches.length === 0) {
1436
+ return { success: false, message: `Selector ${selector} not found` };
1437
+ }
1438
+ return { success: true, matches };
1439
+ }
1440
+ export default {
1441
+ // insertCode,
1442
+ // expose only when planner becomes mature enough to use it directly
1443
+ // ensureImport,
1444
+ // removeImport,
1445
+ // enrichImport,
1446
+ // optimizeImports,
1447
+ // updateComponentImports,
1448
+ findNodeByLine,
1449
+ findComponentInJsx,
1450
+ findJSXElement,
1451
+ findComponentDirectory,
1452
+ findCssSelector,
1453
+ };
1454
+ //# sourceMappingURL=finders.js.map