gitnexus 1.4.7 → 1.4.9
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 +29 -1
- package/dist/cli/ai-context.d.ts +1 -1
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +54 -21
- package/dist/cli/index-repo.d.ts +15 -0
- package/dist/cli/index-repo.js +115 -0
- package/dist/cli/index.js +13 -3
- package/dist/cli/setup.js +90 -10
- package/dist/cli/wiki.d.ts +4 -0
- package/dist/cli/wiki.js +174 -53
- package/dist/config/supported-languages.d.ts +33 -1
- package/dist/config/supported-languages.js +32 -0
- package/dist/core/embeddings/embedder.d.ts +6 -1
- package/dist/core/embeddings/embedder.js +65 -5
- package/dist/core/embeddings/embedding-pipeline.js +11 -9
- package/dist/core/embeddings/http-client.d.ts +31 -0
- package/dist/core/embeddings/http-client.js +179 -0
- package/dist/core/embeddings/index.d.ts +1 -0
- package/dist/core/embeddings/index.js +1 -0
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/graph.js +9 -1
- package/dist/core/graph/types.d.ts +11 -2
- package/dist/core/ingestion/call-processor.d.ts +66 -2
- package/dist/core/ingestion/call-processor.js +650 -30
- package/dist/core/ingestion/call-routing.d.ts +9 -18
- package/dist/core/ingestion/call-routing.js +0 -19
- package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
- package/dist/core/ingestion/cobol/cobol-copy-expander.js +385 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +1509 -0
- package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
- package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
- package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
- package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
- package/dist/core/ingestion/cobol-processor.d.ts +54 -0
- package/dist/core/ingestion/cobol-processor.js +1186 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +17 -0
- package/dist/core/ingestion/entry-point-scoring.js +52 -28
- package/dist/core/ingestion/export-detection.d.ts +47 -8
- package/dist/core/ingestion/export-detection.js +29 -50
- package/dist/core/ingestion/field-extractor.d.ts +29 -0
- package/dist/core/ingestion/field-extractor.js +25 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.js +108 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.js +73 -0
- package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/dart.js +76 -0
- package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
- package/dist/core/ingestion/field-extractors/configs/go.js +64 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +44 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.js +134 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
- package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/php.js +67 -0
- package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
- package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.js +75 -0
- package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
- package/dist/core/ingestion/field-extractors/configs/rust.js +55 -0
- package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/swift.js +63 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +60 -0
- package/dist/core/ingestion/field-extractors/generic.d.ts +46 -0
- package/dist/core/ingestion/field-extractors/generic.js +111 -0
- package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
- package/dist/core/ingestion/field-extractors/typescript.js +291 -0
- package/dist/core/ingestion/field-types.d.ts +59 -0
- package/dist/core/ingestion/field-types.js +2 -0
- package/dist/core/ingestion/framework-detection.d.ts +97 -2
- package/dist/core/ingestion/framework-detection.js +114 -14
- package/dist/core/ingestion/heritage-processor.js +62 -66
- package/dist/core/ingestion/import-processor.d.ts +9 -10
- package/dist/core/ingestion/import-processor.js +150 -196
- package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.d.ts +6 -9
- package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.js +20 -2
- package/dist/core/ingestion/import-resolvers/dart.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/dart.js +44 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/go.d.ts +4 -5
- package/dist/core/ingestion/{resolvers → import-resolvers}/go.js +17 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.d.ts +10 -1
- package/dist/core/ingestion/import-resolvers/jvm.js +159 -0
- package/dist/core/ingestion/import-resolvers/php.d.ts +25 -0
- package/dist/core/ingestion/import-resolvers/php.js +80 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/python.d.ts +9 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/python.js +35 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.d.ts +5 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.js +7 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/rust.d.ts +5 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/rust.js +41 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/standard.d.ts +15 -7
- package/dist/core/ingestion/{resolvers → import-resolvers}/standard.js +22 -3
- package/dist/core/ingestion/import-resolvers/swift.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/swift.js +23 -0
- package/dist/core/ingestion/import-resolvers/types.d.ts +44 -0
- package/dist/core/ingestion/import-resolvers/types.js +6 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.d.ts +2 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +7 -0
- package/dist/core/ingestion/language-config.d.ts +6 -0
- package/dist/core/ingestion/language-config.js +13 -0
- package/dist/core/ingestion/language-provider.d.ts +121 -0
- package/dist/core/ingestion/language-provider.js +24 -0
- package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
- package/dist/core/ingestion/languages/c-cpp.js +71 -0
- package/dist/core/ingestion/languages/cobol.d.ts +1 -0
- package/dist/core/ingestion/languages/cobol.js +26 -0
- package/dist/core/ingestion/languages/csharp.d.ts +8 -0
- package/dist/core/ingestion/languages/csharp.js +49 -0
- package/dist/core/ingestion/languages/dart.d.ts +12 -0
- package/dist/core/ingestion/languages/dart.js +58 -0
- package/dist/core/ingestion/languages/go.d.ts +11 -0
- package/dist/core/ingestion/languages/go.js +28 -0
- package/dist/core/ingestion/languages/index.d.ts +38 -0
- package/dist/core/ingestion/languages/index.js +63 -0
- package/dist/core/ingestion/languages/java.d.ts +9 -0
- package/dist/core/ingestion/languages/java.js +29 -0
- package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
- package/dist/core/ingestion/languages/kotlin.js +53 -0
- package/dist/core/ingestion/languages/php.d.ts +8 -0
- package/dist/core/ingestion/languages/php.js +145 -0
- package/dist/core/ingestion/languages/python.d.ts +12 -0
- package/dist/core/ingestion/languages/python.js +39 -0
- package/dist/core/ingestion/languages/ruby.d.ts +9 -0
- package/dist/core/ingestion/languages/ruby.js +44 -0
- package/dist/core/ingestion/languages/rust.d.ts +12 -0
- package/dist/core/ingestion/languages/rust.js +44 -0
- package/dist/core/ingestion/languages/swift.d.ts +12 -0
- package/dist/core/ingestion/languages/swift.js +133 -0
- package/dist/core/ingestion/languages/typescript.d.ts +10 -0
- package/dist/core/ingestion/languages/typescript.js +60 -0
- package/dist/core/ingestion/markdown-processor.d.ts +17 -0
- package/dist/core/ingestion/markdown-processor.js +124 -0
- package/dist/core/ingestion/mro-processor.js +22 -18
- package/dist/core/ingestion/named-binding-processor.d.ts +18 -0
- package/dist/core/ingestion/named-binding-processor.js +42 -0
- package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/csharp.js +37 -0
- package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/java.js +29 -0
- package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
- package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/php.js +61 -0
- package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/python.js +49 -0
- package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/rust.js +64 -0
- package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
- package/dist/core/ingestion/named-bindings/types.js +6 -0
- package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/typescript.js +58 -0
- package/dist/core/ingestion/parsing-processor.d.ts +6 -2
- package/dist/core/ingestion/parsing-processor.js +125 -85
- package/dist/core/ingestion/pipeline.d.ts +10 -0
- package/dist/core/ingestion/pipeline.js +1235 -317
- package/dist/core/ingestion/resolution-context.d.ts +5 -0
- package/dist/core/ingestion/resolution-context.js +8 -5
- package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
- package/dist/core/ingestion/route-extractors/expo.js +36 -0
- package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
- package/dist/core/ingestion/route-extractors/middleware.js +143 -0
- package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
- package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
- package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
- package/dist/core/ingestion/route-extractors/php.js +21 -0
- package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
- package/dist/core/ingestion/route-extractors/response-shapes.js +290 -0
- package/dist/core/ingestion/symbol-table.d.ts +16 -0
- package/dist/core/ingestion/symbol-table.js +20 -6
- package/dist/core/ingestion/tree-sitter-queries.d.ts +10 -9
- package/dist/core/ingestion/tree-sitter-queries.js +274 -11
- package/dist/core/ingestion/type-env.d.ts +42 -18
- package/dist/core/ingestion/type-env.js +481 -106
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
- package/dist/core/ingestion/type-extractors/csharp.js +149 -16
- package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
- package/dist/core/ingestion/type-extractors/dart.js +371 -0
- package/dist/core/ingestion/type-extractors/jvm.js +169 -66
- package/dist/core/ingestion/type-extractors/rust.js +35 -1
- package/dist/core/ingestion/type-extractors/shared.d.ts +1 -15
- package/dist/core/ingestion/type-extractors/shared.js +14 -112
- package/dist/core/ingestion/type-extractors/swift.js +338 -7
- package/dist/core/ingestion/type-extractors/types.d.ts +40 -8
- package/dist/core/ingestion/type-extractors/typescript.js +141 -9
- package/dist/core/ingestion/utils/ast-helpers.d.ts +83 -0
- package/dist/core/ingestion/utils/ast-helpers.js +817 -0
- package/dist/core/ingestion/utils/call-analysis.d.ts +73 -0
- package/dist/core/ingestion/utils/call-analysis.js +527 -0
- package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
- package/dist/core/ingestion/utils/event-loop.js +5 -0
- package/dist/core/ingestion/utils/language-detection.d.ts +9 -0
- package/dist/core/ingestion/utils/language-detection.js +70 -0
- package/dist/core/ingestion/utils/verbose.d.ts +1 -0
- package/dist/core/ingestion/utils/verbose.js +7 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +55 -5
- package/dist/core/ingestion/workers/parse-worker.js +415 -225
- package/dist/core/lbug/csv-generator.js +51 -1
- package/dist/core/lbug/lbug-adapter.d.ts +10 -0
- package/dist/core/lbug/lbug-adapter.js +75 -4
- package/dist/core/lbug/schema.d.ts +8 -4
- package/dist/core/lbug/schema.js +65 -4
- package/dist/core/tree-sitter/parser-loader.js +7 -1
- package/dist/core/wiki/cursor-client.d.ts +31 -0
- package/dist/core/wiki/cursor-client.js +127 -0
- package/dist/core/wiki/generator.d.ts +28 -9
- package/dist/core/wiki/generator.js +115 -18
- package/dist/core/wiki/graph-queries.d.ts +4 -0
- package/dist/core/wiki/graph-queries.js +7 -1
- package/dist/core/wiki/llm-client.d.ts +2 -0
- package/dist/core/wiki/llm-client.js +8 -4
- package/dist/core/wiki/prompts.d.ts +3 -3
- package/dist/core/wiki/prompts.js +6 -0
- package/dist/mcp/core/embedder.js +11 -3
- package/dist/mcp/core/lbug-adapter.d.ts +5 -0
- package/dist/mcp/core/lbug-adapter.js +23 -2
- package/dist/mcp/local/local-backend.d.ts +38 -5
- package/dist/mcp/local/local-backend.js +804 -63
- package/dist/mcp/resources.js +2 -0
- package/dist/mcp/tools.js +73 -4
- package/dist/server/api.d.ts +19 -1
- package/dist/server/api.js +66 -6
- package/dist/storage/git.d.ts +12 -0
- package/dist/storage/git.js +21 -0
- package/dist/storage/repo-manager.d.ts +3 -0
- package/package.json +25 -16
- package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
- package/dist/core/ingestion/named-binding-extraction.js +0 -363
- package/dist/core/ingestion/resolvers/index.d.ts +0 -18
- package/dist/core/ingestion/resolvers/index.js +0 -13
- package/dist/core/ingestion/resolvers/jvm.js +0 -87
- package/dist/core/ingestion/resolvers/php.d.ts +0 -15
- package/dist/core/ingestion/resolvers/php.js +0 -35
- package/dist/core/ingestion/type-extractors/index.d.ts +0 -22
- package/dist/core/ingestion/type-extractors/index.js +0 -31
- package/dist/core/ingestion/utils.d.ts +0 -138
- package/dist/core/ingestion/utils.js +0 -1290
- package/scripts/patch-tree-sitter-swift.cjs +0 -74
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript and JavaScript language providers.
|
|
3
|
+
*
|
|
4
|
+
* Both languages share the same type extraction config (typescriptConfig),
|
|
5
|
+
* export checker (tsExportChecker), and named binding extractor
|
|
6
|
+
* (extractTsNamedBindings). They differ in file extensions, tree-sitter
|
|
7
|
+
* queries (TypeScript grammar has interface/type nodes), and language ID.
|
|
8
|
+
*/
|
|
9
|
+
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
10
|
+
import { defineLanguage } from '../language-provider.js';
|
|
11
|
+
import { typeConfig as typescriptConfig } from '../type-extractors/typescript.js';
|
|
12
|
+
import { tsExportChecker } from '../export-detection.js';
|
|
13
|
+
import { resolveTypescriptImport, resolveJavascriptImport } from '../import-resolvers/standard.js';
|
|
14
|
+
import { extractTsNamedBindings } from '../named-bindings/typescript.js';
|
|
15
|
+
import { TYPESCRIPT_QUERIES, JAVASCRIPT_QUERIES } from '../tree-sitter-queries.js';
|
|
16
|
+
import { typescriptFieldExtractor } from '../field-extractors/typescript.js';
|
|
17
|
+
import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
18
|
+
import { javascriptConfig } from '../field-extractors/configs/typescript-javascript.js';
|
|
19
|
+
const BUILT_INS = new Set([
|
|
20
|
+
'console', 'log', 'warn', 'error', 'info', 'debug',
|
|
21
|
+
'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
|
|
22
|
+
'parseInt', 'parseFloat', 'isNaN', 'isFinite',
|
|
23
|
+
'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent',
|
|
24
|
+
'JSON', 'parse', 'stringify',
|
|
25
|
+
'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
|
|
26
|
+
'Map', 'Set', 'WeakMap', 'WeakSet',
|
|
27
|
+
'Promise', 'resolve', 'reject', 'then', 'catch', 'finally',
|
|
28
|
+
'Math', 'Date', 'RegExp', 'Error',
|
|
29
|
+
'require', 'import', 'export', 'fetch', 'Response', 'Request',
|
|
30
|
+
'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext',
|
|
31
|
+
'useReducer', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue',
|
|
32
|
+
'createElement', 'createContext', 'createRef', 'forwardRef', 'memo', 'lazy',
|
|
33
|
+
'map', 'filter', 'reduce', 'forEach', 'find', 'findIndex', 'some', 'every',
|
|
34
|
+
'includes', 'indexOf', 'slice', 'splice', 'concat', 'join', 'split',
|
|
35
|
+
'push', 'pop', 'shift', 'unshift', 'sort', 'reverse',
|
|
36
|
+
'keys', 'values', 'entries', 'assign', 'freeze', 'seal',
|
|
37
|
+
'hasOwnProperty', 'toString', 'valueOf',
|
|
38
|
+
]);
|
|
39
|
+
export const typescriptProvider = defineLanguage({
|
|
40
|
+
id: SupportedLanguages.TypeScript,
|
|
41
|
+
extensions: ['.ts', '.tsx'],
|
|
42
|
+
treeSitterQueries: TYPESCRIPT_QUERIES,
|
|
43
|
+
typeConfig: typescriptConfig,
|
|
44
|
+
exportChecker: tsExportChecker,
|
|
45
|
+
importResolver: resolveTypescriptImport,
|
|
46
|
+
namedBindingExtractor: extractTsNamedBindings,
|
|
47
|
+
fieldExtractor: typescriptFieldExtractor,
|
|
48
|
+
builtInNames: BUILT_INS,
|
|
49
|
+
});
|
|
50
|
+
export const javascriptProvider = defineLanguage({
|
|
51
|
+
id: SupportedLanguages.JavaScript,
|
|
52
|
+
extensions: ['.js', '.jsx'],
|
|
53
|
+
treeSitterQueries: JAVASCRIPT_QUERIES,
|
|
54
|
+
typeConfig: typescriptConfig,
|
|
55
|
+
exportChecker: tsExportChecker,
|
|
56
|
+
importResolver: resolveJavascriptImport,
|
|
57
|
+
namedBindingExtractor: extractTsNamedBindings,
|
|
58
|
+
fieldExtractor: createFieldExtractor(javascriptConfig),
|
|
59
|
+
builtInNames: BUILT_INS,
|
|
60
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Processor
|
|
3
|
+
*
|
|
4
|
+
* Extracts structure from .md files using regex (no tree-sitter dependency).
|
|
5
|
+
* Creates Section nodes for headings with hierarchy, and IMPORTS edges for
|
|
6
|
+
* cross-file links.
|
|
7
|
+
*/
|
|
8
|
+
import { KnowledgeGraph } from '../graph/types.js';
|
|
9
|
+
interface MdFile {
|
|
10
|
+
path: string;
|
|
11
|
+
content: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const processMarkdown: (graph: KnowledgeGraph, files: MdFile[], allPathSet: Set<string>) => {
|
|
14
|
+
sections: number;
|
|
15
|
+
links: number;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Processor
|
|
3
|
+
*
|
|
4
|
+
* Extracts structure from .md files using regex (no tree-sitter dependency).
|
|
5
|
+
* Creates Section nodes for headings with hierarchy, and IMPORTS edges for
|
|
6
|
+
* cross-file links.
|
|
7
|
+
*/
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { generateId } from '../../lib/utils.js';
|
|
10
|
+
const HEADING_RE = /^(#{1,6})\s+(.+)$/;
|
|
11
|
+
const LINK_RE = /\[([^\]]*)\]\(([^)]+)\)/g;
|
|
12
|
+
const MD_EXTENSIONS = new Set(['.md', '.mdx']);
|
|
13
|
+
export const processMarkdown = (graph, files, allPathSet) => {
|
|
14
|
+
let totalSections = 0;
|
|
15
|
+
let totalLinks = 0;
|
|
16
|
+
for (const file of files) {
|
|
17
|
+
const ext = path.extname(file.path).toLowerCase();
|
|
18
|
+
if (!MD_EXTENSIONS.has(ext))
|
|
19
|
+
continue;
|
|
20
|
+
const fileNodeId = generateId('File', file.path);
|
|
21
|
+
// Skip if file node doesn't exist (shouldn't happen, structure-processor creates it)
|
|
22
|
+
if (!graph.getNode(fileNodeId))
|
|
23
|
+
continue;
|
|
24
|
+
const lines = file.content.split('\n');
|
|
25
|
+
// --- Extract headings and build hierarchy ---
|
|
26
|
+
// First pass: collect all heading positions so we can compute endLine spans
|
|
27
|
+
const headings = [];
|
|
28
|
+
for (let i = 0; i < lines.length; i++) {
|
|
29
|
+
const match = lines[i].match(HEADING_RE);
|
|
30
|
+
if (!match)
|
|
31
|
+
continue;
|
|
32
|
+
headings.push({
|
|
33
|
+
level: match[1].length,
|
|
34
|
+
heading: match[2].trim(),
|
|
35
|
+
lineNum: i + 1, // 1-indexed
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// Second pass: create nodes with proper endLine spans
|
|
39
|
+
const sectionStack = [];
|
|
40
|
+
for (let h = 0; h < headings.length; h++) {
|
|
41
|
+
const { level, heading, lineNum } = headings[h];
|
|
42
|
+
// endLine = line before next heading at same or higher level, or EOF
|
|
43
|
+
let endLine = lines.length;
|
|
44
|
+
for (let j = h + 1; j < headings.length; j++) {
|
|
45
|
+
if (headings[j].level <= level) {
|
|
46
|
+
endLine = headings[j].lineNum - 1;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const sectionId = generateId('Section', `${file.path}:L${lineNum}:${heading}`);
|
|
51
|
+
const node = {
|
|
52
|
+
id: sectionId,
|
|
53
|
+
label: 'Section',
|
|
54
|
+
properties: {
|
|
55
|
+
name: heading,
|
|
56
|
+
filePath: file.path,
|
|
57
|
+
startLine: lineNum,
|
|
58
|
+
endLine,
|
|
59
|
+
level,
|
|
60
|
+
description: `h${level}`,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
graph.addNode(node);
|
|
64
|
+
totalSections++;
|
|
65
|
+
// Find parent: pop stack until we find a level strictly less than current
|
|
66
|
+
while (sectionStack.length > 0 && sectionStack[sectionStack.length - 1].level >= level) {
|
|
67
|
+
sectionStack.pop();
|
|
68
|
+
}
|
|
69
|
+
const parentId = sectionStack.length > 0
|
|
70
|
+
? sectionStack[sectionStack.length - 1].id
|
|
71
|
+
: fileNodeId;
|
|
72
|
+
graph.addRelationship({
|
|
73
|
+
id: generateId('CONTAINS', `${parentId}->${sectionId}`),
|
|
74
|
+
type: 'CONTAINS',
|
|
75
|
+
sourceId: parentId,
|
|
76
|
+
targetId: sectionId,
|
|
77
|
+
confidence: 1.0,
|
|
78
|
+
reason: 'markdown-heading',
|
|
79
|
+
});
|
|
80
|
+
sectionStack.push({ level, id: sectionId });
|
|
81
|
+
}
|
|
82
|
+
// --- Extract links to other files in the repo ---
|
|
83
|
+
const fileDir = path.dirname(file.path);
|
|
84
|
+
const seenLinks = new Set();
|
|
85
|
+
let linkMatch;
|
|
86
|
+
LINK_RE.lastIndex = 0;
|
|
87
|
+
while ((linkMatch = LINK_RE.exec(file.content)) !== null) {
|
|
88
|
+
const href = linkMatch[2];
|
|
89
|
+
// Skip external URLs, anchors, and mailto
|
|
90
|
+
if (href.startsWith('http://') || href.startsWith('https://') ||
|
|
91
|
+
href.startsWith('#') || href.startsWith('mailto:')) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// Strip anchor fragments from local links
|
|
95
|
+
const cleanHref = href.split('#')[0];
|
|
96
|
+
if (!cleanHref)
|
|
97
|
+
continue;
|
|
98
|
+
// Resolve relative to the file's directory, then normalize
|
|
99
|
+
const resolved = path.posix.normalize(path.posix.join(fileDir, cleanHref));
|
|
100
|
+
if (allPathSet.has(resolved)) {
|
|
101
|
+
const targetFileId = generateId('File', resolved);
|
|
102
|
+
// Skip if target file node doesn't exist
|
|
103
|
+
if (!graph.getNode(targetFileId))
|
|
104
|
+
continue;
|
|
105
|
+
// Dedup: skip if we've already linked this file pair
|
|
106
|
+
const linkKey = `${fileNodeId}->${targetFileId}`;
|
|
107
|
+
if (seenLinks.has(linkKey))
|
|
108
|
+
continue;
|
|
109
|
+
seenLinks.add(linkKey);
|
|
110
|
+
const relId = generateId('IMPORTS', linkKey);
|
|
111
|
+
graph.addRelationship({
|
|
112
|
+
id: relId,
|
|
113
|
+
type: 'IMPORTS',
|
|
114
|
+
sourceId: fileNodeId,
|
|
115
|
+
targetId: targetFileId,
|
|
116
|
+
confidence: 0.8,
|
|
117
|
+
reason: 'markdown-link',
|
|
118
|
+
});
|
|
119
|
+
totalLinks++;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { sections: totalSections, links: totalLinks };
|
|
124
|
+
};
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* Cypher: MATCH (c:Class)-[r:CodeRelation {type: 'OVERRIDES'}]->(m:Method)
|
|
20
20
|
*/
|
|
21
21
|
import { generateId } from '../../lib/utils.js';
|
|
22
|
-
import {
|
|
22
|
+
import { getProvider } from './languages/index.js';
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
// Internal helpers
|
|
25
25
|
// ---------------------------------------------------------------------------
|
|
@@ -158,10 +158,11 @@ function resolveByMroOrder(methodName, defs, mroOrder, reasonPrefix) {
|
|
|
158
158
|
return {
|
|
159
159
|
resolvedTo: match.methodId,
|
|
160
160
|
reason: `${reasonPrefix}: ${match.className}::${methodName}`,
|
|
161
|
+
confidence: 0.9, // MRO-ordered resolution
|
|
161
162
|
};
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
|
-
return { resolvedTo: defs[0].methodId, reason: `${reasonPrefix} fallback: first definition
|
|
165
|
+
return { resolvedTo: defs[0].methodId, reason: `${reasonPrefix} fallback: first definition`, confidence: 0.7 };
|
|
165
166
|
}
|
|
166
167
|
function resolveCsharpJava(methodName, defs, parentEdgeTypes) {
|
|
167
168
|
const classDefs = [];
|
|
@@ -179,21 +180,24 @@ function resolveCsharpJava(methodName, defs, parentEdgeTypes) {
|
|
|
179
180
|
return {
|
|
180
181
|
resolvedTo: classDefs[0].methodId,
|
|
181
182
|
reason: `class method wins: ${classDefs[0].className}::${methodName}`,
|
|
183
|
+
confidence: 0.95, // Class method is authoritative
|
|
182
184
|
};
|
|
183
185
|
}
|
|
184
186
|
if (interfaceDefs.length > 1) {
|
|
185
187
|
return {
|
|
186
188
|
resolvedTo: null,
|
|
187
189
|
reason: `ambiguous: ${methodName} defined in multiple interfaces: ${interfaceDefs.map(d => d.className).join(', ')}`,
|
|
190
|
+
confidence: 0.5,
|
|
188
191
|
};
|
|
189
192
|
}
|
|
190
193
|
if (interfaceDefs.length === 1) {
|
|
191
194
|
return {
|
|
192
195
|
resolvedTo: interfaceDefs[0].methodId,
|
|
193
196
|
reason: `single interface default: ${interfaceDefs[0].className}::${methodName}`,
|
|
197
|
+
confidence: 0.85, // Single interface, unambiguous
|
|
194
198
|
};
|
|
195
199
|
}
|
|
196
|
-
return { resolvedTo: null, reason: 'no resolution found' };
|
|
200
|
+
return { resolvedTo: null, reason: 'no resolution found', confidence: 0.5 };
|
|
197
201
|
}
|
|
198
202
|
// ---------------------------------------------------------------------------
|
|
199
203
|
// Main entry point
|
|
@@ -215,9 +219,10 @@ export function computeMRO(graph) {
|
|
|
215
219
|
if (!language)
|
|
216
220
|
continue;
|
|
217
221
|
const className = classNode.properties.name;
|
|
218
|
-
// Compute linearized MRO depending on language
|
|
222
|
+
// Compute linearized MRO depending on language strategy
|
|
223
|
+
const provider = getProvider(language);
|
|
219
224
|
let mroOrder;
|
|
220
|
-
if (
|
|
225
|
+
if (provider.mroStrategy === 'c3') {
|
|
221
226
|
const c3Result = c3Linearize(classId, parentMap, c3Cache);
|
|
222
227
|
mroOrder = c3Result ?? gatherAncestors(classId, parentMap);
|
|
223
228
|
}
|
|
@@ -260,8 +265,8 @@ export function computeMRO(graph) {
|
|
|
260
265
|
}
|
|
261
266
|
// Detect collisions: methods defined in 2+ different ancestors
|
|
262
267
|
const ambiguities = [];
|
|
263
|
-
// Compute transitive edge types once per class (only needed for
|
|
264
|
-
const needsEdgeTypes =
|
|
268
|
+
// Compute transitive edge types once per class (only needed for implements-split languages)
|
|
269
|
+
const needsEdgeTypes = provider.mroStrategy === 'implements-split';
|
|
265
270
|
const classEdgeTypes = needsEdgeTypes
|
|
266
271
|
? buildTransitiveEdgeTypes(classId, parentMap, parentEdgeType)
|
|
267
272
|
: undefined;
|
|
@@ -277,22 +282,21 @@ export function computeMRO(graph) {
|
|
|
277
282
|
if (ownDefinesIt)
|
|
278
283
|
continue;
|
|
279
284
|
let resolution;
|
|
280
|
-
switch (
|
|
281
|
-
case
|
|
282
|
-
resolution = resolveByMroOrder(methodName, defs, mroOrder, '
|
|
285
|
+
switch (provider.mroStrategy) {
|
|
286
|
+
case 'leftmost-base':
|
|
287
|
+
resolution = resolveByMroOrder(methodName, defs, mroOrder, 'leftmost base');
|
|
283
288
|
break;
|
|
284
|
-
case
|
|
285
|
-
case SupportedLanguages.Java:
|
|
286
|
-
case SupportedLanguages.Kotlin:
|
|
289
|
+
case 'implements-split':
|
|
287
290
|
resolution = resolveCsharpJava(methodName, defs, classEdgeTypes);
|
|
288
291
|
break;
|
|
289
|
-
case
|
|
290
|
-
resolution = resolveByMroOrder(methodName, defs, mroOrder, '
|
|
292
|
+
case 'c3':
|
|
293
|
+
resolution = resolveByMroOrder(methodName, defs, mroOrder, 'C3 MRO');
|
|
291
294
|
break;
|
|
292
|
-
case
|
|
295
|
+
case 'qualified-syntax':
|
|
293
296
|
resolution = {
|
|
294
297
|
resolvedTo: null,
|
|
295
|
-
reason: `
|
|
298
|
+
reason: `requires qualified syntax: <Type as Trait>::${methodName}()`,
|
|
299
|
+
confidence: 0.5,
|
|
296
300
|
};
|
|
297
301
|
break;
|
|
298
302
|
default:
|
|
@@ -316,7 +320,7 @@ export function computeMRO(graph) {
|
|
|
316
320
|
sourceId: classId,
|
|
317
321
|
targetId: resolution.resolvedTo,
|
|
318
322
|
type: 'OVERRIDES',
|
|
319
|
-
confidence:
|
|
323
|
+
confidence: resolution.confidence,
|
|
320
324
|
reason: resolution.reason,
|
|
321
325
|
});
|
|
322
326
|
overrideEdges++;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SymbolTable, SymbolDefinition } from './symbol-table.js';
|
|
2
|
+
import type { NamedImportMap } from './import-processor.js';
|
|
3
|
+
/**
|
|
4
|
+
* Walk a named-binding re-export chain through NamedImportMap.
|
|
5
|
+
*
|
|
6
|
+
* When file A imports { User } from B, and B re-exports { User } from C,
|
|
7
|
+
* the NamedImportMap for A points to B, but B has no User definition.
|
|
8
|
+
* This function follows the chain: A→B→C until a definition is found.
|
|
9
|
+
*
|
|
10
|
+
* Returns the definitions found at the end of the chain, or null if the
|
|
11
|
+
* chain breaks (missing binding, circular reference, or depth exceeded).
|
|
12
|
+
* Max depth 5 to prevent infinite loops.
|
|
13
|
+
*
|
|
14
|
+
* @param allDefs Pre-computed `symbolTable.lookupFuzzy(name)` result — must be the
|
|
15
|
+
* complete unfiltered result. Passing a file-filtered subset will cause
|
|
16
|
+
* silent misses at depth=0 for non-aliased bindings.
|
|
17
|
+
*/
|
|
18
|
+
export declare function walkBindingChain(name: string, currentFilePath: string, symbolTable: SymbolTable, namedImportMap: NamedImportMap, allDefs: SymbolDefinition[]): SymbolDefinition[] | null;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk a named-binding re-export chain through NamedImportMap.
|
|
3
|
+
*
|
|
4
|
+
* When file A imports { User } from B, and B re-exports { User } from C,
|
|
5
|
+
* the NamedImportMap for A points to B, but B has no User definition.
|
|
6
|
+
* This function follows the chain: A→B→C until a definition is found.
|
|
7
|
+
*
|
|
8
|
+
* Returns the definitions found at the end of the chain, or null if the
|
|
9
|
+
* chain breaks (missing binding, circular reference, or depth exceeded).
|
|
10
|
+
* Max depth 5 to prevent infinite loops.
|
|
11
|
+
*
|
|
12
|
+
* @param allDefs Pre-computed `symbolTable.lookupFuzzy(name)` result — must be the
|
|
13
|
+
* complete unfiltered result. Passing a file-filtered subset will cause
|
|
14
|
+
* silent misses at depth=0 for non-aliased bindings.
|
|
15
|
+
*/
|
|
16
|
+
export function walkBindingChain(name, currentFilePath, symbolTable, namedImportMap, allDefs) {
|
|
17
|
+
let lookupFile = currentFilePath;
|
|
18
|
+
let lookupName = name;
|
|
19
|
+
const visited = new Set();
|
|
20
|
+
for (let depth = 0; depth < 5; depth++) {
|
|
21
|
+
const bindings = namedImportMap.get(lookupFile);
|
|
22
|
+
if (!bindings)
|
|
23
|
+
return null;
|
|
24
|
+
const binding = bindings.get(lookupName);
|
|
25
|
+
if (!binding)
|
|
26
|
+
return null;
|
|
27
|
+
const key = `${binding.sourcePath}:${binding.exportedName}`;
|
|
28
|
+
if (visited.has(key))
|
|
29
|
+
return null; // circular
|
|
30
|
+
visited.add(key);
|
|
31
|
+
const targetName = binding.exportedName;
|
|
32
|
+
const resolvedDefs = targetName !== lookupName || depth > 0
|
|
33
|
+
? symbolTable.lookupFuzzy(targetName).filter(def => def.filePath === binding.sourcePath)
|
|
34
|
+
: allDefs.filter(def => def.filePath === binding.sourcePath);
|
|
35
|
+
if (resolvedDefs.length > 0)
|
|
36
|
+
return resolvedDefs;
|
|
37
|
+
// No definition in source file → follow re-export chain
|
|
38
|
+
lookupFile = binding.sourcePath;
|
|
39
|
+
lookupName = targetName;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function extractCSharpNamedBindings(importNode) {
|
|
2
|
+
// using_directive — three forms:
|
|
3
|
+
// using Alias = NS.Type; → aliasIdent + qualifiedName
|
|
4
|
+
// using static NS.Type; → static + qualifiedName (no alias)
|
|
5
|
+
// using NS; → qualifiedName only (namespace, not capturable)
|
|
6
|
+
if (importNode.type !== 'using_directive')
|
|
7
|
+
return undefined;
|
|
8
|
+
let aliasIdent = null;
|
|
9
|
+
let qualifiedName = null;
|
|
10
|
+
let isStatic = false;
|
|
11
|
+
for (let i = 0; i < importNode.childCount; i++) {
|
|
12
|
+
const child = importNode.child(i);
|
|
13
|
+
if (child?.text === 'static')
|
|
14
|
+
isStatic = true;
|
|
15
|
+
}
|
|
16
|
+
for (let i = 0; i < importNode.namedChildCount; i++) {
|
|
17
|
+
const child = importNode.namedChild(i);
|
|
18
|
+
if (child?.type === 'identifier' && !aliasIdent)
|
|
19
|
+
aliasIdent = child;
|
|
20
|
+
else if (child?.type === 'qualified_name')
|
|
21
|
+
qualifiedName = child;
|
|
22
|
+
}
|
|
23
|
+
// Form 1: using Alias = NS.Type;
|
|
24
|
+
if (aliasIdent && qualifiedName) {
|
|
25
|
+
const fullText = qualifiedName.text;
|
|
26
|
+
const exportedName = fullText.includes('.') ? fullText.split('.').pop() : fullText;
|
|
27
|
+
return [{ local: aliasIdent.text, exported: exportedName }];
|
|
28
|
+
}
|
|
29
|
+
// Form 2: using static NS.Type; — last segment is the class name
|
|
30
|
+
if (isStatic && qualifiedName) {
|
|
31
|
+
const fullText = qualifiedName.text;
|
|
32
|
+
const lastSegment = fullText.includes('.') ? fullText.split('.').pop() : fullText;
|
|
33
|
+
return [{ local: lastSegment, exported: lastSegment }];
|
|
34
|
+
}
|
|
35
|
+
// Form 3: using NS; — namespace import, can't resolve to per-symbol bindings
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { findChild } from '../utils/ast-helpers.js';
|
|
2
|
+
export function extractJavaNamedBindings(importNode) {
|
|
3
|
+
// import_declaration > scoped_identifier "com.example.models.User"
|
|
4
|
+
// Wildcard imports (.*) don't produce named bindings
|
|
5
|
+
if (importNode.type !== 'import_declaration')
|
|
6
|
+
return undefined;
|
|
7
|
+
// Check for asterisk (wildcard import) and static modifier
|
|
8
|
+
let isStatic = false;
|
|
9
|
+
for (let i = 0; i < importNode.childCount; i++) {
|
|
10
|
+
const child = importNode.child(i);
|
|
11
|
+
if (child?.type === 'asterisk')
|
|
12
|
+
return undefined;
|
|
13
|
+
if (child?.text === 'static')
|
|
14
|
+
isStatic = true;
|
|
15
|
+
}
|
|
16
|
+
const scopedId = findChild(importNode, 'scoped_identifier');
|
|
17
|
+
if (!scopedId)
|
|
18
|
+
return undefined;
|
|
19
|
+
const fullText = scopedId.text;
|
|
20
|
+
const lastDot = fullText.lastIndexOf('.');
|
|
21
|
+
if (lastDot === -1)
|
|
22
|
+
return undefined;
|
|
23
|
+
const name = fullText.slice(lastDot + 1);
|
|
24
|
+
// Non-static: skip lowercase names — those are package imports, not class imports.
|
|
25
|
+
// Static: allow lowercase — `import static models.UserFactory.getUser` imports a method.
|
|
26
|
+
if (!isStatic && name[0] && name[0] === name[0].toLowerCase())
|
|
27
|
+
return undefined;
|
|
28
|
+
return [{ local: name, exported: name }];
|
|
29
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { findChild } from '../utils/ast-helpers.js';
|
|
2
|
+
export function extractKotlinNamedBindings(importNode) {
|
|
3
|
+
// import_header > identifier + import_alias > simple_identifier
|
|
4
|
+
if (importNode.type !== 'import_header')
|
|
5
|
+
return undefined;
|
|
6
|
+
const fullIdent = findChild(importNode, 'identifier');
|
|
7
|
+
if (!fullIdent)
|
|
8
|
+
return undefined;
|
|
9
|
+
const fullText = fullIdent.text;
|
|
10
|
+
const exportedName = fullText.includes('.') ? fullText.split('.').pop() : fullText;
|
|
11
|
+
const importAlias = findChild(importNode, 'import_alias');
|
|
12
|
+
if (importAlias) {
|
|
13
|
+
// Aliased: import com.example.User as U
|
|
14
|
+
const aliasIdent = findChild(importAlias, 'simple_identifier');
|
|
15
|
+
if (!aliasIdent)
|
|
16
|
+
return undefined;
|
|
17
|
+
return [{ local: aliasIdent.text, exported: exportedName }];
|
|
18
|
+
}
|
|
19
|
+
// Non-aliased: import com.example.User → local="User", exported="User"
|
|
20
|
+
// Also handles top-level function imports: import models.getUser → local="getUser"
|
|
21
|
+
// Skip wildcard imports (ending in *)
|
|
22
|
+
if (fullText.endsWith('.*') || fullText.endsWith('*'))
|
|
23
|
+
return undefined;
|
|
24
|
+
// Skip class-member imports (e.g., import util.OneArg.writeAudit) where the
|
|
25
|
+
// second-to-last segment is PascalCase (a class name). Multiple member imports
|
|
26
|
+
// with the same function name would collide in NamedImportMap, breaking
|
|
27
|
+
// arity-based disambiguation. Top-level function imports (import models.getUser)
|
|
28
|
+
// and class imports (import models.User) have package-only prefixes.
|
|
29
|
+
const segments = fullText.split('.');
|
|
30
|
+
if (segments.length >= 3) {
|
|
31
|
+
const parentSegment = segments[segments.length - 2];
|
|
32
|
+
if (parentSegment[0] && parentSegment[0] === parentSegment[0].toUpperCase())
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
return [{ local: exportedName, exported: exportedName }];
|
|
36
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export function extractPhpNamedBindings(importNode) {
|
|
2
|
+
// namespace_use_declaration > namespace_use_clause* (flat)
|
|
3
|
+
// namespace_use_declaration > namespace_use_group > namespace_use_clause* (grouped)
|
|
4
|
+
if (importNode.type !== 'namespace_use_declaration')
|
|
5
|
+
return undefined;
|
|
6
|
+
// Skip 'use function' and 'use const' declarations — these import callables/constants,
|
|
7
|
+
// not class types, and should not be added to namedImportMap as type bindings.
|
|
8
|
+
const useTypeNode = importNode.childForFieldName?.('type');
|
|
9
|
+
if (useTypeNode && (useTypeNode.text === 'function' || useTypeNode.text === 'const')) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const bindings = [];
|
|
13
|
+
// Collect all clauses — from direct children AND from namespace_use_group
|
|
14
|
+
const clauses = [];
|
|
15
|
+
for (let i = 0; i < importNode.namedChildCount; i++) {
|
|
16
|
+
const child = importNode.namedChild(i);
|
|
17
|
+
if (child?.type === 'namespace_use_clause') {
|
|
18
|
+
clauses.push(child);
|
|
19
|
+
}
|
|
20
|
+
else if (child?.type === 'namespace_use_group') {
|
|
21
|
+
for (let j = 0; j < child.namedChildCount; j++) {
|
|
22
|
+
const groupChild = child.namedChild(j);
|
|
23
|
+
if (groupChild?.type === 'namespace_use_clause')
|
|
24
|
+
clauses.push(groupChild);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
for (const clause of clauses) {
|
|
29
|
+
// Flat imports: qualified_name + name (alias)
|
|
30
|
+
let qualifiedName = null;
|
|
31
|
+
const names = [];
|
|
32
|
+
for (let j = 0; j < clause.namedChildCount; j++) {
|
|
33
|
+
const child = clause.namedChild(j);
|
|
34
|
+
if (child?.type === 'qualified_name')
|
|
35
|
+
qualifiedName = child;
|
|
36
|
+
else if (child?.type === 'name')
|
|
37
|
+
names.push(child);
|
|
38
|
+
}
|
|
39
|
+
if (qualifiedName && names.length > 0) {
|
|
40
|
+
// Flat aliased import: use App\Models\Repo as R;
|
|
41
|
+
const fullText = qualifiedName.text;
|
|
42
|
+
const exportedName = fullText.includes('\\') ? fullText.split('\\').pop() : fullText;
|
|
43
|
+
bindings.push({ local: names[0].text, exported: exportedName });
|
|
44
|
+
}
|
|
45
|
+
else if (qualifiedName && names.length === 0) {
|
|
46
|
+
// Flat non-aliased import: use App\Models\User;
|
|
47
|
+
const fullText = qualifiedName.text;
|
|
48
|
+
const lastSegment = fullText.includes('\\') ? fullText.split('\\').pop() : fullText;
|
|
49
|
+
bindings.push({ local: lastSegment, exported: lastSegment });
|
|
50
|
+
}
|
|
51
|
+
else if (!qualifiedName && names.length >= 2) {
|
|
52
|
+
// Grouped aliased import: {Repo as R} — first name = exported, second = alias
|
|
53
|
+
bindings.push({ local: names[1].text, exported: names[0].text });
|
|
54
|
+
}
|
|
55
|
+
else if (!qualifiedName && names.length === 1) {
|
|
56
|
+
// Grouped non-aliased import: {User} in use App\Models\{User, Repo as R}
|
|
57
|
+
bindings.push({ local: names[0].text, exported: names[0].text });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return bindings.length > 0 ? bindings : undefined;
|
|
61
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { findChild } from '../utils/ast-helpers.js';
|
|
2
|
+
export function extractPythonNamedBindings(importNode) {
|
|
3
|
+
// Handle: from x import User, Repo as R
|
|
4
|
+
if (importNode.type === 'import_from_statement') {
|
|
5
|
+
const bindings = [];
|
|
6
|
+
for (let i = 0; i < importNode.namedChildCount; i++) {
|
|
7
|
+
const child = importNode.namedChild(i);
|
|
8
|
+
if (!child)
|
|
9
|
+
continue;
|
|
10
|
+
if (child.type === 'dotted_name') {
|
|
11
|
+
// Skip the module_name (first dotted_name is the source module)
|
|
12
|
+
const fieldName = importNode.childForFieldName?.('module_name');
|
|
13
|
+
if (fieldName && child.startIndex === fieldName.startIndex)
|
|
14
|
+
continue;
|
|
15
|
+
// This is an imported name: from x import User
|
|
16
|
+
const name = child.text;
|
|
17
|
+
if (name)
|
|
18
|
+
bindings.push({ local: name, exported: name });
|
|
19
|
+
}
|
|
20
|
+
if (child.type === 'aliased_import') {
|
|
21
|
+
// from x import Repo as R
|
|
22
|
+
const dottedName = findChild(child, 'dotted_name');
|
|
23
|
+
const aliasIdent = findChild(child, 'identifier');
|
|
24
|
+
if (dottedName && aliasIdent) {
|
|
25
|
+
bindings.push({ local: aliasIdent.text, exported: dottedName.text });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return bindings.length > 0 ? bindings : undefined;
|
|
30
|
+
}
|
|
31
|
+
// Handle: import numpy as np (import_statement with aliased_import child)
|
|
32
|
+
// Tagged with isModuleAlias so applyImportResult routes these directly to
|
|
33
|
+
// moduleAliasMap (e.g. "np" → "numpy.py") instead of namedImportMap.
|
|
34
|
+
if (importNode.type === 'import_statement') {
|
|
35
|
+
const bindings = [];
|
|
36
|
+
for (let i = 0; i < importNode.namedChildCount; i++) {
|
|
37
|
+
const child = importNode.namedChild(i);
|
|
38
|
+
if (!child || child.type !== 'aliased_import')
|
|
39
|
+
continue;
|
|
40
|
+
const dottedName = findChild(child, 'dotted_name');
|
|
41
|
+
const aliasIdent = findChild(child, 'identifier');
|
|
42
|
+
if (dottedName && aliasIdent) {
|
|
43
|
+
bindings.push({ local: aliasIdent.text, exported: dottedName.text, isModuleAlias: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return bindings.length > 0 ? bindings : undefined;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|