@reactgraph/cli 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +319 -0
  2. package/bun.lock +527 -0
  3. package/dist/cli/components/IndexProgress.d.ts +18 -0
  4. package/dist/cli/components/IndexProgress.d.ts.map +1 -0
  5. package/dist/cli/components/IndexProgress.js +26 -0
  6. package/dist/cli/components/IndexProgress.js.map +1 -0
  7. package/dist/cli/components/InitResult.d.ts +7 -0
  8. package/dist/cli/components/InitResult.d.ts.map +1 -0
  9. package/dist/cli/components/InitResult.js +6 -0
  10. package/dist/cli/components/InitResult.js.map +1 -0
  11. package/dist/cli/index-cmd.d.ts +7 -0
  12. package/dist/cli/index-cmd.d.ts.map +1 -0
  13. package/dist/cli/index-cmd.js +28 -0
  14. package/dist/cli/index-cmd.js.map +1 -0
  15. package/dist/cli/index.d.ts +3 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +81 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/cli/init.d.ts +8 -0
  20. package/dist/cli/init.d.ts.map +1 -0
  21. package/dist/cli/init.js +77 -0
  22. package/dist/cli/init.js.map +1 -0
  23. package/dist/cli/serve.d.ts +2 -0
  24. package/dist/cli/serve.d.ts.map +1 -0
  25. package/dist/cli/serve.js +28 -0
  26. package/dist/cli/serve.js.map +1 -0
  27. package/dist/cli/unused.d.ts +2 -0
  28. package/dist/cli/unused.d.ts.map +1 -0
  29. package/dist/cli/unused.js +56 -0
  30. package/dist/cli/unused.js.map +1 -0
  31. package/dist/graph/graph.d.ts +30 -0
  32. package/dist/graph/graph.d.ts.map +1 -0
  33. package/dist/graph/graph.js +166 -0
  34. package/dist/graph/graph.js.map +1 -0
  35. package/dist/graph/index.d.ts +5 -0
  36. package/dist/graph/index.d.ts.map +1 -0
  37. package/dist/graph/index.js +5 -0
  38. package/dist/graph/index.js.map +1 -0
  39. package/dist/graph/schema.d.ts +33 -0
  40. package/dist/graph/schema.d.ts.map +1 -0
  41. package/dist/graph/schema.js +3 -0
  42. package/dist/graph/schema.js.map +1 -0
  43. package/dist/graph/serialize.d.ts +7 -0
  44. package/dist/graph/serialize.d.ts.map +1 -0
  45. package/dist/graph/serialize.js +39 -0
  46. package/dist/graph/serialize.js.map +1 -0
  47. package/dist/graph/traverse.d.ts +14 -0
  48. package/dist/graph/traverse.d.ts.map +1 -0
  49. package/dist/graph/traverse.js +50 -0
  50. package/dist/graph/traverse.js.map +1 -0
  51. package/dist/mcp/formatter.d.ts +26 -0
  52. package/dist/mcp/formatter.d.ts.map +1 -0
  53. package/dist/mcp/formatter.js +691 -0
  54. package/dist/mcp/formatter.js.map +1 -0
  55. package/dist/mcp/server.d.ts +2 -0
  56. package/dist/mcp/server.d.ts.map +1 -0
  57. package/dist/mcp/server.js +45 -0
  58. package/dist/mcp/server.js.map +1 -0
  59. package/dist/mcp/tools.d.ts +9 -0
  60. package/dist/mcp/tools.d.ts.map +1 -0
  61. package/dist/mcp/tools.js +136 -0
  62. package/dist/mcp/tools.js.map +1 -0
  63. package/dist/output/ai-context.d.ts +7 -0
  64. package/dist/output/ai-context.d.ts.map +1 -0
  65. package/dist/output/ai-context.js +26 -0
  66. package/dist/output/ai-context.js.map +1 -0
  67. package/dist/parser/extractors/api-calls.d.ts +15 -0
  68. package/dist/parser/extractors/api-calls.d.ts.map +1 -0
  69. package/dist/parser/extractors/api-calls.js +168 -0
  70. package/dist/parser/extractors/api-calls.js.map +1 -0
  71. package/dist/parser/extractors/components.d.ts +5 -0
  72. package/dist/parser/extractors/components.d.ts.map +1 -0
  73. package/dist/parser/extractors/components.js +236 -0
  74. package/dist/parser/extractors/components.js.map +1 -0
  75. package/dist/parser/extractors/context.d.ts +14 -0
  76. package/dist/parser/extractors/context.d.ts.map +1 -0
  77. package/dist/parser/extractors/context.js +196 -0
  78. package/dist/parser/extractors/context.js.map +1 -0
  79. package/dist/parser/extractors/effects.d.ts +14 -0
  80. package/dist/parser/extractors/effects.d.ts.map +1 -0
  81. package/dist/parser/extractors/effects.js +175 -0
  82. package/dist/parser/extractors/effects.js.map +1 -0
  83. package/dist/parser/extractors/hooks.d.ts +5 -0
  84. package/dist/parser/extractors/hooks.d.ts.map +1 -0
  85. package/dist/parser/extractors/hooks.js +242 -0
  86. package/dist/parser/extractors/hooks.js.map +1 -0
  87. package/dist/parser/extractors/imports.d.ts +6 -0
  88. package/dist/parser/extractors/imports.d.ts.map +1 -0
  89. package/dist/parser/extractors/imports.js +148 -0
  90. package/dist/parser/extractors/imports.js.map +1 -0
  91. package/dist/parser/extractors/index.d.ts +12 -0
  92. package/dist/parser/extractors/index.d.ts.map +1 -0
  93. package/dist/parser/extractors/index.js +11 -0
  94. package/dist/parser/extractors/index.js.map +1 -0
  95. package/dist/parser/extractors/jsx-tree.d.ts +5 -0
  96. package/dist/parser/extractors/jsx-tree.d.ts.map +1 -0
  97. package/dist/parser/extractors/jsx-tree.js +226 -0
  98. package/dist/parser/extractors/jsx-tree.js.map +1 -0
  99. package/dist/parser/extractors/routes.d.ts +13 -0
  100. package/dist/parser/extractors/routes.d.ts.map +1 -0
  101. package/dist/parser/extractors/routes.js +275 -0
  102. package/dist/parser/extractors/routes.js.map +1 -0
  103. package/dist/parser/extractors/state.d.ts +14 -0
  104. package/dist/parser/extractors/state.d.ts.map +1 -0
  105. package/dist/parser/extractors/state.js +368 -0
  106. package/dist/parser/extractors/state.js.map +1 -0
  107. package/dist/parser/extractors/types.d.ts +22 -0
  108. package/dist/parser/extractors/types.d.ts.map +1 -0
  109. package/dist/parser/extractors/types.js +51 -0
  110. package/dist/parser/extractors/types.js.map +1 -0
  111. package/dist/parser/indexer.d.ts +14 -0
  112. package/dist/parser/indexer.d.ts.map +1 -0
  113. package/dist/parser/indexer.js +167 -0
  114. package/dist/parser/indexer.js.map +1 -0
  115. package/dist/parser/pipeline.d.ts +16 -0
  116. package/dist/parser/pipeline.d.ts.map +1 -0
  117. package/dist/parser/pipeline.js +63 -0
  118. package/dist/parser/pipeline.js.map +1 -0
  119. package/dist/parser/setup.d.ts +4 -0
  120. package/dist/parser/setup.d.ts.map +1 -0
  121. package/dist/parser/setup.js +29 -0
  122. package/dist/parser/setup.js.map +1 -0
  123. package/dist/parser/walker.d.ts +6 -0
  124. package/dist/parser/walker.d.ts.map +1 -0
  125. package/dist/parser/walker.js +45 -0
  126. package/dist/parser/walker.js.map +1 -0
  127. package/dist/watcher.d.ts +12 -0
  128. package/dist/watcher.d.ts.map +1 -0
  129. package/dist/watcher.js +72 -0
  130. package/dist/watcher.js.map +1 -0
  131. package/package.json +51 -0
  132. package/src/cli/components/IndexProgress.tsx +79 -0
  133. package/src/cli/components/InitResult.tsx +28 -0
  134. package/src/cli/index-cmd.ts +41 -0
  135. package/src/cli/index.ts +92 -0
  136. package/src/cli/init.ts +97 -0
  137. package/src/cli/serve.ts +29 -0
  138. package/src/cli/unused.ts +88 -0
  139. package/src/graph/graph.ts +179 -0
  140. package/src/graph/index.ts +4 -0
  141. package/src/graph/schema.ts +68 -0
  142. package/src/graph/serialize.ts +40 -0
  143. package/src/graph/traverse.ts +66 -0
  144. package/src/mcp/formatter.ts +757 -0
  145. package/src/mcp/server.ts +59 -0
  146. package/src/mcp/tools.ts +154 -0
  147. package/src/output/ai-context.ts +29 -0
  148. package/src/parser/extractors/api-calls.ts +192 -0
  149. package/src/parser/extractors/components.ts +273 -0
  150. package/src/parser/extractors/context.ts +216 -0
  151. package/src/parser/extractors/effects.ts +205 -0
  152. package/src/parser/extractors/hooks.ts +268 -0
  153. package/src/parser/extractors/imports.ts +192 -0
  154. package/src/parser/extractors/index.ts +11 -0
  155. package/src/parser/extractors/jsx-tree.ts +271 -0
  156. package/src/parser/extractors/routes.ts +331 -0
  157. package/src/parser/extractors/state.ts +392 -0
  158. package/src/parser/extractors/types.ts +71 -0
  159. package/src/parser/indexer.ts +197 -0
  160. package/src/parser/pipeline.ts +89 -0
  161. package/src/parser/setup.ts +33 -0
  162. package/src/parser/walker.ts +61 -0
  163. package/src/watcher.ts +91 -0
  164. package/templates/CLAUDE.md +7 -0
  165. package/tests/extractors.test.ts +164 -0
  166. package/tests/fixtures/basic/src/App.tsx +12 -0
  167. package/tests/fixtures/basic/src/components/Dashboard.tsx +24 -0
  168. package/tests/fixtures/basic/src/components/MetricsCard.tsx +15 -0
  169. package/tests/fixtures/basic/src/components/Sidebar.tsx +20 -0
  170. package/tests/fixtures/basic/src/contexts/ThemeContext.tsx +16 -0
  171. package/tests/fixtures/basic/src/hooks/useAuth.ts +25 -0
  172. package/tests/fixtures/basic/src/stores/authStore.ts +15 -0
  173. package/tests/fixtures/basic/src/utils.ts +7 -0
  174. package/tests/graph.test.ts +91 -0
  175. package/tests/phase2.test.ts +309 -0
  176. package/tests/smoke.test.ts +77 -0
  177. package/tsconfig.json +20 -0
  178. package/vitest.config.ts +8 -0
@@ -0,0 +1,236 @@
1
+ import { nodeId, isPascalCase, findAll } from './types.js';
2
+ export function extractComponents(tree, filePath, sourceCode, _existingNodes) {
3
+ const nodes = [];
4
+ const edges = [];
5
+ const root = tree.rootNode;
6
+ for (let i = 0; i < root.childCount; i++) {
7
+ const child = root.child(i);
8
+ const component = detectComponent(child, filePath);
9
+ if (component)
10
+ nodes.push(component);
11
+ }
12
+ return { nodes, edges };
13
+ }
14
+ function detectComponent(node, filePath) {
15
+ // Pattern A: function_declaration
16
+ if (node.type === 'function_declaration') {
17
+ return tryFunctionComponent(node, filePath, 'none');
18
+ }
19
+ // Pattern: export_statement wrapping a declaration
20
+ if (node.type === 'export_statement') {
21
+ const isDefault = node.text.startsWith('export default');
22
+ const exportType = isDefault ? 'default' : 'named';
23
+ // export function Foo() { ... }
24
+ const funcDecl = findDirectChild(node, 'function_declaration');
25
+ if (funcDecl)
26
+ return tryFunctionComponent(funcDecl, filePath, exportType);
27
+ // export const Foo = ...
28
+ const lexDecl = findDirectChild(node, 'lexical_declaration');
29
+ if (lexDecl)
30
+ return tryLexicalComponent(lexDecl, filePath, exportType);
31
+ // export default function() { ... } (anonymous)
32
+ const funcExpr = findDirectChild(node, 'function');
33
+ if (funcExpr)
34
+ return tryFunctionComponent(funcExpr, filePath, exportType);
35
+ // export class Foo extends Component { ... }
36
+ const classDecl = findDirectChild(node, 'class_declaration');
37
+ if (classDecl)
38
+ return tryClassComponent(classDecl, filePath, exportType);
39
+ return null;
40
+ }
41
+ // Pattern B: const Foo = () => ...
42
+ if (node.type === 'lexical_declaration') {
43
+ return tryLexicalComponent(node, filePath, 'none');
44
+ }
45
+ // Pattern D: class Foo extends Component
46
+ if (node.type === 'class_declaration') {
47
+ return tryClassComponent(node, filePath, 'none');
48
+ }
49
+ return null;
50
+ }
51
+ function tryFunctionComponent(node, filePath, exportType) {
52
+ const nameNode = node.childForFieldName('name');
53
+ const name = nameNode?.text;
54
+ if (!name || !isPascalCase(name))
55
+ return null;
56
+ const body = node.childForFieldName('body');
57
+ if (!body || !containsJSX(body))
58
+ return null;
59
+ return {
60
+ id: nodeId(filePath, name),
61
+ kind: 'Component',
62
+ name,
63
+ file: filePath,
64
+ line: node.startPosition.row + 1,
65
+ exportType,
66
+ props: extractProps(node),
67
+ meta: {},
68
+ };
69
+ }
70
+ function tryLexicalComponent(node, filePath, exportType) {
71
+ const declarator = findDirectChild(node, 'variable_declarator');
72
+ if (!declarator)
73
+ return null;
74
+ const nameNode = declarator.childForFieldName('name');
75
+ const name = nameNode?.text;
76
+ if (!name || !isPascalCase(name))
77
+ return null;
78
+ const value = declarator.childForFieldName('value');
79
+ if (!value)
80
+ return null;
81
+ // Direct arrow function: const Foo = () => <div/>
82
+ if (value.type === 'arrow_function' || value.type === 'function_expression') {
83
+ const body = value.childForFieldName('body');
84
+ if (!body)
85
+ return null;
86
+ if (!containsJSX(body) && !isJSX(body))
87
+ return null;
88
+ return {
89
+ id: nodeId(filePath, name),
90
+ kind: 'Component',
91
+ name,
92
+ file: filePath,
93
+ line: node.startPosition.row + 1,
94
+ exportType,
95
+ props: extractPropsFromArrow(value),
96
+ meta: {},
97
+ };
98
+ }
99
+ // Wrapped: const Foo = React.memo(() => ...) or memo(() => ...)
100
+ if (value.type === 'call_expression') {
101
+ const wrapper = getWrapperName(value);
102
+ if (wrapper) {
103
+ const innerFn = findInnerFunction(value);
104
+ if (innerFn) {
105
+ const body = innerFn.childForFieldName('body');
106
+ if (body && (containsJSX(body) || isJSX(body))) {
107
+ return {
108
+ id: nodeId(filePath, name),
109
+ kind: 'Component',
110
+ name,
111
+ file: filePath,
112
+ line: node.startPosition.row + 1,
113
+ exportType,
114
+ props: extractPropsFromArrow(innerFn),
115
+ meta: { wrapped: wrapper },
116
+ };
117
+ }
118
+ }
119
+ }
120
+ }
121
+ return null;
122
+ }
123
+ function tryClassComponent(node, filePath, exportType) {
124
+ const nameNode = node.childForFieldName('name');
125
+ const name = nameNode?.text;
126
+ if (!name || !isPascalCase(name))
127
+ return null;
128
+ // Check extends React.Component or Component
129
+ const heritage = findDirectChild(node, 'class_heritage');
130
+ if (!heritage)
131
+ return null;
132
+ const extendsText = heritage.text;
133
+ if (!extendsText.includes('Component') && !extendsText.includes('PureComponent'))
134
+ return null;
135
+ // Check render() method has JSX
136
+ const body = node.childForFieldName('body');
137
+ if (!body)
138
+ return null;
139
+ const renderMethod = findAll(body, 'method_definition').find(m => {
140
+ const n = m.childForFieldName('name');
141
+ return n?.text === 'render';
142
+ });
143
+ if (!renderMethod || !containsJSX(renderMethod))
144
+ return null;
145
+ return {
146
+ id: nodeId(filePath, name),
147
+ kind: 'Component',
148
+ name,
149
+ file: filePath,
150
+ line: node.startPosition.row + 1,
151
+ exportType,
152
+ props: [],
153
+ meta: { isClass: true },
154
+ };
155
+ }
156
+ function containsJSX(node) {
157
+ if (isJSX(node))
158
+ return true;
159
+ for (let i = 0; i < node.childCount; i++) {
160
+ if (containsJSX(node.child(i)))
161
+ return true;
162
+ }
163
+ return false;
164
+ }
165
+ function isJSX(node) {
166
+ return (node.type === 'jsx_element' ||
167
+ node.type === 'jsx_self_closing_element' ||
168
+ node.type === 'jsx_fragment');
169
+ }
170
+ function extractProps(funcNode) {
171
+ const params = funcNode.childForFieldName('parameters');
172
+ if (!params || params.childCount === 0)
173
+ return [];
174
+ return extractParamProps(params);
175
+ }
176
+ function extractPropsFromArrow(arrowNode) {
177
+ const params = arrowNode.childForFieldName('parameters');
178
+ if (!params)
179
+ return [];
180
+ return extractParamProps(params);
181
+ }
182
+ function extractParamProps(params) {
183
+ // Look for destructured first param: ({ a, b, c })
184
+ for (let i = 0; i < params.childCount; i++) {
185
+ const param = params.child(i);
186
+ // required_parameter or just object_pattern
187
+ const objPattern = param.type === 'object_pattern'
188
+ ? param
189
+ : findDirectChild(param, 'object_pattern');
190
+ if (objPattern) {
191
+ const props = [];
192
+ for (let j = 0; j < objPattern.childCount; j++) {
193
+ const child = objPattern.child(j);
194
+ if (child.type === 'shorthand_property_identifier_pattern' || child.type === 'shorthand_property_identifier') {
195
+ props.push(child.text);
196
+ }
197
+ else if (child.type === 'pair_pattern') {
198
+ const key = child.childForFieldName('key');
199
+ if (key)
200
+ props.push(key.text);
201
+ }
202
+ }
203
+ return props;
204
+ }
205
+ }
206
+ return [];
207
+ }
208
+ function getWrapperName(callNode) {
209
+ const fn = callNode.childForFieldName('function');
210
+ if (!fn)
211
+ return null;
212
+ const WRAPPERS = ['memo', 'React.memo', 'forwardRef', 'React.forwardRef'];
213
+ if (WRAPPERS.includes(fn.text))
214
+ return fn.text;
215
+ return null;
216
+ }
217
+ function findInnerFunction(callNode) {
218
+ const args = callNode.childForFieldName('arguments');
219
+ if (!args)
220
+ return null;
221
+ for (let i = 0; i < args.childCount; i++) {
222
+ const child = args.child(i);
223
+ if (child.type === 'arrow_function' || child.type === 'function_expression') {
224
+ return child;
225
+ }
226
+ }
227
+ return null;
228
+ }
229
+ function findDirectChild(node, type) {
230
+ for (let i = 0; i < node.childCount; i++) {
231
+ if (node.child(i).type === type)
232
+ return node.child(i);
233
+ }
234
+ return null;
235
+ }
236
+ //# sourceMappingURL=components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../../../src/parser/extractors/components.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type Parser from 'tree-sitter';
2
+ import type { GraphNode } from '../../graph/schema.js';
3
+ import type { ExtractionResult } from './types.js';
4
+ /**
5
+ * Context extractor — detects React Context creation, Provider usage, and consumers.
6
+ *
7
+ * Patterns:
8
+ * const ThemeCtx = createContext(default) → Context node
9
+ * <ThemeCtx.Provider value={...}> → provides edge
10
+ * useContext(ThemeCtx) → consumes edge
11
+ * <ThemeCtx.Consumer>{fn}</ThemeCtx.Consumer> → consumes edge
12
+ */
13
+ export declare function extractContext(tree: Parser.Tree, filePath: string, sourceCode: string, existingNodes: GraphNode[]): ExtractionResult;
14
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/parser/extractors/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,uBAAuB,CAAC;AAElE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,CAAC,IAAI,EACjB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,SAAS,EAAE,GACzB,gBAAgB,CAsIlB"}
@@ -0,0 +1,196 @@
1
+ import { nodeId, findAll, findEnclosingFunction } from './types.js';
2
+ /**
3
+ * Context extractor — detects React Context creation, Provider usage, and consumers.
4
+ *
5
+ * Patterns:
6
+ * const ThemeCtx = createContext(default) → Context node
7
+ * <ThemeCtx.Provider value={...}> → provides edge
8
+ * useContext(ThemeCtx) → consumes edge
9
+ * <ThemeCtx.Consumer>{fn}</ThemeCtx.Consumer> → consumes edge
10
+ */
11
+ export function extractContext(tree, filePath, sourceCode, existingNodes) {
12
+ const nodes = [];
13
+ const edges = [];
14
+ const root = tree.rootNode;
15
+ // Build map of component/hook names in this file for edge sources
16
+ const functionNodes = new Map();
17
+ for (const n of existingNodes) {
18
+ if (n.file === filePath && (n.kind === 'Component' || n.kind === 'Hook')) {
19
+ functionNodes.set(n.name, n);
20
+ }
21
+ }
22
+ // Pass 1: Find context creation — const Ctx = createContext(...)
23
+ const contextNames = new Map(); // variable name → node id
24
+ for (let i = 0; i < root.childCount; i++) {
25
+ const child = root.child(i);
26
+ const ctx = detectContextCreation(child, filePath);
27
+ if (ctx) {
28
+ nodes.push(ctx);
29
+ contextNames.set(ctx.name, ctx.id);
30
+ }
31
+ // Also check inside export statements
32
+ if (child.type === 'export_statement') {
33
+ for (let j = 0; j < child.childCount; j++) {
34
+ const inner = child.child(j);
35
+ const ctx2 = detectContextCreation(inner, filePath);
36
+ if (ctx2) {
37
+ ctx2.exportType = child.text.startsWith('export default') ? 'default' : 'named';
38
+ nodes.push(ctx2);
39
+ contextNames.set(ctx2.name, ctx2.id);
40
+ }
41
+ }
42
+ }
43
+ }
44
+ // Also track context names imported from other files
45
+ // We look for any identifier used in useContext() or .Provider that we can resolve
46
+ const importedContexts = buildImportedContextMap(root, existingNodes);
47
+ // Pass 2: Find Provider usage in JSX — <Ctx.Provider value={...}>
48
+ const jsxOpenings = findAll(root, 'jsx_opening_element');
49
+ const jsxSelfClosing = findAll(root, 'jsx_self_closing_element');
50
+ for (const jsx of [...jsxOpenings, ...jsxSelfClosing]) {
51
+ const nameNode = jsx.childForFieldName('name') ?? jsx.child(1);
52
+ if (!nameNode || nameNode.type !== 'member_expression')
53
+ continue;
54
+ const prop = nameNode.childForFieldName('property');
55
+ if (prop?.text !== 'Provider')
56
+ continue;
57
+ const obj = nameNode.childForFieldName('object');
58
+ if (!obj)
59
+ continue;
60
+ const ctxName = obj.text;
61
+ const ctxId = contextNames.get(ctxName) ?? importedContexts.get(ctxName) ?? `unresolved:${ctxName}`;
62
+ // Find enclosing component
63
+ const enclosingFn = findEnclosingFunction(jsx);
64
+ if (!enclosingFn)
65
+ continue;
66
+ const enclosingName = getFunctionName(enclosingFn);
67
+ if (!enclosingName)
68
+ continue;
69
+ const enclosingNode = functionNodes.get(enclosingName);
70
+ if (!enclosingNode)
71
+ continue;
72
+ edges.push({
73
+ source: enclosingNode.id,
74
+ target: ctxId,
75
+ kind: 'provides',
76
+ meta: {},
77
+ });
78
+ }
79
+ // Pass 3: Find useContext() consumers
80
+ const callExprs = findAll(root, 'call_expression');
81
+ for (const call of callExprs) {
82
+ const callee = call.childForFieldName('function');
83
+ if (!callee || callee.text !== 'useContext')
84
+ continue;
85
+ const args = call.childForFieldName('arguments');
86
+ if (!args || args.childCount < 2)
87
+ continue;
88
+ const ctxArg = args.child(1); // skip "("
89
+ if (!ctxArg)
90
+ continue;
91
+ const ctxName = ctxArg.text;
92
+ const ctxId = contextNames.get(ctxName) ?? importedContexts.get(ctxName) ?? `unresolved:${ctxName}`;
93
+ const enclosingFn = findEnclosingFunction(call);
94
+ if (!enclosingFn)
95
+ continue;
96
+ const enclosingName = getFunctionName(enclosingFn);
97
+ if (!enclosingName)
98
+ continue;
99
+ const enclosingNode = functionNodes.get(enclosingName);
100
+ if (!enclosingNode)
101
+ continue;
102
+ edges.push({
103
+ source: enclosingNode.id,
104
+ target: ctxId,
105
+ kind: 'consumes',
106
+ meta: {},
107
+ });
108
+ }
109
+ // Pass 4: Find <Ctx.Consumer> pattern (legacy)
110
+ for (const jsx of [...jsxOpenings, ...jsxSelfClosing]) {
111
+ const nameNode = jsx.childForFieldName('name') ?? jsx.child(1);
112
+ if (!nameNode || nameNode.type !== 'member_expression')
113
+ continue;
114
+ const prop = nameNode.childForFieldName('property');
115
+ if (prop?.text !== 'Consumer')
116
+ continue;
117
+ const obj = nameNode.childForFieldName('object');
118
+ if (!obj)
119
+ continue;
120
+ const ctxName = obj.text;
121
+ const ctxId = contextNames.get(ctxName) ?? importedContexts.get(ctxName) ?? `unresolved:${ctxName}`;
122
+ const enclosingFn = findEnclosingFunction(jsx);
123
+ if (!enclosingFn)
124
+ continue;
125
+ const enclosingName = getFunctionName(enclosingFn);
126
+ if (!enclosingName)
127
+ continue;
128
+ const enclosingNode = functionNodes.get(enclosingName);
129
+ if (!enclosingNode)
130
+ continue;
131
+ edges.push({
132
+ source: enclosingNode.id,
133
+ target: ctxId,
134
+ kind: 'consumes',
135
+ meta: { legacy: true },
136
+ });
137
+ }
138
+ return { nodes, edges };
139
+ }
140
+ function detectContextCreation(node, filePath) {
141
+ if (node.type !== 'lexical_declaration')
142
+ return null;
143
+ const declarator = findDirectChild(node, 'variable_declarator');
144
+ if (!declarator)
145
+ return null;
146
+ const nameNode = declarator.childForFieldName('name');
147
+ const value = declarator.childForFieldName('value');
148
+ if (!nameNode || !value)
149
+ return null;
150
+ if (value.type !== 'call_expression')
151
+ return null;
152
+ const callee = value.childForFieldName('function');
153
+ if (!callee)
154
+ return null;
155
+ // Match createContext or React.createContext
156
+ if (callee.text !== 'createContext' && callee.text !== 'React.createContext')
157
+ return null;
158
+ return {
159
+ id: nodeId(filePath, nameNode.text),
160
+ kind: 'Context',
161
+ name: nameNode.text,
162
+ file: filePath,
163
+ line: node.startPosition.row + 1,
164
+ exportType: 'none',
165
+ meta: {},
166
+ };
167
+ }
168
+ function buildImportedContextMap(root, existingNodes) {
169
+ const map = new Map();
170
+ for (const n of existingNodes) {
171
+ if (n.kind === 'Context') {
172
+ map.set(n.name, n.id);
173
+ }
174
+ }
175
+ return map;
176
+ }
177
+ function getFunctionName(node) {
178
+ const nameField = node.childForFieldName('name');
179
+ if (nameField)
180
+ return nameField.text;
181
+ if (node.type === 'arrow_function' || node.type === 'function_expression') {
182
+ const parent = node.parent;
183
+ if (parent?.type === 'variable_declarator') {
184
+ return parent.childForFieldName('name')?.text ?? null;
185
+ }
186
+ }
187
+ return null;
188
+ }
189
+ function findDirectChild(node, type) {
190
+ for (let i = 0; i < node.childCount; i++) {
191
+ if (node.child(i).type === type)
192
+ return node.child(i);
193
+ }
194
+ return null;
195
+ }
196
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/parser/extractors/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type Parser from 'tree-sitter';
2
+ import type { GraphNode } from '../../graph/schema.js';
3
+ import type { ExtractionResult } from './types.js';
4
+ /**
5
+ * Effects extractor — annotates existing component/hook nodes with useEffect metadata.
6
+ * Does not create new nodes — enriches existing ones.
7
+ *
8
+ * Captures:
9
+ * - dependency array contents (or 'mount-only' / 'every-render')
10
+ * - whether cleanup function exists
11
+ * - function calls inside the effect body
12
+ */
13
+ export declare function extractEffects(tree: Parser.Tree, filePath: string, sourceCode: string, existingNodes: GraphNode[]): ExtractionResult;
14
+ //# sourceMappingURL=effects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effects.d.ts","sourceRoot":"","sources":["../../../src/parser/extractors/effects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,uBAAuB,CAAC;AAElE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,CAAC,IAAI,EACjB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,SAAS,EAAE,GACzB,gBAAgB,CA+DlB"}
@@ -0,0 +1,175 @@
1
+ import { findAll, findEnclosingFunction } from './types.js';
2
+ const EFFECT_HOOKS = new Set(['useEffect', 'useLayoutEffect', 'useInsertionEffect']);
3
+ /**
4
+ * Effects extractor — annotates existing component/hook nodes with useEffect metadata.
5
+ * Does not create new nodes — enriches existing ones.
6
+ *
7
+ * Captures:
8
+ * - dependency array contents (or 'mount-only' / 'every-render')
9
+ * - whether cleanup function exists
10
+ * - function calls inside the effect body
11
+ */
12
+ export function extractEffects(tree, filePath, sourceCode, existingNodes) {
13
+ const edges = [];
14
+ const root = tree.rootNode;
15
+ // Build function map
16
+ const functionNodes = new Map();
17
+ for (const n of existingNodes) {
18
+ if (n.file === filePath && (n.kind === 'Component' || n.kind === 'Hook')) {
19
+ functionNodes.set(n.name, n);
20
+ }
21
+ }
22
+ const callExprs = findAll(root, 'call_expression');
23
+ for (const call of callExprs) {
24
+ const callee = call.childForFieldName('function');
25
+ if (!callee || !EFFECT_HOOKS.has(callee.text))
26
+ continue;
27
+ const enclosingFn = findEnclosingFunction(call);
28
+ if (!enclosingFn)
29
+ continue;
30
+ const enclosingName = getFunctionName(enclosingFn);
31
+ if (!enclosingName)
32
+ continue;
33
+ const enclosingNode = functionNodes.get(enclosingName);
34
+ if (!enclosingNode)
35
+ continue;
36
+ const args = call.childForFieldName('arguments');
37
+ if (!args)
38
+ continue;
39
+ // Extract callback (first arg) and deps array (second arg)
40
+ const { callback, depsNode } = extractEffectArgs(args);
41
+ // Parse dependency array
42
+ let deps;
43
+ if (!depsNode) {
44
+ deps = 'every-render';
45
+ }
46
+ else if (depsNode.type === 'array' && depsNode.childCount <= 2) {
47
+ // [ ] — just brackets, empty array
48
+ deps = 'mount-only';
49
+ }
50
+ else if (depsNode.type === 'array') {
51
+ deps = extractArrayIdentifiers(depsNode);
52
+ }
53
+ else {
54
+ deps = 'unknown';
55
+ }
56
+ // Check for cleanup (return statement in callback returning a function)
57
+ const hasCleanup = callback ? detectCleanup(callback) : false;
58
+ // Extract function calls inside the effect body
59
+ const triggers = callback ? extractTriggers(callback) : [];
60
+ // Store as metadata on the enclosing node
61
+ if (!enclosingNode.meta.effects) {
62
+ enclosingNode.meta.effects = [];
63
+ }
64
+ enclosingNode.meta.effects.push({
65
+ type: callee.text,
66
+ deps,
67
+ hasCleanup,
68
+ triggers,
69
+ });
70
+ }
71
+ return { nodes: [], edges };
72
+ }
73
+ function extractEffectArgs(args) {
74
+ let callback = null;
75
+ let depsNode = null;
76
+ let argIndex = 0;
77
+ for (let i = 0; i < args.childCount; i++) {
78
+ const child = args.child(i);
79
+ if (child.type === '(' || child.type === ')' || child.type === ',')
80
+ continue;
81
+ if (argIndex === 0) {
82
+ callback = child;
83
+ }
84
+ else if (argIndex === 1) {
85
+ depsNode = child;
86
+ }
87
+ argIndex++;
88
+ }
89
+ return { callback, depsNode };
90
+ }
91
+ function extractArrayIdentifiers(arrayNode) {
92
+ const ids = [];
93
+ for (let i = 0; i < arrayNode.childCount; i++) {
94
+ const child = arrayNode.child(i);
95
+ if (child.type === 'identifier') {
96
+ ids.push(child.text);
97
+ }
98
+ else if (child.type === 'member_expression') {
99
+ ids.push(child.text);
100
+ }
101
+ }
102
+ return ids;
103
+ }
104
+ function detectCleanup(callback) {
105
+ // Look for a return statement that returns a function
106
+ const returns = findAll(callback, 'return_statement');
107
+ for (const ret of returns) {
108
+ // Make sure this return is directly in the callback, not in a nested function
109
+ const enclosing = findEnclosingFunction(ret);
110
+ if (enclosing && enclosing !== callback) {
111
+ // Check if enclosing is the callback itself
112
+ // For arrow functions, the callback IS the arrow_function
113
+ if (enclosing.parent !== callback && enclosing !== callback)
114
+ continue;
115
+ }
116
+ const val = ret.child(1);
117
+ if (!val)
118
+ continue;
119
+ if (val.type === 'arrow_function' ||
120
+ val.type === 'function_expression' ||
121
+ val.type === 'identifier' // return cleanup;
122
+ ) {
123
+ return true;
124
+ }
125
+ }
126
+ return false;
127
+ }
128
+ function extractTriggers(callback) {
129
+ const triggers = [];
130
+ const calls = findAll(callback, 'call_expression');
131
+ for (const call of calls) {
132
+ const callee = call.childForFieldName('function');
133
+ if (!callee)
134
+ continue;
135
+ // Skip nested function definitions
136
+ const enclosing = findEnclosingFunction(call);
137
+ if (enclosing && enclosing !== callback && !isDirectChild(enclosing, callback))
138
+ continue;
139
+ if (callee.type === 'identifier') {
140
+ triggers.push(callee.text);
141
+ }
142
+ else if (callee.type === 'member_expression') {
143
+ triggers.push(callee.text);
144
+ }
145
+ }
146
+ return triggers;
147
+ }
148
+ function isDirectChild(inner, outer) {
149
+ let current = inner.parent;
150
+ while (current) {
151
+ if (current === outer)
152
+ return true;
153
+ if (current.type === 'arrow_function' ||
154
+ current.type === 'function_expression' ||
155
+ current.type === 'function_declaration') {
156
+ // Hit another function boundary — not direct
157
+ return current === outer;
158
+ }
159
+ current = current.parent;
160
+ }
161
+ return false;
162
+ }
163
+ function getFunctionName(node) {
164
+ const nameField = node.childForFieldName('name');
165
+ if (nameField)
166
+ return nameField.text;
167
+ if (node.type === 'arrow_function' || node.type === 'function_expression') {
168
+ const parent = node.parent;
169
+ if (parent?.type === 'variable_declarator') {
170
+ return parent.childForFieldName('name')?.text ?? null;
171
+ }
172
+ }
173
+ return null;
174
+ }
175
+ //# sourceMappingURL=effects.js.map