codesift-mcp 0.5.24 → 0.5.27

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 (217) hide show
  1. package/README.md +4 -4
  2. package/dist/cli/commands.d.ts.map +1 -1
  3. package/dist/cli/commands.js +28 -0
  4. package/dist/cli/commands.js.map +1 -1
  5. package/dist/cli/help.js +1 -1
  6. package/dist/cli/help.js.map +1 -1
  7. package/dist/cli/hooks.d.ts +4 -0
  8. package/dist/cli/hooks.d.ts.map +1 -1
  9. package/dist/cli/hooks.js +15 -4
  10. package/dist/cli/hooks.js.map +1 -1
  11. package/dist/cli/journal-commands.d.ts +9 -0
  12. package/dist/cli/journal-commands.d.ts.map +1 -0
  13. package/dist/cli/journal-commands.js +160 -0
  14. package/dist/cli/journal-commands.js.map +1 -0
  15. package/dist/cli/wiki-commands.d.ts.map +1 -1
  16. package/dist/cli/wiki-commands.js +5 -0
  17. package/dist/cli/wiki-commands.js.map +1 -1
  18. package/dist/instructions.d.ts +1 -1
  19. package/dist/instructions.d.ts.map +1 -1
  20. package/dist/instructions.js +41 -47
  21. package/dist/instructions.js.map +1 -1
  22. package/dist/parser/extractors/_shared.d.ts +2 -0
  23. package/dist/parser/extractors/_shared.d.ts.map +1 -1
  24. package/dist/parser/extractors/_shared.js +4 -0
  25. package/dist/parser/extractors/_shared.js.map +1 -1
  26. package/dist/parser/extractors/typescript.d.ts.map +1 -1
  27. package/dist/parser/extractors/typescript.js +606 -31
  28. package/dist/parser/extractors/typescript.js.map +1 -1
  29. package/dist/parser/parse-worker.d.ts +2 -0
  30. package/dist/parser/parse-worker.d.ts.map +1 -0
  31. package/dist/parser/parse-worker.js +47 -0
  32. package/dist/parser/parse-worker.js.map +1 -0
  33. package/dist/parser/parser-manager.d.ts +10 -1
  34. package/dist/parser/parser-manager.d.ts.map +1 -1
  35. package/dist/parser/parser-manager.js +40 -2
  36. package/dist/parser/parser-manager.js.map +1 -1
  37. package/dist/parser/parser-pool.d.ts +31 -0
  38. package/dist/parser/parser-pool.d.ts.map +1 -0
  39. package/dist/parser/parser-pool.js +211 -0
  40. package/dist/parser/parser-pool.js.map +1 -0
  41. package/dist/register-tools.d.ts.map +1 -1
  42. package/dist/register-tools.js +382 -35
  43. package/dist/register-tools.js.map +1 -1
  44. package/dist/retrieval/codebase-retrieval.d.ts.map +1 -1
  45. package/dist/retrieval/codebase-retrieval.js +9 -1
  46. package/dist/retrieval/codebase-retrieval.js.map +1 -1
  47. package/dist/search/chunker.d.ts +1 -0
  48. package/dist/search/chunker.d.ts.map +1 -1
  49. package/dist/search/chunker.js +8 -1
  50. package/dist/search/chunker.js.map +1 -1
  51. package/dist/server-helpers.d.ts +27 -0
  52. package/dist/server-helpers.d.ts.map +1 -1
  53. package/dist/server-helpers.js +90 -9
  54. package/dist/server-helpers.js.map +1 -1
  55. package/dist/server.js +31 -0
  56. package/dist/server.js.map +1 -1
  57. package/dist/storage/index-store.d.ts +41 -4
  58. package/dist/storage/index-store.d.ts.map +1 -1
  59. package/dist/storage/index-store.js +89 -6
  60. package/dist/storage/index-store.js.map +1 -1
  61. package/dist/storage/watcher.d.ts +33 -6
  62. package/dist/storage/watcher.d.ts.map +1 -1
  63. package/dist/storage/watcher.js +73 -34
  64. package/dist/storage/watcher.js.map +1 -1
  65. package/dist/storage/workspace-resolver.d.ts +15 -0
  66. package/dist/storage/workspace-resolver.d.ts.map +1 -0
  67. package/dist/storage/workspace-resolver.js +130 -0
  68. package/dist/storage/workspace-resolver.js.map +1 -0
  69. package/dist/tools/_helpers.d.ts +37 -0
  70. package/dist/tools/_helpers.d.ts.map +1 -0
  71. package/dist/tools/_helpers.js +31 -0
  72. package/dist/tools/_helpers.js.map +1 -0
  73. package/dist/tools/constant-resolution-tools.d.ts +8 -0
  74. package/dist/tools/constant-resolution-tools.d.ts.map +1 -0
  75. package/dist/tools/constant-resolution-tools.js +68 -0
  76. package/dist/tools/constant-resolution-tools.js.map +1 -0
  77. package/dist/tools/graph-tools.d.ts +27 -5
  78. package/dist/tools/graph-tools.d.ts.map +1 -1
  79. package/dist/tools/graph-tools.js +141 -18
  80. package/dist/tools/graph-tools.js.map +1 -1
  81. package/dist/tools/hotspot-tools.d.ts +6 -0
  82. package/dist/tools/hotspot-tools.d.ts.map +1 -1
  83. package/dist/tools/hotspot-tools.js +48 -8
  84. package/dist/tools/hotspot-tools.js.map +1 -1
  85. package/dist/tools/impact-tools.d.ts.map +1 -1
  86. package/dist/tools/impact-tools.js +9 -2
  87. package/dist/tools/impact-tools.js.map +1 -1
  88. package/dist/tools/index-tools.d.ts +15 -1
  89. package/dist/tools/index-tools.d.ts.map +1 -1
  90. package/dist/tools/index-tools.js +161 -16
  91. package/dist/tools/index-tools.js.map +1 -1
  92. package/dist/tools/journal-generator-helpers.d.ts +55 -0
  93. package/dist/tools/journal-generator-helpers.d.ts.map +1 -0
  94. package/dist/tools/journal-generator-helpers.js +250 -0
  95. package/dist/tools/journal-generator-helpers.js.map +1 -0
  96. package/dist/tools/journal-generator.d.ts +48 -0
  97. package/dist/tools/journal-generator.d.ts.map +1 -0
  98. package/dist/tools/journal-generator.js +204 -0
  99. package/dist/tools/journal-generator.js.map +1 -0
  100. package/dist/tools/journal-git-client.d.ts +20 -0
  101. package/dist/tools/journal-git-client.d.ts.map +1 -0
  102. package/dist/tools/journal-git-client.js +57 -0
  103. package/dist/tools/journal-git-client.js.map +1 -0
  104. package/dist/tools/journal-llm-client.d.ts +57 -0
  105. package/dist/tools/journal-llm-client.d.ts.map +1 -0
  106. package/dist/tools/journal-llm-client.js +175 -0
  107. package/dist/tools/journal-llm-client.js.map +1 -0
  108. package/dist/tools/journal-migrator.d.ts +22 -0
  109. package/dist/tools/journal-migrator.d.ts.map +1 -0
  110. package/dist/tools/journal-migrator.js +183 -0
  111. package/dist/tools/journal-migrator.js.map +1 -0
  112. package/dist/tools/journal-phase-detector.d.ts +26 -0
  113. package/dist/tools/journal-phase-detector.d.ts.map +1 -0
  114. package/dist/tools/journal-phase-detector.js +126 -0
  115. package/dist/tools/journal-phase-detector.js.map +1 -0
  116. package/dist/tools/journal-sentinel.d.ts +15 -0
  117. package/dist/tools/journal-sentinel.d.ts.map +1 -0
  118. package/dist/tools/journal-sentinel.js +110 -0
  119. package/dist/tools/journal-sentinel.js.map +1 -0
  120. package/dist/tools/journal-templates.d.ts +28 -0
  121. package/dist/tools/journal-templates.d.ts.map +1 -0
  122. package/dist/tools/journal-templates.js +93 -0
  123. package/dist/tools/journal-templates.js.map +1 -0
  124. package/dist/tools/pattern-tools.d.ts +2 -0
  125. package/dist/tools/pattern-tools.d.ts.map +1 -1
  126. package/dist/tools/pattern-tools.js +73 -0
  127. package/dist/tools/pattern-tools.js.map +1 -1
  128. package/dist/tools/perf-tools.d.ts.map +1 -1
  129. package/dist/tools/perf-tools.js +6 -0
  130. package/dist/tools/perf-tools.js.map +1 -1
  131. package/dist/tools/plan-turn-tools.d.ts.map +1 -1
  132. package/dist/tools/plan-turn-tools.js +38 -2
  133. package/dist/tools/plan-turn-tools.js.map +1 -1
  134. package/dist/tools/project-tools.d.ts +6 -0
  135. package/dist/tools/project-tools.d.ts.map +1 -1
  136. package/dist/tools/project-tools.js +65 -25
  137. package/dist/tools/project-tools.js.map +1 -1
  138. package/dist/tools/python-constants-tools.d.ts +6 -0
  139. package/dist/tools/python-constants-tools.d.ts.map +1 -1
  140. package/dist/tools/python-constants-tools.js.map +1 -1
  141. package/dist/tools/react-tools.d.ts +44 -1
  142. package/dist/tools/react-tools.d.ts.map +1 -1
  143. package/dist/tools/react-tools.js +142 -9
  144. package/dist/tools/react-tools.js.map +1 -1
  145. package/dist/tools/search-tools.d.ts.map +1 -1
  146. package/dist/tools/search-tools.js +21 -2
  147. package/dist/tools/search-tools.js.map +1 -1
  148. package/dist/tools/status-tools.d.ts +10 -0
  149. package/dist/tools/status-tools.d.ts.map +1 -1
  150. package/dist/tools/status-tools.js +53 -2
  151. package/dist/tools/status-tools.js.map +1 -1
  152. package/dist/tools/symbol-tools.d.ts +13 -0
  153. package/dist/tools/symbol-tools.d.ts.map +1 -1
  154. package/dist/tools/symbol-tools.js +38 -0
  155. package/dist/tools/symbol-tools.js.map +1 -1
  156. package/dist/tools/typescript-constants-tools.d.ts +6 -0
  157. package/dist/tools/typescript-constants-tools.d.ts.map +1 -0
  158. package/dist/tools/typescript-constants-tools.js +687 -0
  159. package/dist/tools/typescript-constants-tools.js.map +1 -0
  160. package/dist/tools/wiki-hub-ranker.d.ts +36 -0
  161. package/dist/tools/wiki-hub-ranker.d.ts.map +1 -0
  162. package/dist/tools/wiki-hub-ranker.js +72 -0
  163. package/dist/tools/wiki-hub-ranker.js.map +1 -0
  164. package/dist/tools/wiki-lint.d.ts +7 -2
  165. package/dist/tools/wiki-lint.d.ts.map +1 -1
  166. package/dist/tools/wiki-lint.js +73 -1
  167. package/dist/tools/wiki-lint.js.map +1 -1
  168. package/dist/tools/wiki-manifest.d.ts +129 -21
  169. package/dist/tools/wiki-manifest.d.ts.map +1 -1
  170. package/dist/tools/wiki-manifest.js +16 -9
  171. package/dist/tools/wiki-manifest.js.map +1 -1
  172. package/dist/tools/wiki-module-builder.d.ts +42 -0
  173. package/dist/tools/wiki-module-builder.d.ts.map +1 -0
  174. package/dist/tools/wiki-module-builder.js +449 -0
  175. package/dist/tools/wiki-module-builder.js.map +1 -0
  176. package/dist/tools/wiki-overview-sources.d.ts +26 -0
  177. package/dist/tools/wiki-overview-sources.d.ts.map +1 -0
  178. package/dist/tools/wiki-overview-sources.js +128 -0
  179. package/dist/tools/wiki-overview-sources.js.map +1 -0
  180. package/dist/tools/wiki-page-generators.d.ts +19 -3
  181. package/dist/tools/wiki-page-generators.d.ts.map +1 -1
  182. package/dist/tools/wiki-page-generators.js +156 -22
  183. package/dist/tools/wiki-page-generators.js.map +1 -1
  184. package/dist/tools/wiki-tools.d.ts +20 -0
  185. package/dist/tools/wiki-tools.d.ts.map +1 -1
  186. package/dist/tools/wiki-tools.js +181 -29
  187. package/dist/tools/wiki-tools.js.map +1 -1
  188. package/dist/tools/workspace-scope-helper.d.ts +21 -0
  189. package/dist/tools/workspace-scope-helper.d.ts.map +1 -0
  190. package/dist/tools/workspace-scope-helper.js +50 -0
  191. package/dist/tools/workspace-scope-helper.js.map +1 -0
  192. package/dist/tools/workspace-tools.d.ts +74 -0
  193. package/dist/tools/workspace-tools.d.ts.map +1 -0
  194. package/dist/tools/workspace-tools.js +366 -0
  195. package/dist/tools/workspace-tools.js.map +1 -0
  196. package/dist/types.d.ts +65 -0
  197. package/dist/types.d.ts.map +1 -1
  198. package/dist/utils/import-graph.d.ts +27 -0
  199. package/dist/utils/import-graph.d.ts.map +1 -1
  200. package/dist/utils/import-graph.js +331 -8
  201. package/dist/utils/import-graph.js.map +1 -1
  202. package/dist/utils/ts-imports.d.ts +30 -0
  203. package/dist/utils/ts-imports.d.ts.map +1 -0
  204. package/dist/utils/ts-imports.js +168 -0
  205. package/dist/utils/ts-imports.js.map +1 -0
  206. package/dist/utils/tsconfig-paths.d.ts +59 -0
  207. package/dist/utils/tsconfig-paths.d.ts.map +1 -0
  208. package/dist/utils/tsconfig-paths.js +176 -0
  209. package/dist/utils/tsconfig-paths.js.map +1 -0
  210. package/dist/utils/walk.d.ts.map +1 -1
  211. package/dist/utils/walk.js +1 -0
  212. package/dist/utils/walk.js.map +1 -1
  213. package/package.json +13 -3
  214. package/rules/codesift.md +19 -2
  215. package/rules/codesift.mdc +2 -2
  216. package/rules/codex.md +13 -2
  217. package/rules/gemini.md +13 -2
@@ -97,31 +97,72 @@ const REACT_COMPONENT_BASES = new Set([
97
97
  "Component", "PureComponent",
98
98
  ]);
99
99
  /**
100
- * Check if a class extends React.Component or React.PureComponent.
101
- * Handles: `extends Component`, `extends React.Component`, `extends PureComponent`.
100
+ * Strip type arguments, expand intersection/union, preserve qualified names.
101
+ * Returns string[] (not string|null) so intersection/union types expand to
102
+ * multiple elements via flatMap at the call site. AC #6 normalization rule:
103
+ * - identifier → ["Foo"] (extends Foo — runtime value position)
104
+ * - type_identifier → ["I"] (implements I — type position)
105
+ * - member_expression / nested_type_identifier → ["ns.Base"] (qualified preserved)
106
+ * - generic_type → recurse into name field, drop type_arguments → ["Box"]
107
+ * - intersection_type / union_type → flatMap across members → ["A", "B"]
108
+ *
109
+ * IMPORTANT: tree-sitter-typescript parses `extends Foo` as `identifier` NOT
110
+ * `type_identifier`. Without the identifier case, all standard ES6 inheritance
111
+ * is silently dropped (gemini adversarial pre-execute finding).
102
112
  */
103
- function isReactClassComponent(node) {
104
- // Look for extends clause: class_heritage → extends_clause → type_identifier or member_expression
113
+ function extractHeritageNames(node) {
114
+ if (node.type === "identifier")
115
+ return [node.text];
116
+ if (node.type === "type_identifier")
117
+ return [node.text];
118
+ // member_expression / nested_type_identifier text is `ns.Base`. Strip any
119
+ // whitespace defensively — uncommon but legal in source like `extends ns . Base`.
120
+ if (node.type === "member_expression" || node.type === "nested_type_identifier") {
121
+ return [node.text.replace(/\s+/g, "")];
122
+ }
123
+ if (node.type === "generic_type") {
124
+ const innerType = node.childForFieldName("name") ?? node.namedChildren[0];
125
+ return innerType ? extractHeritageNames(innerType) : [];
126
+ }
127
+ if (node.type === "intersection_type" || node.type === "union_type") {
128
+ return node.namedChildren.flatMap((child) => extractHeritageNames(child));
129
+ }
130
+ return [];
131
+ }
132
+ /** Walk class_heritage and return separate extends/implements lists. */
133
+ function getClassHeritage(node) {
134
+ const extendsList = [];
135
+ const implementsList = [];
105
136
  for (const child of node.namedChildren) {
106
- if (child.type === "class_heritage") {
107
- for (const clause of child.namedChildren) {
108
- if (clause.type === "extends_clause") {
109
- const superclass = clause.namedChildren[0];
110
- if (!superclass)
111
- continue;
112
- // extends Component / extends PureComponent
113
- if (superclass.type === "identifier" && REACT_COMPONENT_BASES.has(superclass.text))
114
- return true;
115
- // extends React.Component / extends React.PureComponent
116
- if (superclass.type === "member_expression") {
117
- const prop = superclass.childForFieldName("property");
118
- if (prop && REACT_COMPONENT_BASES.has(prop.text))
119
- return true;
120
- }
121
- }
137
+ if (child.type !== "class_heritage")
138
+ continue;
139
+ for (const clause of child.namedChildren) {
140
+ const target = clause.type === "extends_clause" ? extendsList :
141
+ clause.type === "implements_clause" ? implementsList : null;
142
+ if (!target)
143
+ continue;
144
+ for (const typeNode of clause.namedChildren) {
145
+ target.push(...extractHeritageNames(typeNode));
122
146
  }
123
147
  }
124
148
  }
149
+ return { extends: extendsList, implements: implementsList };
150
+ }
151
+ /**
152
+ * Check if a class extends React.Component or React.PureComponent.
153
+ * Uses getClassHeritage (shared helper) — NO duplicate AST walk (CQ14).
154
+ */
155
+ function isReactClassComponent(node) {
156
+ const { extends: extendsList } = getClassHeritage(node);
157
+ for (const name of extendsList) {
158
+ // "Component" / "PureComponent" — bare identifier match
159
+ if (REACT_COMPONENT_BASES.has(name))
160
+ return true;
161
+ // "React.Component" / "React.PureComponent" — qualified match
162
+ const lastDot = name.lastIndexOf(".");
163
+ if (lastDot >= 0 && REACT_COMPONENT_BASES.has(name.slice(lastDot + 1)))
164
+ return true;
165
+ }
125
166
  return false;
126
167
  }
127
168
  /**
@@ -267,10 +308,18 @@ function getSignature(node, source) {
267
308
  const params = node.childForFieldName("parameters");
268
309
  if (!params)
269
310
  return undefined;
270
- let sig = source.slice(params.startIndex, params.endIndex);
311
+ let sig = "";
312
+ // Generics: `<T extends Foo, U = string>` — prepend before parameters when present.
313
+ const typeParams = node.childForFieldName("type_parameters");
314
+ if (typeParams) {
315
+ sig += source.slice(typeParams.startIndex, typeParams.endIndex);
316
+ }
317
+ sig += source.slice(params.startIndex, params.endIndex);
318
+ // return_type field already includes the leading `:` (it points to a
319
+ // type_annotation node containing colon + type) — no `: ` prefix here.
271
320
  const returnType = node.childForFieldName("return_type");
272
321
  if (returnType) {
273
- sig += ": " + source.slice(returnType.startIndex, returnType.endIndex);
322
+ sig += source.slice(returnType.startIndex, returnType.endIndex);
274
323
  }
275
324
  return sig;
276
325
  }
@@ -287,22 +336,270 @@ function getTestName(node) {
287
336
  }
288
337
  return firstArg.text;
289
338
  }
339
+ /**
340
+ * Recognize the LHS of a CommonJS exports assignment.
341
+ *
342
+ * Returns:
343
+ * { kind: "module_exports", property: null } for `module.exports = ...`
344
+ * { kind: "module_exports", property: "foo" } for `module.exports.foo = ...`
345
+ * { kind: "exports", property: "foo" } for `exports.foo = ...`
346
+ * null for any other LHS
347
+ */
348
+ function parseCjsLhs(lhs) {
349
+ if (lhs.type !== "member_expression")
350
+ return null;
351
+ const obj = lhs.childForFieldName("object");
352
+ const prop = lhs.childForFieldName("property");
353
+ if (!obj || !prop)
354
+ return null;
355
+ // exports.foo
356
+ if (obj.type === "identifier" && obj.text === "exports") {
357
+ return { kind: "exports", property: prop.text };
358
+ }
359
+ // module.exports = ...
360
+ if (obj.type === "identifier" && obj.text === "module" && prop.text === "exports") {
361
+ return { kind: "module_exports", property: null };
362
+ }
363
+ // module.exports.foo = ...
364
+ if (obj.type === "member_expression") {
365
+ const innerObj = obj.childForFieldName("object");
366
+ const innerProp = obj.childForFieldName("property");
367
+ if (innerObj?.type === "identifier" && innerObj.text === "module" &&
368
+ innerProp?.text === "exports") {
369
+ return { kind: "module_exports", property: prop.text };
370
+ }
371
+ }
372
+ return null;
373
+ }
374
+ /** Pick a SymbolKind for the RHS of a CJS property assignment. */
375
+ function cjsValueKind(value, name) {
376
+ if (value.type === "arrow_function" || value.type === "function_expression") {
377
+ return classifyReactKind(name, value);
378
+ }
379
+ if (value.type === "class_expression")
380
+ return "class";
381
+ if (SCREAMING_CASE_RE.test(name))
382
+ return "constant";
383
+ return "variable";
384
+ }
385
+ /**
386
+ * Handle a CommonJS exports assignment. Returns true when the assignment was
387
+ * recognized as a CJS export pattern (caller should `return` and skip default
388
+ * walking), false when the assignment is unrelated.
389
+ *
390
+ * Behavior:
391
+ * - `module.exports = identifier` → defer: tag matching symbol in post-pass
392
+ * - `module.exports = { foo, bar }` → defer shorthand keys; emit pair-with-fn keys
393
+ * - `module.exports = arrow|function|class` → emit `default_export` symbol
394
+ * - `module.exports.foo = expr` → emit `foo` symbol with is_exported=true
395
+ * - `exports.foo = expr` → emit `foo` symbol with is_exported=true
396
+ */
397
+ function handleCjsExport(assign, stmt, parentId, source, filePath, repo, out, cjsExported) {
398
+ const lhs = assign.childForFieldName("left");
399
+ const rhs = assign.childForFieldName("right");
400
+ if (!lhs || !rhs)
401
+ return false;
402
+ const target = parseCjsLhs(lhs);
403
+ if (!target)
404
+ return false;
405
+ // module.exports.<X> / exports.<X> → emit a fresh exported symbol for X
406
+ if (target.property) {
407
+ const name = target.property;
408
+ const kind = cjsValueKind(rhs, name);
409
+ out.push(makeSymbol(stmt, name, kind, filePath, source, repo, {
410
+ parentId,
411
+ docstring: getDocstring(stmt, source),
412
+ signature: (rhs.type === "arrow_function" || rhs.type === "function_expression")
413
+ ? getSignature(rhs, source) : undefined,
414
+ is_exported: true,
415
+ }));
416
+ cjsExported.add(name);
417
+ return true;
418
+ }
419
+ // module.exports = ...
420
+ switch (rhs.type) {
421
+ case "identifier": {
422
+ // Defer to post-pass: tag the matching declaration.
423
+ cjsExported.add(rhs.text);
424
+ return true;
425
+ }
426
+ case "object": {
427
+ for (const member of rhs.namedChildren) {
428
+ if (member.type === "shorthand_property_identifier") {
429
+ cjsExported.add(member.text);
430
+ }
431
+ else if (member.type === "pair") {
432
+ const keyNode = member.childForFieldName("key");
433
+ const valNode = member.childForFieldName("value");
434
+ if (!keyNode || !valNode)
435
+ continue;
436
+ const keyName = keyNode.text.replace(/^['"`]|['"`]$/g, "");
437
+ if (valNode.type === "identifier") {
438
+ // { foo: someFn } — defer; tag someFn AND keyName both as exported.
439
+ cjsExported.add(valNode.text);
440
+ cjsExported.add(keyName);
441
+ }
442
+ else if (valNode.type === "arrow_function" || valNode.type === "function_expression") {
443
+ // { handler: () => {} } — emit a fresh exported symbol under keyName
444
+ const kind = classifyReactKind(keyName, valNode);
445
+ out.push(makeSymbol(member, keyName, kind, filePath, source, repo, {
446
+ parentId,
447
+ signature: getSignature(valNode, source),
448
+ is_exported: true,
449
+ }));
450
+ }
451
+ }
452
+ }
453
+ return true;
454
+ }
455
+ case "arrow_function":
456
+ case "function_expression":
457
+ case "class_expression": {
458
+ // Anonymous default export: `module.exports = () => {}`
459
+ out.push(makeSymbol(stmt, "default", "default_export", filePath, source, repo, {
460
+ parentId,
461
+ signature: (rhs.type === "arrow_function" || rhs.type === "function_expression")
462
+ ? getSignature(rhs, source) : undefined,
463
+ is_exported: true,
464
+ }));
465
+ return true;
466
+ }
467
+ default:
468
+ // Unknown RHS shape (call_expression, member_expression, etc.) —
469
+ // not a recognized CJS pattern, fall through to default handling.
470
+ return false;
471
+ }
472
+ }
473
+ /**
474
+ * Extract method-shaped members from an object literal `{ foo() {}, bar: () => {} }`.
475
+ * Mutates `symbols` in place so the caller (lexical_declaration handler) can keep
476
+ * its short-circuit `return` without losing object-method visibility.
477
+ *
478
+ * Handles two shapes commonly used as JS controller / handler objects:
479
+ * - method shorthand: { create() { ... } } → method_definition
480
+ * - arrow assignment: { onClick: () => {...} } → pair(arrow_function)
481
+ * - function value: { handler: function(){...} } → pair(function_expression)
482
+ */
483
+ function extractObjectLiteralMethods(objectNode, parentId, source, filePath, repo, out) {
484
+ for (const child of objectNode.namedChildren) {
485
+ if (child.type === "method_definition") {
486
+ const methodName = getNodeName(child);
487
+ if (!methodName)
488
+ continue;
489
+ out.push(makeSymbol(child, methodName, "method", filePath, source, repo, {
490
+ parentId,
491
+ signature: getSignature(child, source),
492
+ }));
493
+ continue;
494
+ }
495
+ if (child.type === "pair") {
496
+ const keyNode = child.childForFieldName("key");
497
+ const valNode = child.childForFieldName("value");
498
+ if (!keyNode || !valNode)
499
+ continue;
500
+ if (valNode.type !== "arrow_function" && valNode.type !== "function_expression")
501
+ continue;
502
+ const methodName = keyNode.text.replace(/^['"`]|['"`]$/g, "");
503
+ // Honor React conventions even on object members (e.g. an exported
504
+ // hooks object): use[A-Z] → hook, PascalCase + JSX → component.
505
+ const kind = classifyReactKind(methodName, valNode);
506
+ out.push(makeSymbol(child, methodName, kind === "function" ? "method" : kind, filePath, source, repo, {
507
+ parentId,
508
+ signature: getSignature(valNode, source),
509
+ }));
510
+ }
511
+ }
512
+ }
290
513
  // --- Main extractor ---
514
+ /** True if the declaration has an `export` keyword child (modifier-based export). */
515
+ function hasExportModifier(node) {
516
+ for (const child of node.children) {
517
+ if (child.type === "export")
518
+ return true;
519
+ }
520
+ return false;
521
+ }
522
+ /** True if the function/method/arrow has an `async` keyword child. */
523
+ function hasAsyncModifier(node) {
524
+ for (const child of node.children) {
525
+ if (child.type === "async")
526
+ return true;
527
+ }
528
+ return false;
529
+ }
530
+ /** Modifier keyword tokens recognized for methods and fields, in two forms:
531
+ * - bare keyword child (e.g., `static`, `readonly`, `abstract`, `declare`)
532
+ * - wrapper named node (e.g., `override_modifier` containing the `override` keyword,
533
+ * `accessibility_modifier` containing `public`/`private`/`protected`)
534
+ * Order in the resulting array follows source-token order for stability. */
535
+ const MODIFIER_KEYWORD_TOKENS = new Set([
536
+ "static", "abstract", "readonly", "declare",
537
+ ]);
538
+ /** Collect modifier keywords from a method_definition / field_definition node.
539
+ * Handles bare keyword tokens AND named wrapper nodes. Order: source order. */
540
+ function getModifiers(node) {
541
+ const mods = [];
542
+ for (const child of node.children) {
543
+ if (MODIFIER_KEYWORD_TOKENS.has(child.type)) {
544
+ mods.push(child.type);
545
+ }
546
+ else if (child.type === "accessibility_modifier") {
547
+ mods.push(child.text); // "public" | "private" | "protected"
548
+ }
549
+ else if (child.type === "override_modifier") {
550
+ mods.push("override");
551
+ }
552
+ }
553
+ return mods;
554
+ }
555
+ /** Detect getter/setter/auto-accessor kind on method_definition.
556
+ * Returns "get" | "set" | "accessor" | undefined. */
557
+ function getAccessorKind(node) {
558
+ // tree-sitter-typescript exposes `get`/`set` as keyword tokens directly under
559
+ // method_definition. TS 4.9 `accessor` keyword for auto-accessor fields lives
560
+ // similarly. Scan children for the first matching token.
561
+ for (const child of node.children) {
562
+ if (child.type === "get")
563
+ return "get";
564
+ if (child.type === "set")
565
+ return "set";
566
+ if (child.type === "accessor")
567
+ return "accessor";
568
+ }
569
+ return undefined;
570
+ }
291
571
  export function extractTypeScriptSymbols(tree, filePath, source, repo) {
292
572
  const symbols = [];
293
- function walk(node, parentId) {
573
+ const localReExported = new Set();
574
+ // CommonJS exports — names referenced by `module.exports = X` or
575
+ // `module.exports = { foo, bar }`. Resolved in a post-pass against
576
+ // already-emitted symbols so a `function foo(){}` declared earlier in the
577
+ // file gets its `is_exported` flag set when the exports assignment is seen.
578
+ const cjsExported = new Set();
579
+ function walk(node, parentId, isExported = false) {
294
580
  switch (node.type) {
295
- case "function_declaration": {
581
+ case "function_declaration":
582
+ case "generator_function_declaration":
583
+ case "function_signature": {
584
+ // function_signature: ambient declaration body — `declare function foo(): void`
585
+ // and `declare module "x" { export function bar(): void }`. Same shape as
586
+ // function_declaration for our purposes (name field + parameters + return_type).
296
587
  const name = getNodeName(node);
297
588
  if (name) {
298
589
  // React detection: hook (useX) or component (PascalCase + JSX return)
299
590
  const kind = classifyReactKind(name, node);
300
591
  const decorators = getDecorators(node);
592
+ const exported = isExported || hasExportModifier(node);
593
+ const isAsync = hasAsyncModifier(node);
594
+ const isGenerator = node.type === "generator_function_declaration";
301
595
  const sym = makeSymbol(node, name, kind, filePath, source, repo, {
302
596
  parentId,
303
597
  docstring: getDocstring(node, source),
304
598
  signature: getSignature(node, source),
305
599
  decorators: decorators.length > 0 ? decorators : undefined,
600
+ is_async: isAsync ? true : undefined,
601
+ is_exported: exported ? true : undefined,
602
+ meta: isGenerator ? { generator: true } : undefined,
306
603
  });
307
604
  symbols.push(sym);
308
605
  }
@@ -311,6 +608,7 @@ export function extractTypeScriptSymbols(tree, filePath, source, repo) {
311
608
  case "lexical_declaration": {
312
609
  // Determine if this is a `const` declaration (vs let/var)
313
610
  const isConst = node.children.some((c) => c.type === "const");
611
+ const exported = isExported || hasExportModifier(node);
314
612
  for (const declarator of node.namedChildren) {
315
613
  if (declarator.type !== "variable_declarator")
316
614
  continue;
@@ -321,10 +619,13 @@ export function extractTypeScriptSymbols(tree, filePath, source, repo) {
321
619
  if (value && value.type === "arrow_function") {
322
620
  // React detection: hook or component
323
621
  const kind = classifyReactKind(name, value);
622
+ const isAsync = hasAsyncModifier(value);
324
623
  const sym = makeSymbol(node, name, kind, filePath, source, repo, {
325
624
  parentId,
326
625
  docstring: getDocstring(node, source),
327
626
  signature: getSignature(value, source),
627
+ is_async: isAsync ? true : undefined,
628
+ is_exported: exported ? true : undefined,
328
629
  });
329
630
  symbols.push(sym);
330
631
  }
@@ -346,6 +647,7 @@ export function extractTypeScriptSymbols(tree, filePath, source, repo) {
346
647
  parentId,
347
648
  docstring: getDocstring(node, source),
348
649
  signature: innerFn ? getSignature(innerFn, source) : undefined,
650
+ is_exported: exported ? true : undefined,
349
651
  });
350
652
  symbols.push(sym);
351
653
  }
@@ -355,6 +657,24 @@ export function extractTypeScriptSymbols(tree, filePath, source, repo) {
355
657
  const sym = makeSymbol(node, name, kind, filePath, source, repo, {
356
658
  parentId,
357
659
  docstring: getDocstring(node, source),
660
+ is_exported: exported ? true : undefined,
661
+ });
662
+ symbols.push(sym);
663
+ // Object literal: extract methods so `const ctrl = { create() {} }`
664
+ // surfaces `create` as a method parented to `ctrl`. Common in
665
+ // older Node.js / Express controllers and config objects.
666
+ if (value.type === "object") {
667
+ extractObjectLiteralMethods(value, sym.id, source, filePath, repo, symbols);
668
+ }
669
+ }
670
+ else {
671
+ // No value (e.g., `declare const X: number;` inside ambient_declaration).
672
+ // Still emit a symbol so the declaration is searchable.
673
+ const kind = isConst && SCREAMING_CASE_RE.test(name) ? "constant" : "variable";
674
+ const sym = makeSymbol(node, name, kind, filePath, source, repo, {
675
+ parentId,
676
+ docstring: getDocstring(node, source),
677
+ is_exported: exported ? true : undefined,
358
678
  });
359
679
  symbols.push(sym);
360
680
  }
@@ -368,10 +688,15 @@ export function extractTypeScriptSymbols(tree, filePath, source, repo) {
368
688
  // React class component: class Foo extends Component / React.Component / PureComponent
369
689
  const kind = isReactClassComponent(node) ? "component" : "class";
370
690
  const decorators = getDecorators(node);
691
+ const exported = isExported || hasExportModifier(node);
692
+ const heritage = getClassHeritage(node);
371
693
  const sym = makeSymbol(node, name, kind, filePath, source, repo, {
372
694
  parentId,
373
695
  docstring: getDocstring(node, source),
374
696
  decorators: decorators.length > 0 ? decorators : undefined,
697
+ extends: heritage.extends.length > 0 ? heritage.extends : undefined,
698
+ implements: heritage.implements.length > 0 ? heritage.implements : undefined,
699
+ is_exported: exported ? true : undefined,
375
700
  });
376
701
  // Walk class body with this class as parent (before trimming so children get full source)
377
702
  for (const child of node.namedChildren) {
@@ -387,11 +712,19 @@ export function extractTypeScriptSymbols(tree, filePath, source, repo) {
387
712
  const name = getNodeName(node);
388
713
  if (name) {
389
714
  const decorators = getDecorators(node);
715
+ const modifiers = getModifiers(node);
716
+ // abstract methods always carry "abstract" by syntax — ensure it's recorded
717
+ if (!modifiers.includes("abstract"))
718
+ modifiers.push("abstract");
719
+ const meta = {};
720
+ if (modifiers.length > 0)
721
+ meta.modifiers = modifiers;
390
722
  const sym = makeSymbol(node, name, "method", filePath, source, repo, {
391
723
  parentId,
392
724
  docstring: getDocstring(node, source),
393
725
  signature: getSignature(node, source),
394
726
  decorators: decorators.length > 0 ? decorators : undefined,
727
+ meta: Object.keys(meta).length > 0 ? meta : undefined,
395
728
  });
396
729
  symbols.push(sym);
397
730
  }
@@ -401,35 +734,74 @@ export function extractTypeScriptSymbols(tree, filePath, source, repo) {
401
734
  const name = getNodeName(node);
402
735
  if (name) {
403
736
  const decorators = getDecorators(node);
737
+ const isAsync = hasAsyncModifier(node);
738
+ const modifiers = getModifiers(node);
739
+ const accessorKind = getAccessorKind(node);
740
+ const meta = {};
741
+ if (modifiers.length > 0)
742
+ meta.modifiers = modifiers;
743
+ if (accessorKind)
744
+ meta.accessor_kind = accessorKind;
404
745
  const sym = makeSymbol(node, name, "method", filePath, source, repo, {
405
746
  parentId,
406
747
  docstring: getDocstring(node, source),
407
748
  signature: getSignature(node, source),
408
749
  decorators: decorators.length > 0 ? decorators : undefined,
750
+ is_async: isAsync ? true : undefined,
751
+ meta: Object.keys(meta).length > 0 ? meta : undefined,
409
752
  });
410
753
  symbols.push(sym);
411
754
  }
412
755
  break;
413
756
  }
414
- case "public_field_definition": {
415
- const name = getNodeName(node);
757
+ case "public_field_definition":
758
+ case "field_definition": {
759
+ // public_field_definition: TS class fields with modifiers
760
+ // field_definition: JS class fields (incl. private #name, static)
761
+ // The `name` field works for both; tree-sitter-javascript exposes the
762
+ // identifier (or private_property_identifier) under `property` for JS,
763
+ // but childForFieldName("name") falls back to "property" via getNodeName.
764
+ const nameNode = node.childForFieldName("name") ?? node.childForFieldName("property");
765
+ const name = nameNode?.text;
416
766
  if (name) {
417
767
  const decorators = getDecorators(node);
768
+ const modifiers = getModifiers(node);
769
+ // Auto-accessor field: TS 4.9 `accessor x = ...` parses as a field with `accessor` keyword
770
+ const accessorKind = getAccessorKind(node);
771
+ const meta = {};
772
+ if (modifiers.length > 0)
773
+ meta.modifiers = modifiers;
774
+ if (accessorKind)
775
+ meta.accessor_kind = accessorKind;
418
776
  const sym = makeSymbol(node, name, "field", filePath, source, repo, {
419
777
  parentId,
420
778
  docstring: getDocstring(node, source),
421
779
  decorators: decorators.length > 0 ? decorators : undefined,
780
+ meta: Object.keys(meta).length > 0 ? meta : undefined,
422
781
  });
423
782
  symbols.push(sym);
424
783
  }
425
784
  break;
426
785
  }
786
+ case "class_static_block": {
787
+ // class Foo { static { ... } } — JS 2022, no name. Emit as a method
788
+ // named "<static>" so it shows up under the class in outlines without
789
+ // colliding with user-named methods.
790
+ const sym = makeSymbol(node, "<static>", "method", filePath, source, repo, {
791
+ parentId,
792
+ docstring: getDocstring(node, source),
793
+ });
794
+ symbols.push(sym);
795
+ break;
796
+ }
427
797
  case "interface_declaration": {
428
798
  const name = getNodeName(node);
429
799
  if (name) {
800
+ const exported = isExported || hasExportModifier(node);
430
801
  const sym = makeSymbol(node, name, "interface", filePath, source, repo, {
431
802
  parentId,
432
803
  docstring: getDocstring(node, source),
804
+ is_exported: exported ? true : undefined,
433
805
  });
434
806
  symbols.push(sym);
435
807
  }
@@ -438,33 +810,203 @@ export function extractTypeScriptSymbols(tree, filePath, source, repo) {
438
810
  case "type_alias_declaration": {
439
811
  const name = getNodeName(node);
440
812
  if (name) {
813
+ const exported = isExported || hasExportModifier(node);
441
814
  const sym = makeSymbol(node, name, "type", filePath, source, repo, {
442
815
  parentId,
443
816
  docstring: getDocstring(node, source),
817
+ is_exported: exported ? true : undefined,
444
818
  });
445
819
  symbols.push(sym);
446
820
  }
447
821
  break;
448
822
  }
823
+ case "internal_module":
824
+ case "module": {
825
+ // L2/L12: `namespace M {}`, `module M {}`, OR `module "x" {}` (when nested
826
+ // inside ambient_declaration → declare module "x" {}).
827
+ // Name field: identifier for namespace, string for ambient module.
828
+ const nameNode = node.childForFieldName("name");
829
+ let nsName = null;
830
+ if (nameNode) {
831
+ nsName = nameNode.type === "string"
832
+ ? nameNode.text.replace(/^['"`]|['"`]$/g, "")
833
+ : nameNode.text;
834
+ }
835
+ else {
836
+ // Fallback: scan named children for identifier or string
837
+ for (const c of node.namedChildren) {
838
+ if (c.type === "identifier") {
839
+ nsName = c.text;
840
+ break;
841
+ }
842
+ if (c.type === "string") {
843
+ nsName = c.text.replace(/^['"`]|['"`]$/g, "");
844
+ break;
845
+ }
846
+ }
847
+ }
848
+ if (nsName) {
849
+ const exported = isExported || hasExportModifier(node);
850
+ const sym = makeSymbol(node, nsName, "namespace", filePath, source, repo, {
851
+ parentId,
852
+ docstring: getDocstring(node, source),
853
+ is_exported: exported ? true : undefined,
854
+ });
855
+ symbols.push(sym);
856
+ // Walk body so nested function/class/interface declarations are parented
857
+ const body = node.childForFieldName("body")
858
+ ?? node.namedChildren.find((c) => c.type === "statement_block");
859
+ if (body) {
860
+ for (const child of body.namedChildren) {
861
+ walk(child, sym.id, exported);
862
+ }
863
+ }
864
+ }
865
+ return;
866
+ }
867
+ case "ambient_declaration": {
868
+ // L12: `declare const X`, `declare module "x" {}`, `declare class Y {}`, etc.
869
+ // Rule: ambient module declarations with STRING-literal name are intrinsically
870
+ // module-public; force is_exported=true via the special case below. For other
871
+ // `declare X` forms, propagate isExported || hasExportModifier(node).
872
+ const ambientExported = isExported || hasExportModifier(node);
873
+ for (const child of node.namedChildren) {
874
+ // Detect `declare module "x"` — child is `module` with a string-literal name
875
+ const isStringNamedModule = child.type === "module"
876
+ && (child.childForFieldName("name")?.type === "string"
877
+ || child.namedChildren.some((c) => c.type === "string"));
878
+ walk(child, parentId, ambientExported || isStringNamedModule);
879
+ }
880
+ return;
881
+ }
449
882
  case "enum_declaration": {
450
883
  const name = getNodeName(node);
451
884
  if (name) {
885
+ const exported = isExported || hasExportModifier(node);
452
886
  const sym = makeSymbol(node, name, "enum", filePath, source, repo, {
453
887
  parentId,
454
888
  docstring: getDocstring(node, source),
889
+ is_exported: exported ? true : undefined,
455
890
  });
456
891
  symbols.push(sym);
892
+ // Walk enum_body to emit members as `constant` parented to the enum.
893
+ // children: enum_assignment (named member with value) | property_identifier
894
+ const body = node.childForFieldName("body");
895
+ if (body) {
896
+ for (const child of body.namedChildren) {
897
+ let memberName = null;
898
+ if (child.type === "enum_assignment") {
899
+ memberName = getNodeName(child);
900
+ }
901
+ else if (child.type === "property_identifier") {
902
+ memberName = child.text;
903
+ }
904
+ if (memberName) {
905
+ symbols.push(makeSymbol(child, memberName, "constant", filePath, source, repo, {
906
+ parentId: sym.id,
907
+ }));
908
+ }
909
+ }
910
+ }
457
911
  }
458
- break;
912
+ return; // body already walked; do not let default child-walk re-enter
459
913
  }
460
914
  case "export_statement": {
461
- // Unwrap: extract from the inner declaration
915
+ const source_field = node.childForFieldName("source");
916
+ if (source_field) {
917
+ // Re-export from module: `export { X as Y } from "./m"` or `export * as ns from "./m"`
918
+ for (const child of node.namedChildren) {
919
+ if (child.type === "export_clause") {
920
+ for (const spec of child.namedChildren) {
921
+ if (spec.type === "export_specifier") {
922
+ const aliasNode = spec.childForFieldName("alias");
923
+ const nameNode = spec.childForFieldName("name");
924
+ const emitName = (aliasNode ?? nameNode)?.text;
925
+ if (emitName) {
926
+ symbols.push(makeSymbol(spec, emitName, "variable", filePath, source, repo, {
927
+ parentId,
928
+ is_exported: true,
929
+ }));
930
+ }
931
+ }
932
+ }
933
+ }
934
+ else if (child.type === "namespace_export") {
935
+ // export * as ns from "./m"
936
+ for (const c of child.namedChildren) {
937
+ if (c.type === "identifier") {
938
+ symbols.push(makeSymbol(child, c.text, "namespace", filePath, source, repo, {
939
+ parentId,
940
+ is_exported: true,
941
+ }));
942
+ }
943
+ }
944
+ }
945
+ }
946
+ return;
947
+ }
948
+ // Local re-export `export { X }` — collect names for post-pass
949
+ for (const child of node.namedChildren) {
950
+ if (child.type === "export_clause") {
951
+ for (const spec of child.namedChildren) {
952
+ if (spec.type === "export_specifier") {
953
+ const nameNode = spec.childForFieldName("name");
954
+ if (nameNode)
955
+ localReExported.add(nameNode.text);
956
+ }
957
+ }
958
+ }
959
+ }
960
+ // L11: Anonymous default export — `export default function() {}` /
961
+ // `export default class {}` / `export default () => <div/>`. Such
962
+ // children have no `name` field; without synth, the named-children
963
+ // walk-through silently drops them. Detect via presence of `default`
964
+ // keyword child + an unnamed function/class/arrow expression child.
965
+ const isDefaultExport = node.children.some((c) => c.type === "default");
966
+ if (isDefaultExport) {
967
+ for (const child of node.namedChildren) {
968
+ const isAnon = (child.type === "function_expression" || child.type === "class" || child.type === "class_declaration" || child.type === "function_declaration" || child.type === "generator_function_declaration" || child.type === "arrow_function")
969
+ && !getNodeName(child);
970
+ if (isAnon) {
971
+ // For JSX-returning anonymous defaults, also flag is_react_component
972
+ // so React-aware tools can still find them. Spec AC #10 mandates
973
+ // kind:"default_export" regardless.
974
+ const meta = {};
975
+ if (returnsJSX(child))
976
+ meta.is_react_component = true;
977
+ const sym = makeSymbol(node, "default", "default_export", filePath, source, repo, {
978
+ parentId,
979
+ is_exported: true,
980
+ signature: child.type !== "class" && child.type !== "class_declaration"
981
+ ? getSignature(child, source) : undefined,
982
+ meta: Object.keys(meta).length > 0 ? meta : undefined,
983
+ });
984
+ symbols.push(sym);
985
+ return;
986
+ }
987
+ }
988
+ }
989
+ // Walk children with isExported=true so wrapped declarations get tagged
462
990
  for (const child of node.namedChildren) {
463
- walk(child, parentId);
991
+ walk(child, parentId, true);
464
992
  }
465
993
  return;
466
994
  }
467
995
  case "expression_statement": {
996
+ // CommonJS export detection (JS-only in practice — TS files use ESM):
997
+ // module.exports = X
998
+ // module.exports = { foo, bar }
999
+ // module.exports.foo = expr
1000
+ // exports.foo = expr
1001
+ // Order matters: check CJS before test-call branch since both peek at
1002
+ // namedChildren[0]; CJS is `assignment_expression` and tests are
1003
+ // `call_expression` so they're disjoint and either can return early.
1004
+ const firstChild = node.namedChildren[0];
1005
+ if (firstChild?.type === "assignment_expression") {
1006
+ const handled = handleCjsExport(firstChild, node, parentId, source, filePath, repo, symbols, cjsExported);
1007
+ if (handled)
1008
+ return;
1009
+ }
468
1010
  // Check for test calls: describe(...), it(...), test(...)
469
1011
  // Also handles member forms: it.skip(...), it.each(...)(...), describe.only(...)
470
1012
  const expr = node.namedChildren[0];
@@ -524,10 +1066,43 @@ export function extractTypeScriptSymbols(tree, filePath, source, repo) {
524
1066
  }
525
1067
  // Default: walk children
526
1068
  for (const child of node.namedChildren) {
527
- walk(child, parentId);
1069
+ walk(child, parentId, isExported);
1070
+ }
1071
+ }
1072
+ // ERROR-node warning: emit a single warn line per file so silent grammar
1073
+ // mismatches (e.g., a syntax the bundled WASM doesn't understand) are
1074
+ // visible without crashing the index. Decorator metadata or other affected
1075
+ // symbols may be incomplete; this is an observability hook, not a hard fail.
1076
+ // Note: web-tree-sitter exposes `hasError` as a getter (boolean property),
1077
+ // not a method — accessing as property is correct.
1078
+ if (tree.rootNode.hasError) {
1079
+ console.warn(`[ts-extractor] grammar errors detected in ${filePath}; some symbols may be incomplete`);
1080
+ }
1081
+ // Top-level walk guard: catch RangeError ("Maximum call stack") on extremely
1082
+ // deep ASTs (e.g., 50k+ node bundled .d.ts files). Returns the partial
1083
+ // symbol list rather than crashing the entire index_folder run. Iterative
1084
+ // walk is deferred to v2 — partial extraction is acceptable for v1.
1085
+ try {
1086
+ walk(tree.rootNode);
1087
+ }
1088
+ catch (err) {
1089
+ if (err instanceof RangeError && /Maximum call stack/i.test(err.message)) {
1090
+ console.warn(`[ts-extractor] stack overflow on ${filePath}; returning ${symbols.length} partial symbols`);
1091
+ return symbols;
1092
+ }
1093
+ throw err;
1094
+ }
1095
+ // Post-pass: local re-exports `export { X }` and CommonJS exports both
1096
+ // mark prior-declared symbols as exported. Merged into a single Set so we
1097
+ // walk the symbol list at most once.
1098
+ if (localReExported.size > 0 || cjsExported.size > 0) {
1099
+ const exportedNames = new Set([...localReExported, ...cjsExported]);
1100
+ for (const sym of symbols) {
1101
+ if (!sym.is_exported && exportedNames.has(sym.name)) {
1102
+ sym.is_exported = true;
1103
+ }
528
1104
  }
529
1105
  }
530
- walk(tree.rootNode);
531
1106
  return symbols;
532
1107
  }
533
1108
  //# sourceMappingURL=typescript.js.map