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,840 @@
1
+ import fs from "fs";
2
+ import traverse from "@babel/traverse";
3
+ import { parse } from "@babel/parser";
4
+ import t from "@babel/types";
5
+ import postcss from "postcss";
6
+ import { ERROR_CODES } from "../../schemas/index.js";
7
+ import { LoomaError } from "../../server/error.js";
8
+ /**
9
+ * Parses JavaScript/TypeScript/JSX source code into AST.
10
+ *
11
+ * ------------------------------------------------------------
12
+ * WHAT THIS FUNCTION DOES
13
+ * ------------------------------------------------------------
14
+ *
15
+ * This function converts source code text into:
16
+ *
17
+ * AST (Abstract Syntax Tree)
18
+ *
19
+ * Example:
20
+ *
21
+ * INPUT:
22
+ *
23
+ * function sum(a, b) {
24
+ * return a + b;
25
+ * }
26
+ *
27
+ * OUTPUT:
28
+ *
29
+ * Program
30
+ * └── FunctionDeclaration
31
+ * └── Identifier(sum)
32
+ *
33
+ * ------------------------------------------------------------
34
+ * WHAT IS AST?
35
+ * ------------------------------------------------------------
36
+ *
37
+ * AST = structural representation of code.
38
+ *
39
+ * Instead of manipulating raw strings,
40
+ * tools manipulate syntax nodes safely.
41
+ *
42
+ * Example:
43
+ *
44
+ * Code:
45
+ *
46
+ * const x = 10;
47
+ *
48
+ * becomes:
49
+ *
50
+ * VariableDeclaration
51
+ * └── VariableDeclarator
52
+ * └── Identifier(x)
53
+ *
54
+ * ------------------------------------------------------------
55
+ * WHY THIS FUNCTION EXISTS
56
+ * ------------------------------------------------------------
57
+ *
58
+ * Almost every intelligent code transformation requires AST.
59
+ *
60
+ * Example operations:
61
+ *
62
+ * - insert imports
63
+ * - update variables
64
+ * - create components
65
+ * - manipulate JSX
66
+ * - optimize imports
67
+ * - refactor functions
68
+ *
69
+ * ALL depend on parsing source code first.
70
+ *
71
+ * ------------------------------------------------------------
72
+ * IMPORTANT ARCHITECTURAL IDEA
73
+ * ------------------------------------------------------------
74
+ *
75
+ * parseAST is:
76
+ *
77
+ * INTERNAL ONLY
78
+ *
79
+ * It should NOT be exposed directly as user task.
80
+ *
81
+ * WHY?
82
+ *
83
+ * Users never say:
84
+ *
85
+ * "parse this AST"
86
+ *
87
+ * Instead they say:
88
+ *
89
+ * "add button"
90
+ * "remove import"
91
+ * "create component"
92
+ *
93
+ * Higher-level primitives internally use parseAST.
94
+ *
95
+ * ------------------------------------------------------------
96
+ * WHY AST IS BETTER THAN STRING MANIPULATION
97
+ * ------------------------------------------------------------
98
+ *
99
+ * String manipulation breaks easily.
100
+ *
101
+ * Problems:
102
+ *
103
+ * - formatting differences
104
+ * - nested JSX
105
+ * - multiline code
106
+ * - syntax ambiguity
107
+ * - duplicate names
108
+ *
109
+ * AST guarantees:
110
+ *
111
+ * - syntax-aware transformations
112
+ * - deterministic edits
113
+ * - valid output generation
114
+ *
115
+ * ------------------------------------------------------------
116
+ * SUPPORTED CODE TYPES
117
+ * ------------------------------------------------------------
118
+ *
119
+ * Current parser supports:
120
+ *
121
+ * - JavaScript
122
+ * - JSX
123
+ * - TypeScript
124
+ * - TSX
125
+ *
126
+ * via Babel plugins.
127
+ *
128
+ * ------------------------------------------------------------
129
+ * PARSER PLUGINS
130
+ * ------------------------------------------------------------
131
+ *
132
+ * jsx
133
+ * → enables React JSX parsing
134
+ *
135
+ * typescript
136
+ * → enables TS/TSX parsing
137
+ *
138
+ * ------------------------------------------------------------
139
+ * IMPORTANT LIMITATION
140
+ * ------------------------------------------------------------
141
+ *
142
+ * Current implementation:
143
+ *
144
+ * - assumes modern JS syntax
145
+ *
146
+ * Future improvements:
147
+ *
148
+ * - decorators
149
+ * - class properties
150
+ * - import assertions
151
+ * - pipeline operators
152
+ * - custom parser plugins
153
+ *
154
+ * ------------------------------------------------------------
155
+ * PARAMS
156
+ * ------------------------------------------------------------
157
+ *
158
+ * @param {Object} params
159
+ *
160
+ * @param {string} params.code
161
+ * Source code string.
162
+ *
163
+ * ------------------------------------------------------------
164
+ * RETURNS
165
+ * ------------------------------------------------------------
166
+ *
167
+ * @returns {object}
168
+ * Babel AST object.
169
+ *
170
+ */
171
+ function parseAST({ code }) {
172
+ // ----------------------------------------------------------
173
+ // STEP 1:
174
+ // Parse source code into AST
175
+ //
176
+ // sourceType: "module"
177
+ //
178
+ // enables:
179
+ //
180
+ // import/export parsing.
181
+ //
182
+ // plugins:
183
+ //
184
+ // jsx
185
+ // → React JSX support
186
+ //
187
+ // typescript
188
+ // → TS/TSX support
189
+ // ----------------------------------------------------------
190
+ try {
191
+ return parse(code, {
192
+ sourceType: "module",
193
+ plugins: ["jsx", "typescript"],
194
+ });
195
+ }
196
+ catch (error) {
197
+ throw new LoomaError(ERROR_CODES.PARSE_ERROR, `AST parsing failed: ${error.message}`);
198
+ }
199
+ }
200
+ /**
201
+ * Parses CSS into a structured AST representation.
202
+ *
203
+ * ------------------------------------------------------------
204
+ * WHAT THIS FUNCTION DOES
205
+ * ------------------------------------------------------------
206
+ *
207
+ * This function converts raw CSS code into:
208
+ *
209
+ * CSS AST (Abstract Syntax Tree)
210
+ *
211
+ * using:
212
+ *
213
+ * PostCSS
214
+ *
215
+ * Example:
216
+ *
217
+ * INPUT:
218
+ *
219
+ * .header {
220
+ * color: red;
221
+ * }
222
+ *
223
+ * OUTPUT:
224
+ *
225
+ * {
226
+ * type: "root",
227
+ * nodes: [...]
228
+ * }
229
+ *
230
+ * ------------------------------------------------------------
231
+ * WHY THIS FUNCTION EXISTS
232
+ * ------------------------------------------------------------
233
+ *
234
+ * String replacement becomes dangerous
235
+ * as CSS complexity grows.
236
+ *
237
+ * Example problems:
238
+ *
239
+ * - nested media queries
240
+ * - duplicate selectors
241
+ * - partial replacements
242
+ * - malformed css
243
+ * - accidental deletions
244
+ *
245
+ * AST parsing allows:
246
+ *
247
+ * - safe css mutations
248
+ * - selector analysis
249
+ * - declaration traversal
250
+ * - style refactoring
251
+ * - advanced css tooling
252
+ *
253
+ * ------------------------------------------------------------
254
+ * IMPORTANT ARCHITECTURAL DECISION
255
+ * ------------------------------------------------------------
256
+ *
257
+ * Looma should eventually perform:
258
+ *
259
+ * ALL CSS OPERATIONS
260
+ *
261
+ * using AST transformations.
262
+ *
263
+ * NOT regex.
264
+ *
265
+ * Regex-based CSS editing breaks
266
+ * at scale.
267
+ *
268
+ * This function is foundational
269
+ * infrastructure for that transition.
270
+ *
271
+ * ------------------------------------------------------------
272
+ * DEPENDENCIES
273
+ * ------------------------------------------------------------
274
+ *
275
+ * This function complements:
276
+ *
277
+ * - insertStyles()
278
+ * - updateStyles()
279
+ * - removeStyles()
280
+ * - renameCssClass()
281
+ * - syncComponentStyles()
282
+ *
283
+ * ------------------------------------------------------------
284
+ * WHERE THIS FUNCTION IS USEFUL
285
+ * ------------------------------------------------------------
286
+ *
287
+ * Useful in commands like:
288
+ *
289
+ * - "make button red"
290
+ * - "update responsive styles"
291
+ * - "rename css class"
292
+ * - "remove unused styles"
293
+ * - "extract component"
294
+ * - "optimize css"
295
+ * - "convert to dark mode"
296
+ *
297
+ * It is usually used BEFORE:
298
+ *
299
+ * - advanced css mutations
300
+ * - css analysis
301
+ * - selector synchronization
302
+ *
303
+ * ------------------------------------------------------------
304
+ * IMPORTANT NOTE
305
+ * ------------------------------------------------------------
306
+ *
307
+ * This function ONLY parses CSS.
308
+ *
309
+ * It does NOT:
310
+ *
311
+ * - modify css
312
+ * - optimize css
313
+ * - write files
314
+ *
315
+ * It only converts CSS:
316
+ *
317
+ * text
318
+ * →
319
+ * AST
320
+ *
321
+ * ------------------------------------------------------------
322
+ * REQUIRED PACKAGE
323
+ * ------------------------------------------------------------
324
+ *
325
+ * npm install postcss
326
+ *
327
+ * ------------------------------------------------------------
328
+ * PARAMS
329
+ * ------------------------------------------------------------
330
+ *
331
+ * @param {Object} params
332
+ *
333
+ * @param {string} [params.cssCode]
334
+ * Raw css code.
335
+ *
336
+ * OR
337
+ *
338
+ * @param {string} [params.cssPath]
339
+ * Path to css file.
340
+ *
341
+ * One of:
342
+ *
343
+ * - cssCode
344
+ * - cssPath
345
+ *
346
+ * is required.
347
+ *
348
+ * ------------------------------------------------------------
349
+ * RETURNS
350
+ * ------------------------------------------------------------
351
+ *
352
+ * @returns {Object}
353
+ *
354
+ * {
355
+ * ast,
356
+ * cssCode
357
+ * }
358
+ *
359
+ */
360
+ function parseCSS({ cssCode, cssPath, }) {
361
+ // ----------------------------------------------------------
362
+ // STEP 1:
363
+ // Validate input
364
+ // ----------------------------------------------------------
365
+ if (!cssCode && !cssPath) {
366
+ throw new LoomaError(ERROR_CODES.PARSE_ERROR, "Either cssCode or cssPath is required");
367
+ }
368
+ // ----------------------------------------------------------
369
+ // STEP 2:
370
+ // Read css file if path provided
371
+ // ----------------------------------------------------------
372
+ if (cssPath) {
373
+ // --------------------------------------------------------
374
+ // Validate css file existence
375
+ // --------------------------------------------------------
376
+ if (!fs.existsSync(cssPath)) {
377
+ throw new LoomaError(ERROR_CODES.FILE_NOT_FOUND, `CSS file does not exist: ${cssPath}`);
378
+ }
379
+ // --------------------------------------------------------
380
+ // Read css content
381
+ // --------------------------------------------------------
382
+ cssCode = fs.readFileSync(cssPath, "utf8");
383
+ }
384
+ // ----------------------------------------------------------
385
+ // STEP 3:
386
+ // Parse css using PostCSS
387
+ //
388
+ // Converts:
389
+ //
390
+ // raw css
391
+ // →
392
+ // AST
393
+ // ----------------------------------------------------------
394
+ const ast = postcss.parse(cssCode);
395
+ // ----------------------------------------------------------
396
+ // STEP 4:
397
+ // Return parsed result
398
+ // ----------------------------------------------------------
399
+ return {
400
+ success: true,
401
+ ast,
402
+ cssCode,
403
+ };
404
+ }
405
+ /**
406
+ * Parses React Router routes from AST.
407
+ *
408
+ * returns
409
+ * {
410
+ success: true,
411
+ routes: [ {}, { path: 'step-2' }, { path: 'step-3' } ]
412
+ }
413
+ *
414
+ */
415
+ function parseRoutes({ ast }) {
416
+ const routes = [];
417
+ traverse.default(ast, {
418
+ JSXElement(path) {
419
+ const openingElement = path.node.openingElement;
420
+ if (t.isJSXIdentifier(openingElement.name) &&
421
+ openingElement.name.name === "Route") {
422
+ const route = {};
423
+ openingElement.attributes.forEach((attr) => {
424
+ if (attr.type !== "JSXAttribute") {
425
+ return;
426
+ }
427
+ const name = attr.name.name;
428
+ if (attr.value && attr.value.type === "StringLiteral") {
429
+ if (typeof name === "string") {
430
+ route[name] = attr.value.value;
431
+ }
432
+ }
433
+ });
434
+ routes.push(route);
435
+ }
436
+ },
437
+ });
438
+ if (routes.length === 0) {
439
+ return { success: false, message: "No react route found." };
440
+ }
441
+ return { success: true, routes };
442
+ }
443
+ /**
444
+ * returns all the imports of a component like this
445
+ *
446
+ * [
447
+ { localName: 'useState', source: 'react' },
448
+ { localName: 'useEffect', source: 'react' },
449
+ { localName: 'reactLogo', source: '../../assets/react.svg' },
450
+ { localName: 'viteLogo', source: '/vite.svg' },
451
+ { localName: 'Header', source: './Header' }
452
+ ]
453
+ *
454
+ */
455
+ function parseComponentDependencies({ ast }) {
456
+ const dependencies = [];
457
+ traverse.default(ast, {
458
+ ImportDeclaration(path) {
459
+ const source = path.node.source.value;
460
+ path.node.specifiers.forEach((specifier) => {
461
+ dependencies.push({
462
+ localName: specifier.local.name,
463
+ source,
464
+ });
465
+ });
466
+ },
467
+ });
468
+ return { success: true, dependencies };
469
+ }
470
+ /**
471
+ * Parses component props usage.
472
+ */
473
+ function parseProps({ ast }) {
474
+ const props = new Set();
475
+ traverse.default(ast, {
476
+ MemberExpression(path) {
477
+ if (t.isIdentifier(path.node.object) &&
478
+ path.node.object.name === "props") {
479
+ if (t.isIdentifier(path.node.property)) {
480
+ props.add(path.node.property.name);
481
+ }
482
+ }
483
+ },
484
+ ObjectPattern(path) {
485
+ path.node.properties.forEach((property) => {
486
+ if (property.type === "ObjectProperty") {
487
+ if (property.key && property.key.type === "Identifier") {
488
+ props.add(property.key.name);
489
+ }
490
+ }
491
+ });
492
+ },
493
+ });
494
+ return { success: true, props: [...props] };
495
+ }
496
+ // /**
497
+ // * Parses useState usage.
498
+ // */
499
+ // function parseStateUsage(
500
+ // ast: TaskPayload<"parseStateUsage">
501
+ // ): TaskResponse<TaskReturn<"parseStateUsage">> {
502
+ // const states = [];
503
+ // traverse.default(ast, {
504
+ // VariableDeclarator(path) {
505
+ // const init = path.node.init;
506
+ // if (
507
+ // init &&
508
+ // init.type === "CallExpression" &&
509
+ // init.callee.name === "useState"
510
+ // ) {
511
+ // const elements = path.node.id.elements;
512
+ // states.push({
513
+ // state: elements?.[0]?.name,
514
+ // setter: elements?.[1]?.name,
515
+ // });
516
+ // }
517
+ // },
518
+ // });
519
+ // return states;
520
+ // }
521
+ // /**
522
+ // * Parses JSX event handlers.
523
+ // */
524
+ // function parseEventHandlers(
525
+ // ast: TaskPayload<"parseEventHandlers">
526
+ // ): TaskResponse<TaskReturn<"parseEventHandlers">> {
527
+ // const handlers = [];
528
+ // traverse.default(ast, {
529
+ // JSXAttribute(path) {
530
+ // const name = path.node.name.name;
531
+ // if (!name.startsWith("on")) {
532
+ // return;
533
+ // }
534
+ // const expression = path.node.value?.expression;
535
+ // handlers.push({
536
+ // event: name,
537
+ // handler: expression?.name,
538
+ // });
539
+ // },
540
+ // });
541
+ // return handlers;
542
+ // }
543
+ /**
544
+ * Parses fetch and axios API calls.
545
+ */
546
+ // function parseAPICalls(
547
+ // ast: TaskPayload<"parseEventHandlers">
548
+ // ): TaskResponse<TaskReturn<"parseAPICalls">> {
549
+ // const calls = [];
550
+ // traverse.default(ast, {
551
+ // CallExpression(path) {
552
+ // const callee = path.node.callee;
553
+ // if (callee.name === "fetch") {
554
+ // calls.push({
555
+ // type: "fetch",
556
+ // });
557
+ // }
558
+ // if (callee.object?.name === "axios") {
559
+ // calls.push({
560
+ // type: "axios",
561
+ // method: callee.property.name,
562
+ // });
563
+ // }
564
+ // },
565
+ // });
566
+ // return calls;
567
+ // }
568
+ /**
569
+ * Parses TypeScript interfaces and types.
570
+ */
571
+ // function parseTypescriptTypes(
572
+ // ast: TaskPayload<"parseTypescriptTypes">
573
+ // ): TaskResponse<TaskReturn<"parseTypescriptTypes">> {
574
+ // const types = [];
575
+ // traverse.default(ast, {
576
+ // TSInterfaceDeclaration(path) {
577
+ // types.push({
578
+ // type: "interface",
579
+ // name: path.node.id.name,
580
+ // });
581
+ // },
582
+ // TSTypeAliasDeclaration(path) {
583
+ // types.push({
584
+ // type: "alias",
585
+ // name: path.node.id.name,
586
+ // });
587
+ // },
588
+ // });
589
+ // return types;
590
+ // }
591
+ /**
592
+ * Parses module exports.
593
+ *
594
+ * returns
595
+ * { success: true, exportsList: [ 'default' ] }
596
+ *
597
+ */
598
+ function parseExports({ ast }) {
599
+ const exportsList = [];
600
+ traverse.default(ast, {
601
+ ExportNamedDeclaration(path) {
602
+ path.node.specifiers.forEach((specifier) => {
603
+ if (t.isIdentifier(specifier.exported)) {
604
+ exportsList.push(specifier.exported.name);
605
+ }
606
+ });
607
+ },
608
+ ExportDefaultDeclaration(path) {
609
+ exportsList.push("default");
610
+ },
611
+ });
612
+ return { success: true, exportsList };
613
+ }
614
+ /**
615
+ * Parses React hooks usage.
616
+ */
617
+ function parseHooksUsage({ ast }) {
618
+ const hooks = [];
619
+ traverse.default(ast, {
620
+ CallExpression(path) {
621
+ const callee = path.node.callee;
622
+ if (t.isIdentifier(callee) && callee.name.startsWith("use")) {
623
+ hooks.push(callee.name);
624
+ }
625
+ },
626
+ });
627
+ return [...new Set(hooks)];
628
+ }
629
+ /**
630
+ * Parses JSX DOM hierarchy.
631
+ */
632
+ // function parseDOMHierarchy(
633
+ // ast: TaskPayload<"parseDOMHierarchy">
634
+ // ): TaskResponse<TaskReturn<"parseDOMHierarchy">> {
635
+ // function parseElement(node) {
636
+ // if (node.type !== "JSXElement") {
637
+ // return null;
638
+ // }
639
+ // return {
640
+ // tag: node.openingElement.name.name,
641
+ // children: node.children.map(parseElement).filter(Boolean),
642
+ // };
643
+ // }
644
+ // let tree = null;
645
+ // traverse.default(ast, {
646
+ // ReturnStatement(path) {
647
+ // const argument = path.node.argument;
648
+ // if (argument?.type === "JSXElement") {
649
+ // tree = parseElement(argument);
650
+ // }
651
+ // },
652
+ // });
653
+ // return tree;
654
+ // }
655
+ function parseSelector(selector) {
656
+ // ----------------------------------------------------------
657
+ // Extract attribute selectors
658
+ //
659
+ // div.card[data-id="123"][role="button"]
660
+ // ----------------------------------------------------------
661
+ const attributeRegex = /\[([^\]=]+)=['"]?([^'"\]]+)['"]?\]/g;
662
+ const attributes = {};
663
+ let match;
664
+ while ((match = attributeRegex.exec(selector)) !== null) {
665
+ const [, key, value] = match;
666
+ attributes[key] = value;
667
+ }
668
+ // ----------------------------------------------------------
669
+ // Remove attribute section
670
+ //
671
+ // div.card[data-id="123"]
672
+ // ↓
673
+ // div.card
674
+ // ----------------------------------------------------------
675
+ const selectorWithoutAttributes = selector.replace(attributeRegex, "");
676
+ // ----------------------------------------------------------
677
+ // Extract tag and classes
678
+ //
679
+ // div.card.primary
680
+ // ----------------------------------------------------------
681
+ const [tagName, ...classes] = selectorWithoutAttributes.split(".");
682
+ return {
683
+ tagName,
684
+ classes,
685
+ attributes,
686
+ };
687
+ }
688
+ /*
689
+ parseJSCode returns
690
+ returns {
691
+ "imports": [
692
+ "react",
693
+ "react",
694
+ "../../assets/react.svg",
695
+ "/vite.svg",
696
+ "./App.css",
697
+ "./Header"
698
+ ],
699
+ "exports": ["default"],
700
+ "components": [],
701
+ "functions": [
702
+ { "name": "button", "line": [8, 10] },
703
+ { "name": "App", "line": [11, 32] }
704
+ ],
705
+ "variables": [{ "name": "button", "line": [8, 10] }, { "line": [12, 12] }]
706
+ }
707
+ */
708
+ function parseJSCode({ code }) {
709
+ const ast = parse(code, {
710
+ sourceType: "module",
711
+ plugins: [
712
+ "jsx",
713
+ // "typescript",
714
+ // "classProperties",
715
+ // "topLevelAwait",
716
+ // "objectRestSpread"
717
+ ],
718
+ });
719
+ const info = {
720
+ imports: [],
721
+ exports: [],
722
+ components: [],
723
+ functions: [],
724
+ variables: [],
725
+ };
726
+ traverse.default(ast, {
727
+ ImportDeclaration({ node }) {
728
+ info.imports.push(node.source.value);
729
+ },
730
+ ExportNamedDeclaration({ node }) {
731
+ if (node.declaration &&
732
+ "id" in node.declaration &&
733
+ t.isIdentifier(node.declaration.id)) {
734
+ info.exports.push(node.declaration.id.name);
735
+ }
736
+ },
737
+ ExportDefaultDeclaration({ node }) {
738
+ info.exports.push("default");
739
+ },
740
+ FunctionDeclaration({ node }) {
741
+ if (node.id?.name) {
742
+ info.functions.push({
743
+ name: node.id.name,
744
+ line: [node.loc.start.line, node.loc.end.line],
745
+ });
746
+ }
747
+ },
748
+ VariableDeclaration({ node }) {
749
+ node.declarations.forEach((decl) => {
750
+ const name = t.isIdentifier(decl.id) ? decl.id.name : undefined;
751
+ const line = [decl.loc.start.line, decl.loc.end.line];
752
+ // Arrow function components (already handled)
753
+ if (decl.init?.type === "ArrowFunctionExpression" &&
754
+ (decl.init.body.type === "JSXElement" ||
755
+ decl.init.body.type === "BlockStatement")) {
756
+ info.functions.push({ name, line });
757
+ }
758
+ // All variables, regardless of initializer
759
+ info.variables = info.variables || [];
760
+ info.variables.push({ name, line });
761
+ });
762
+ },
763
+ });
764
+ return info;
765
+ }
766
+ function parseLLMJsonResponse(response) {
767
+ try {
768
+ return JSON.parse(response);
769
+ }
770
+ catch {
771
+ try {
772
+ const extractedData = extractJSONFromLLM(response);
773
+ // CRITICAL FIX: You must explicitly return the extracted data here
774
+ return extractedData;
775
+ }
776
+ catch (error) {
777
+ throw new LoomaError(ERROR_CODES.LLM_INVALID_RESPONSE, "LLM's response in invalid JSON", {
778
+ response,
779
+ originalError: error instanceof Error ? error.message : String(error),
780
+ });
781
+ }
782
+ }
783
+ }
784
+ function extractJSONFromLLM(llmResponse) {
785
+ try {
786
+ // 1. Clean markdown code blocks if present
787
+ let cleanedText = llmResponse
788
+ .replace(/```json/gi, "")
789
+ .replace(/```/g, "")
790
+ .trim();
791
+ // 2. Use regex to find the first '{' or '[' and the last '}' or ']'
792
+ const jsonMatch = cleanedText.match(/[\{\[]([\s\S]*?)[\}\]]/);
793
+ if (!jsonMatch) {
794
+ throw new Error("No JSON structure found in the response.");
795
+ }
796
+ // Extract the matched substring including the outer brackets
797
+ const jsonString = jsonMatch[0];
798
+ // 3. Attempt to parse the extracted string
799
+ return JSON.parse(jsonString);
800
+ }
801
+ catch (firstError) {
802
+ // 4. Fallback: Heavy cleaning for common LLM syntax errors
803
+ try {
804
+ const sanitized = repairLLMJsonString(llmResponse);
805
+ return JSON.parse(sanitized);
806
+ }
807
+ catch (secondError) {
808
+ throw new Error(`Failed to parse LLM JSON. Original error: ${firstError.message}`);
809
+ }
810
+ }
811
+ }
812
+ function repairLLMJsonString(text) {
813
+ // Isolate the text between first '{' and last '}'
814
+ const start = text.indexOf("{");
815
+ const end = text.lastIndexOf("}");
816
+ if (start === -1 || end === -1)
817
+ throw new Error("Brackets missing");
818
+ let jsonStr = text.substring(start, end + 1);
819
+ // Strip trailing commas before closing braces/brackets
820
+ jsonStr = jsonStr.replace(/,\s*([\]\}])/g, "$1");
821
+ return jsonStr;
822
+ }
823
+ export default {
824
+ parseAST,
825
+ parseCSS,
826
+ parseRoutes,
827
+ parseComponentDependencies,
828
+ parseProps,
829
+ // parseTypescriptTypes,
830
+ parseExports,
831
+ parseJSCode,
832
+ parseHooksUsage,
833
+ parseSelector,
834
+ // parseStateUsage,
835
+ // parseEventHandlers,
836
+ // parseAPICalls,
837
+ // parseDOMHierarchy,
838
+ parseLLMJsonResponse,
839
+ };
840
+ //# sourceMappingURL=parsers.js.map