codesift-mcp 0.5.24 → 0.5.28
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.
- package/README.md +4 -4
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +28 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/help.js +1 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/hooks.d.ts +4 -0
- package/dist/cli/hooks.d.ts.map +1 -1
- package/dist/cli/hooks.js +15 -4
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli/journal-commands.d.ts +9 -0
- package/dist/cli/journal-commands.d.ts.map +1 -0
- package/dist/cli/journal-commands.js +160 -0
- package/dist/cli/journal-commands.js.map +1 -0
- package/dist/cli/wiki-commands.d.ts.map +1 -1
- package/dist/cli/wiki-commands.js +5 -0
- package/dist/cli/wiki-commands.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +41 -47
- package/dist/instructions.js.map +1 -1
- package/dist/parser/extractors/_shared.d.ts +2 -0
- package/dist/parser/extractors/_shared.d.ts.map +1 -1
- package/dist/parser/extractors/_shared.js +4 -0
- package/dist/parser/extractors/_shared.js.map +1 -1
- package/dist/parser/extractors/typescript.d.ts.map +1 -1
- package/dist/parser/extractors/typescript.js +606 -31
- package/dist/parser/extractors/typescript.js.map +1 -1
- package/dist/parser/parse-worker.d.ts +2 -0
- package/dist/parser/parse-worker.d.ts.map +1 -0
- package/dist/parser/parse-worker.js +47 -0
- package/dist/parser/parse-worker.js.map +1 -0
- package/dist/parser/parser-manager.d.ts +10 -1
- package/dist/parser/parser-manager.d.ts.map +1 -1
- package/dist/parser/parser-manager.js +40 -2
- package/dist/parser/parser-manager.js.map +1 -1
- package/dist/parser/parser-pool.d.ts +31 -0
- package/dist/parser/parser-pool.d.ts.map +1 -0
- package/dist/parser/parser-pool.js +211 -0
- package/dist/parser/parser-pool.js.map +1 -0
- package/dist/register-tool-loaders.d.ts +1 -1
- package/dist/register-tool-loaders.d.ts.map +1 -1
- package/dist/register-tool-loaders.js +2 -2
- package/dist/register-tool-loaders.js.map +1 -1
- package/dist/register-tools.d.ts.map +1 -1
- package/dist/register-tools.js +382 -35
- package/dist/register-tools.js.map +1 -1
- package/dist/retrieval/codebase-retrieval.d.ts.map +1 -1
- package/dist/retrieval/codebase-retrieval.js +9 -1
- package/dist/retrieval/codebase-retrieval.js.map +1 -1
- package/dist/search/chunker.d.ts +1 -0
- package/dist/search/chunker.d.ts.map +1 -1
- package/dist/search/chunker.js +8 -1
- package/dist/search/chunker.js.map +1 -1
- package/dist/server-helpers.d.ts +27 -0
- package/dist/server-helpers.d.ts.map +1 -1
- package/dist/server-helpers.js +90 -9
- package/dist/server-helpers.js.map +1 -1
- package/dist/server.js +31 -0
- package/dist/server.js.map +1 -1
- package/dist/storage/index-store.d.ts +41 -4
- package/dist/storage/index-store.d.ts.map +1 -1
- package/dist/storage/index-store.js +89 -6
- package/dist/storage/index-store.js.map +1 -1
- package/dist/storage/watcher.d.ts +33 -6
- package/dist/storage/watcher.d.ts.map +1 -1
- package/dist/storage/watcher.js +73 -34
- package/dist/storage/watcher.js.map +1 -1
- package/dist/storage/workspace-resolver.d.ts +15 -0
- package/dist/storage/workspace-resolver.d.ts.map +1 -0
- package/dist/storage/workspace-resolver.js +130 -0
- package/dist/storage/workspace-resolver.js.map +1 -0
- package/dist/tools/_helpers.d.ts +37 -0
- package/dist/tools/_helpers.d.ts.map +1 -0
- package/dist/tools/_helpers.js +31 -0
- package/dist/tools/_helpers.js.map +1 -0
- package/dist/tools/constant-resolution-tools.d.ts +8 -0
- package/dist/tools/constant-resolution-tools.d.ts.map +1 -0
- package/dist/tools/constant-resolution-tools.js +68 -0
- package/dist/tools/constant-resolution-tools.js.map +1 -0
- package/dist/tools/graph-tools.d.ts +27 -5
- package/dist/tools/graph-tools.d.ts.map +1 -1
- package/dist/tools/graph-tools.js +141 -18
- package/dist/tools/graph-tools.js.map +1 -1
- package/dist/tools/hotspot-tools.d.ts +6 -0
- package/dist/tools/hotspot-tools.d.ts.map +1 -1
- package/dist/tools/hotspot-tools.js +48 -8
- package/dist/tools/hotspot-tools.js.map +1 -1
- package/dist/tools/impact-tools.d.ts.map +1 -1
- package/dist/tools/impact-tools.js +9 -2
- package/dist/tools/impact-tools.js.map +1 -1
- package/dist/tools/index-tools.d.ts +15 -1
- package/dist/tools/index-tools.d.ts.map +1 -1
- package/dist/tools/index-tools.js +161 -16
- package/dist/tools/index-tools.js.map +1 -1
- package/dist/tools/journal-generator-helpers.d.ts +55 -0
- package/dist/tools/journal-generator-helpers.d.ts.map +1 -0
- package/dist/tools/journal-generator-helpers.js +250 -0
- package/dist/tools/journal-generator-helpers.js.map +1 -0
- package/dist/tools/journal-generator.d.ts +48 -0
- package/dist/tools/journal-generator.d.ts.map +1 -0
- package/dist/tools/journal-generator.js +204 -0
- package/dist/tools/journal-generator.js.map +1 -0
- package/dist/tools/journal-git-client.d.ts +20 -0
- package/dist/tools/journal-git-client.d.ts.map +1 -0
- package/dist/tools/journal-git-client.js +57 -0
- package/dist/tools/journal-git-client.js.map +1 -0
- package/dist/tools/journal-llm-client.d.ts +57 -0
- package/dist/tools/journal-llm-client.d.ts.map +1 -0
- package/dist/tools/journal-llm-client.js +175 -0
- package/dist/tools/journal-llm-client.js.map +1 -0
- package/dist/tools/journal-migrator.d.ts +22 -0
- package/dist/tools/journal-migrator.d.ts.map +1 -0
- package/dist/tools/journal-migrator.js +183 -0
- package/dist/tools/journal-migrator.js.map +1 -0
- package/dist/tools/journal-phase-detector.d.ts +26 -0
- package/dist/tools/journal-phase-detector.d.ts.map +1 -0
- package/dist/tools/journal-phase-detector.js +126 -0
- package/dist/tools/journal-phase-detector.js.map +1 -0
- package/dist/tools/journal-sentinel.d.ts +15 -0
- package/dist/tools/journal-sentinel.d.ts.map +1 -0
- package/dist/tools/journal-sentinel.js +110 -0
- package/dist/tools/journal-sentinel.js.map +1 -0
- package/dist/tools/journal-templates.d.ts +28 -0
- package/dist/tools/journal-templates.d.ts.map +1 -0
- package/dist/tools/journal-templates.js +93 -0
- package/dist/tools/journal-templates.js.map +1 -0
- package/dist/tools/pattern-tools.d.ts +2 -0
- package/dist/tools/pattern-tools.d.ts.map +1 -1
- package/dist/tools/pattern-tools.js +73 -0
- package/dist/tools/pattern-tools.js.map +1 -1
- package/dist/tools/perf-tools.d.ts.map +1 -1
- package/dist/tools/perf-tools.js +6 -0
- package/dist/tools/perf-tools.js.map +1 -1
- package/dist/tools/plan-turn-tools.d.ts.map +1 -1
- package/dist/tools/plan-turn-tools.js +38 -2
- package/dist/tools/plan-turn-tools.js.map +1 -1
- package/dist/tools/project-tools.d.ts +6 -0
- package/dist/tools/project-tools.d.ts.map +1 -1
- package/dist/tools/project-tools.js +65 -25
- package/dist/tools/project-tools.js.map +1 -1
- package/dist/tools/python-constants-tools.d.ts +6 -0
- package/dist/tools/python-constants-tools.d.ts.map +1 -1
- package/dist/tools/python-constants-tools.js.map +1 -1
- package/dist/tools/react-tools.d.ts +44 -1
- package/dist/tools/react-tools.d.ts.map +1 -1
- package/dist/tools/react-tools.js +142 -9
- package/dist/tools/react-tools.js.map +1 -1
- package/dist/tools/search-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.js +21 -2
- package/dist/tools/search-tools.js.map +1 -1
- package/dist/tools/status-tools.d.ts +10 -0
- package/dist/tools/status-tools.d.ts.map +1 -1
- package/dist/tools/status-tools.js +53 -2
- package/dist/tools/status-tools.js.map +1 -1
- package/dist/tools/symbol-tools.d.ts +13 -0
- package/dist/tools/symbol-tools.d.ts.map +1 -1
- package/dist/tools/symbol-tools.js +38 -0
- package/dist/tools/symbol-tools.js.map +1 -1
- package/dist/tools/typescript-constants-tools.d.ts +6 -0
- package/dist/tools/typescript-constants-tools.d.ts.map +1 -0
- package/dist/tools/typescript-constants-tools.js +687 -0
- package/dist/tools/typescript-constants-tools.js.map +1 -0
- package/dist/tools/wiki-hub-ranker.d.ts +36 -0
- package/dist/tools/wiki-hub-ranker.d.ts.map +1 -0
- package/dist/tools/wiki-hub-ranker.js +72 -0
- package/dist/tools/wiki-hub-ranker.js.map +1 -0
- package/dist/tools/wiki-lint.d.ts +7 -2
- package/dist/tools/wiki-lint.d.ts.map +1 -1
- package/dist/tools/wiki-lint.js +73 -1
- package/dist/tools/wiki-lint.js.map +1 -1
- package/dist/tools/wiki-manifest.d.ts +129 -21
- package/dist/tools/wiki-manifest.d.ts.map +1 -1
- package/dist/tools/wiki-manifest.js +16 -9
- package/dist/tools/wiki-manifest.js.map +1 -1
- package/dist/tools/wiki-module-builder.d.ts +42 -0
- package/dist/tools/wiki-module-builder.d.ts.map +1 -0
- package/dist/tools/wiki-module-builder.js +449 -0
- package/dist/tools/wiki-module-builder.js.map +1 -0
- package/dist/tools/wiki-overview-sources.d.ts +26 -0
- package/dist/tools/wiki-overview-sources.d.ts.map +1 -0
- package/dist/tools/wiki-overview-sources.js +128 -0
- package/dist/tools/wiki-overview-sources.js.map +1 -0
- package/dist/tools/wiki-page-generators.d.ts +19 -3
- package/dist/tools/wiki-page-generators.d.ts.map +1 -1
- package/dist/tools/wiki-page-generators.js +156 -22
- package/dist/tools/wiki-page-generators.js.map +1 -1
- package/dist/tools/wiki-tools.d.ts +20 -0
- package/dist/tools/wiki-tools.d.ts.map +1 -1
- package/dist/tools/wiki-tools.js +181 -29
- package/dist/tools/wiki-tools.js.map +1 -1
- package/dist/tools/workspace-scope-helper.d.ts +21 -0
- package/dist/tools/workspace-scope-helper.d.ts.map +1 -0
- package/dist/tools/workspace-scope-helper.js +50 -0
- package/dist/tools/workspace-scope-helper.js.map +1 -0
- package/dist/tools/workspace-tools.d.ts +74 -0
- package/dist/tools/workspace-tools.d.ts.map +1 -0
- package/dist/tools/workspace-tools.js +366 -0
- package/dist/tools/workspace-tools.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/import-graph.d.ts +27 -0
- package/dist/utils/import-graph.d.ts.map +1 -1
- package/dist/utils/import-graph.js +331 -8
- package/dist/utils/import-graph.js.map +1 -1
- package/dist/utils/ts-imports.d.ts +30 -0
- package/dist/utils/ts-imports.d.ts.map +1 -0
- package/dist/utils/ts-imports.js +168 -0
- package/dist/utils/ts-imports.js.map +1 -0
- package/dist/utils/tsconfig-paths.d.ts +59 -0
- package/dist/utils/tsconfig-paths.d.ts.map +1 -0
- package/dist/utils/tsconfig-paths.js +176 -0
- package/dist/utils/tsconfig-paths.js.map +1 -0
- package/dist/utils/walk.d.ts.map +1 -1
- package/dist/utils/walk.js +1 -0
- package/dist/utils/walk.js.map +1 -1
- package/package.json +13 -3
- package/rules/codesift.md +19 -2
- package/rules/codesift.mdc +2 -2
- package/rules/codex.md +13 -2
- 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
|
-
*
|
|
101
|
-
*
|
|
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
|
|
104
|
-
|
|
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
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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 =
|
|
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 +=
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
912
|
+
return; // body already walked; do not let default child-walk re-enter
|
|
459
913
|
}
|
|
460
914
|
case "export_statement": {
|
|
461
|
-
|
|
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
|