@codemap-ai/code-index 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/dist/file-discovery.d.ts +36 -0
  3. package/dist/file-discovery.d.ts.map +1 -0
  4. package/dist/file-discovery.js +204 -0
  5. package/dist/index.d.ts +6 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +26 -0
  8. package/dist/language-utils.d.ts +10 -0
  9. package/dist/language-utils.d.ts.map +1 -0
  10. package/dist/language-utils.js +75 -0
  11. package/dist/parsers/dart.d.ts +4 -0
  12. package/dist/parsers/dart.d.ts.map +1 -0
  13. package/dist/parsers/dart.js +353 -0
  14. package/dist/parsers/index.d.ts +11 -0
  15. package/dist/parsers/index.d.ts.map +1 -0
  16. package/dist/parsers/index.js +32 -0
  17. package/dist/parsers/java.d.ts +4 -0
  18. package/dist/parsers/java.d.ts.map +1 -0
  19. package/dist/parsers/java.js +222 -0
  20. package/dist/parsers/kotlin.d.ts +4 -0
  21. package/dist/parsers/kotlin.d.ts.map +1 -0
  22. package/dist/parsers/kotlin.js +221 -0
  23. package/dist/parsers/php.d.ts +4 -0
  24. package/dist/parsers/php.d.ts.map +1 -0
  25. package/dist/parsers/php.js +215 -0
  26. package/dist/parsers/python.d.ts +4 -0
  27. package/dist/parsers/python.d.ts.map +1 -0
  28. package/dist/parsers/python.js +200 -0
  29. package/dist/parsers/shared.d.ts +30 -0
  30. package/dist/parsers/shared.d.ts.map +1 -0
  31. package/dist/parsers/shared.js +200 -0
  32. package/dist/parsers/types.d.ts +93 -0
  33. package/dist/parsers/types.d.ts.map +1 -0
  34. package/dist/parsers/types.js +12 -0
  35. package/dist/parsers/typescript.d.ts +5 -0
  36. package/dist/parsers/typescript.d.ts.map +1 -0
  37. package/dist/parsers/typescript.js +549 -0
  38. package/dist/ts-resolver.d.ts +17 -0
  39. package/dist/ts-resolver.d.ts.map +1 -0
  40. package/dist/ts-resolver.js +110 -0
  41. package/package.json +30 -0
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseKotlinFile = parseKotlinFile;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const web_tree_sitter_1 = require("web-tree-sitter");
9
+ const shared_js_1 = require("./shared.js");
10
+ const types_js_1 = require("./types.js");
11
+ let parserReady = null;
12
+ function getKotlinParser() {
13
+ if (!parserReady) {
14
+ parserReady = (async () => {
15
+ await web_tree_sitter_1.Parser.init();
16
+ const wasmPath = node_path_1.default.resolve(node_path_1.default.dirname(require.resolve("tree-sitter-wasms/package.json")), "out", "tree-sitter-kotlin.wasm");
17
+ const language = await web_tree_sitter_1.Language.load(wasmPath);
18
+ const parser = new web_tree_sitter_1.Parser();
19
+ parser.setLanguage(language);
20
+ return { parser, language };
21
+ })();
22
+ }
23
+ return parserReady;
24
+ }
25
+ async function parseKotlinFile(file, projectImportId) {
26
+ const content = file.content ?? "";
27
+ if (!content.trim())
28
+ return { ...types_js_1.EMPTY_SEMANTICS };
29
+ let parser;
30
+ try {
31
+ ({ parser } = await getKotlinParser());
32
+ }
33
+ catch {
34
+ return parseKotlinFileWithRegexFallback(file, projectImportId);
35
+ }
36
+ const tree = parser.parse(content);
37
+ if (!tree)
38
+ return parseKotlinFileWithRegexFallback(file, projectImportId);
39
+ const semantics = createEmptySemantics();
40
+ const lines = content.split(/\r?\n/);
41
+ const seenSymbols = new Set();
42
+ const seenImports = new Set();
43
+ walkKotlinAst(tree.rootNode, (node) => {
44
+ const line = node.startPosition.row + 1;
45
+ const col = node.startPosition.column;
46
+ const packageName = parseKotlinPackage(node.text);
47
+ if (packageName) {
48
+ addKotlinSymbol(semantics, seenSymbols, file, "namespace", packageName, lines[node.startPosition.row] ?? node.text, line, col, node.endPosition.row + 1, node.endPosition.column);
49
+ return;
50
+ }
51
+ const importName = parseKotlinImport(node.text);
52
+ if (importName) {
53
+ addKotlinImport(semantics, seenImports, file, projectImportId, importName, line, col, node.endPosition.row + 1, node.endPosition.column);
54
+ return;
55
+ }
56
+ const symbol = parseKotlinSymbol(node.text, isKotlinMemberNode(node));
57
+ if (!symbol)
58
+ return;
59
+ addKotlinSymbol(semantics, seenSymbols, file, symbol.kind, symbol.displayName, lines[node.startPosition.row] ?? node.text, line, col, node.endPosition.row + 1, node.endPosition.column, findParentKotlinSymbolLocalKey(node, file.path));
60
+ });
61
+ return semantics;
62
+ }
63
+ function createEmptySemantics() {
64
+ return {
65
+ symbols: [],
66
+ imports: [],
67
+ exports: [],
68
+ relationships: [],
69
+ calls: [],
70
+ issues: [],
71
+ externalSymbols: [],
72
+ };
73
+ }
74
+ function walkKotlinAst(node, visitor) {
75
+ visitor(node);
76
+ for (let i = 0; i < node.childCount; i++) {
77
+ const child = node.child(i);
78
+ if (child)
79
+ walkKotlinAst(child, visitor);
80
+ }
81
+ }
82
+ function parseKotlinPackage(text) {
83
+ return text.trim().match(/^package\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/)?.[1] ?? null;
84
+ }
85
+ function parseKotlinImport(text) {
86
+ return text.trim().match(/^import\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*(?:\.\*)?)(?:\s+as\s+[A-Za-z_]\w*)?/)?.[1] ?? null;
87
+ }
88
+ function parseKotlinSymbol(text, isMemberNode) {
89
+ const trimmed = text.trim();
90
+ const typeMatch = trimmed.match(/^(?:(?:public|protected|private|internal|open|abstract|sealed|data|value|inner|companion)\s+)*(class|interface|object|enum\s+class)\s+([A-Za-z_]\w*)/);
91
+ if (typeMatch?.[1] && typeMatch[2]) {
92
+ const kind = typeMatch[1] === "interface"
93
+ ? "interface"
94
+ : typeMatch[1] === "enum class"
95
+ ? "enum"
96
+ : "class";
97
+ return { displayName: typeMatch[2], kind };
98
+ }
99
+ const funMatch = trimmed.match(/^(?:(?:public|protected|private|internal|open|override|suspend|inline|tailrec|operator|infix|external)\s+)*fun\s+(?:[A-Za-z_]\w*\.)?([A-Za-z_]\w*)\s*(?:<[^>{}]+>)?\(/);
100
+ if (funMatch?.[1]) {
101
+ return { displayName: funMatch[1], kind: isMemberNode ? "method" : "function" };
102
+ }
103
+ const propertyMatch = trimmed.match(/^(?:(?:public|protected|private|internal|open|override|lateinit|const)\s+)*(val|var)\s+([A-Za-z_]\w*)\b/);
104
+ if (propertyMatch?.[2]) {
105
+ return { displayName: propertyMatch[2], kind: isMemberNode ? "property" : "variable" };
106
+ }
107
+ return null;
108
+ }
109
+ function isKotlinMemberNode(node) {
110
+ let current = node.parent;
111
+ while (current) {
112
+ if (/^(?:(?:public|protected|private|internal|open|abstract|sealed|data|value|inner|companion)\s+)*(class|interface|object|enum\s+class)\s+/.test(current.text.trimStart())) {
113
+ return true;
114
+ }
115
+ current = current.parent;
116
+ }
117
+ return false;
118
+ }
119
+ function findParentKotlinSymbolLocalKey(node, filePath) {
120
+ let current = node.parent;
121
+ while (current) {
122
+ const parentSymbol = parseKotlinSymbol(current.text, false);
123
+ if (parentSymbol && ["class", "interface", "enum"].includes(parentSymbol.kind)) {
124
+ return (0, shared_js_1.buildLocalSymbolKey)(filePath, parentSymbol.kind, parentSymbol.displayName);
125
+ }
126
+ current = current.parent;
127
+ }
128
+ return undefined;
129
+ }
130
+ function addKotlinImport(semantics, seenImports, file, projectImportId, moduleSpecifier, line, col, endLine, endCol) {
131
+ const localKey = (0, shared_js_1.buildImportLocalKey)(file.path, "import", moduleSpecifier, line, col);
132
+ if (seenImports.has(localKey))
133
+ return;
134
+ seenImports.add(localKey);
135
+ semantics.imports.push({
136
+ localKey,
137
+ moduleSpecifier,
138
+ importKind: "import",
139
+ isTypeOnly: false,
140
+ importedNames: [moduleSpecifier.split(".").pop() ?? moduleSpecifier],
141
+ line,
142
+ col,
143
+ endLine,
144
+ endCol,
145
+ resolutionKind: "package",
146
+ targetPathText: null,
147
+ targetExternalSymbolKey: `kotlin:${moduleSpecifier}`,
148
+ });
149
+ semantics.externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, moduleSpecifier));
150
+ }
151
+ function addKotlinSymbol(semantics, seenSymbols, file, kind, displayName, signature, line, col, endLine, endCol, parentSymbolLocalKey) {
152
+ const stableKey = (0, shared_js_1.buildStableSymbolKey)(file.path, kind, displayName, line);
153
+ if (seenSymbols.has(stableKey))
154
+ return;
155
+ seenSymbols.add(stableKey);
156
+ semantics.symbols.push({
157
+ localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, kind, displayName),
158
+ stableKey,
159
+ displayName,
160
+ kind,
161
+ language: file.language,
162
+ signature: signature.trim(),
163
+ returnType: null,
164
+ doc: null,
165
+ isExported: true,
166
+ isDefaultExport: false,
167
+ line,
168
+ col,
169
+ endLine,
170
+ endCol,
171
+ parentSymbolLocalKey,
172
+ });
173
+ }
174
+ function parseKotlinFileWithRegexFallback(file, projectImportId) {
175
+ const semantics = createEmptySemantics();
176
+ const lines = (0, shared_js_1.maskCommentsAndTemplateLiterals)(file.content ?? "").split(/\r?\n/);
177
+ const originalLines = (file.content ?? "").split(/\r?\n/);
178
+ const seenSymbols = new Set();
179
+ const seenImports = new Set();
180
+ const containerStack = [];
181
+ let braceDepth = 0;
182
+ lines.forEach((line, index) => {
183
+ const originalLine = originalLines[index] ?? line;
184
+ const lineNumber = index + 1;
185
+ while (containerStack.length > 0 && braceDepth < containerStack[containerStack.length - 1].depth) {
186
+ containerStack.pop();
187
+ }
188
+ const parentContainer = containerStack[containerStack.length - 1];
189
+ let matchedContainer = null;
190
+ const packageName = parseKotlinPackage(line);
191
+ if (packageName) {
192
+ addKotlinSymbol(semantics, seenSymbols, file, "namespace", packageName, originalLine, lineNumber, line.indexOf(packageName), lineNumber, line.length);
193
+ }
194
+ const importName = parseKotlinImport(line);
195
+ if (importName) {
196
+ addKotlinImport(semantics, seenImports, file, projectImportId, importName, lineNumber, line.indexOf(importName), lineNumber, line.length);
197
+ }
198
+ const typeSymbol = parseKotlinSymbol(line, false);
199
+ if (typeSymbol && ["class", "interface", "enum"].includes(typeSymbol.kind)) {
200
+ addKotlinSymbol(semantics, seenSymbols, file, typeSymbol.kind, typeSymbol.displayName, originalLine, lineNumber, line.indexOf(typeSymbol.displayName), lineNumber, line.length);
201
+ matchedContainer = {
202
+ kind: typeSymbol.kind,
203
+ displayName: typeSymbol.displayName,
204
+ };
205
+ }
206
+ const funSymbol = parseKotlinSymbol(line, Boolean(parentContainer));
207
+ if (funSymbol && (funSymbol.kind === "function" || funSymbol.kind === "method" || funSymbol.kind === "property" || funSymbol.kind === "variable")) {
208
+ const parentSymbolLocalKey = parentContainer
209
+ ? (0, shared_js_1.buildLocalSymbolKey)(file.path, parentContainer.kind, parentContainer.displayName)
210
+ : undefined;
211
+ addKotlinSymbol(semantics, seenSymbols, file, funSymbol.kind, funSymbol.displayName, originalLine, lineNumber, line.indexOf(funSymbol.displayName), lineNumber, line.length, parentSymbolLocalKey);
212
+ }
213
+ const openBraces = (line.match(/{/g) ?? []).length;
214
+ const closeBraces = (line.match(/}/g) ?? []).length;
215
+ if (matchedContainer && openBraces > 0) {
216
+ containerStack.push({ ...matchedContainer, depth: braceDepth + 1 });
217
+ }
218
+ braceDepth += openBraces - closeBraces;
219
+ });
220
+ return semantics;
221
+ }
@@ -0,0 +1,4 @@
1
+ import type { WorkspaceFileCandidate } from "../file-discovery.js";
2
+ import { type ParsedWorkspaceSemantics } from "./types.js";
3
+ export declare function parsePhpFile(file: WorkspaceFileCandidate, projectImportId: string): Promise<ParsedWorkspaceSemantics>;
4
+ //# sourceMappingURL=php.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"php.d.ts","sourceRoot":"","sources":["../../src/parsers/php.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAsB5E,wBAAsB,YAAY,CAChC,IAAI,EAAE,sBAAsB,EAC5B,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,wBAAwB,CAAC,CA6HnC"}
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parsePhpFile = parsePhpFile;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const web_tree_sitter_1 = require("web-tree-sitter");
9
+ const shared_js_1 = require("./shared.js");
10
+ const types_js_1 = require("./types.js");
11
+ let parserReady = null;
12
+ function getPhpParser() {
13
+ if (!parserReady) {
14
+ parserReady = (async () => {
15
+ await web_tree_sitter_1.Parser.init();
16
+ const wasmPath = node_path_1.default.resolve(node_path_1.default.dirname(require.resolve("tree-sitter-wasms/package.json")), "out", "tree-sitter-php.wasm");
17
+ const language = await web_tree_sitter_1.Language.load(wasmPath);
18
+ const parser = new web_tree_sitter_1.Parser();
19
+ parser.setLanguage(language);
20
+ return { parser, language };
21
+ })();
22
+ }
23
+ return parserReady;
24
+ }
25
+ async function parsePhpFile(file, projectImportId) {
26
+ const content = file.content ?? "";
27
+ if (!content.trim())
28
+ return { ...types_js_1.EMPTY_SEMANTICS };
29
+ let parser;
30
+ let language;
31
+ try {
32
+ ({ parser, language } = await getPhpParser());
33
+ }
34
+ catch {
35
+ return parsePhpFileWithRegexFallback(file, projectImportId);
36
+ }
37
+ const tree = parser.parse(content);
38
+ if (!tree)
39
+ return parsePhpFileWithRegexFallback(file, projectImportId);
40
+ const semantics = {
41
+ symbols: [],
42
+ imports: [],
43
+ exports: [],
44
+ relationships: [],
45
+ calls: [],
46
+ issues: [],
47
+ externalSymbols: [],
48
+ };
49
+ const lines = content.split(/\r?\n/);
50
+ // Extract symbols: namespace, class, interface, trait, enum, function, method
51
+ const symbolQuery = new web_tree_sitter_1.Query(language, `
52
+ (namespace_definition name: (namespace_name) @name) @namespace
53
+ (class_declaration name: (name) @name) @class
54
+ (interface_declaration name: (name) @name) @interface
55
+ (trait_declaration name: (name) @name) @trait
56
+ (enum_declaration name: (name) @name) @enum
57
+ (function_definition name: (name) @name) @function
58
+ (method_declaration name: (name) @name) @method
59
+ `);
60
+ for (const match of symbolQuery.matches(tree.rootNode)) {
61
+ const defCapture = match.captures.find((c) => ["namespace", "class", "interface", "trait", "enum", "function", "method"].includes(c.name));
62
+ const nameCapture = match.captures.find((c) => c.name === "name");
63
+ if (!defCapture || !nameCapture)
64
+ continue;
65
+ const captureName = defCapture.name;
66
+ const kind = captureName === "namespace" ? "namespace" : captureName;
67
+ const displayName = nameCapture.node.text;
68
+ const startPos = defCapture.node.startPosition;
69
+ const endPos = defCapture.node.endPosition;
70
+ const line = startPos.row + 1;
71
+ const col = startPos.column;
72
+ semantics.symbols.push({
73
+ localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, kind, displayName),
74
+ stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, kind, displayName, line),
75
+ displayName,
76
+ kind,
77
+ language: file.language,
78
+ signature: lines[startPos.row]?.trim() ?? null,
79
+ returnType: null,
80
+ doc: null,
81
+ isExported: true,
82
+ isDefaultExport: false,
83
+ line,
84
+ col,
85
+ endLine: endPos.row + 1,
86
+ endCol: endPos.column,
87
+ });
88
+ }
89
+ // Extract use statements and require/include
90
+ const importQuery = new web_tree_sitter_1.Query(language, `
91
+ (use_declaration (use_declarator (qualified_name) @name)) @use
92
+ (namespace_use_declaration (namespace_use_clause (qualified_name) @name)) @use
93
+ (require_expression (string) @path) @require
94
+ (require_once_expression (string) @path) @require_once
95
+ (include_expression (string) @path) @include
96
+ (include_once_expression (string) @path) @include_once
97
+ `);
98
+ const seenImportKeys = new Set();
99
+ for (const match of importQuery.matches(tree.rootNode)) {
100
+ const stmtCapture = match.captures.find((c) => ["use", "require", "require_once", "include", "include_once"].includes(c.name));
101
+ const valueCapture = match.captures.find((c) => c.name === "name" || c.name === "path");
102
+ if (!stmtCapture || !valueCapture)
103
+ continue;
104
+ const stmtNode = stmtCapture.node;
105
+ const startPos = stmtNode.startPosition;
106
+ const endPos = stmtNode.endPosition;
107
+ const line = startPos.row + 1;
108
+ const col = startPos.column;
109
+ const isUse = stmtCapture.name === "use";
110
+ const specifier = valueCapture.node.text.replace(/^['"]|['"]$/g, "");
111
+ const localKey = (0, shared_js_1.buildImportLocalKey)(file.path, isUse ? "use" : "import", specifier, line, col);
112
+ if (seenImportKeys.has(localKey))
113
+ continue;
114
+ seenImportKeys.add(localKey);
115
+ semantics.imports.push({
116
+ localKey,
117
+ moduleSpecifier: specifier,
118
+ importKind: isUse ? "use" : "include",
119
+ isTypeOnly: false,
120
+ importedNames: [],
121
+ line,
122
+ col,
123
+ endLine: endPos.row + 1,
124
+ endCol: endPos.column,
125
+ resolutionKind: isUse ? "package" : "unresolved",
126
+ targetPathText: isUse ? null : specifier,
127
+ targetExternalSymbolKey: isUse ? `php:${specifier}` : null,
128
+ });
129
+ if (isUse) {
130
+ semantics.externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, specifier));
131
+ }
132
+ }
133
+ return semantics;
134
+ }
135
+ function parsePhpFileWithRegexFallback(file, projectImportId) {
136
+ const semantics = {
137
+ symbols: [],
138
+ imports: [],
139
+ exports: [],
140
+ relationships: [],
141
+ calls: [],
142
+ issues: [],
143
+ externalSymbols: [],
144
+ };
145
+ const lines = (file.content ?? "").split(/\r?\n/);
146
+ lines.forEach((line, index) => {
147
+ const lineNumber = index + 1;
148
+ const namespaceMatch = line.match(/^\s*namespace\s+([^;{]+)\s*[;{]/);
149
+ if (namespaceMatch?.[1]) {
150
+ const displayName = namespaceMatch[1].trim();
151
+ const col = line.indexOf(displayName);
152
+ semantics.symbols.push({
153
+ localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, "namespace", displayName),
154
+ stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, "namespace", displayName, lineNumber),
155
+ displayName,
156
+ kind: "namespace",
157
+ language: file.language,
158
+ signature: line.trim(),
159
+ returnType: null,
160
+ doc: null,
161
+ isExported: true,
162
+ isDefaultExport: false,
163
+ line: lineNumber,
164
+ col: Math.max(col, 0),
165
+ endLine: lineNumber,
166
+ endCol: Math.max(col, 0) + displayName.length,
167
+ });
168
+ return;
169
+ }
170
+ const useMatch = line.match(/^\s*use\s+([^;]+)\s*;/);
171
+ if (useMatch?.[1]) {
172
+ const moduleSpecifier = useMatch[1].trim();
173
+ const col = line.indexOf(moduleSpecifier);
174
+ semantics.imports.push({
175
+ localKey: (0, shared_js_1.buildImportLocalKey)(file.path, "use", moduleSpecifier, lineNumber, Math.max(col, 0)),
176
+ moduleSpecifier,
177
+ importKind: "use",
178
+ isTypeOnly: false,
179
+ importedNames: [moduleSpecifier.split("\\").pop() ?? moduleSpecifier],
180
+ line: lineNumber,
181
+ col: Math.max(col, 0),
182
+ endLine: lineNumber,
183
+ endCol: Math.max(col, 0) + moduleSpecifier.length,
184
+ resolutionKind: "package",
185
+ targetPathText: null,
186
+ targetExternalSymbolKey: `php:${moduleSpecifier}`,
187
+ });
188
+ semantics.externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, moduleSpecifier));
189
+ return;
190
+ }
191
+ const symbolMatch = line.match(/^\s*(class|interface|trait|enum|function)\s+([A-Za-z_][A-Za-z0-9_]*)/);
192
+ if (!symbolMatch?.[1] || !symbolMatch[2])
193
+ return;
194
+ const kind = symbolMatch[1];
195
+ const displayName = symbolMatch[2];
196
+ const col = line.indexOf(displayName);
197
+ semantics.symbols.push({
198
+ localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, kind, displayName),
199
+ stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, kind, displayName, lineNumber),
200
+ displayName,
201
+ kind,
202
+ language: file.language,
203
+ signature: line.trim(),
204
+ returnType: null,
205
+ doc: null,
206
+ isExported: true,
207
+ isDefaultExport: false,
208
+ line: lineNumber,
209
+ col: Math.max(col, 0),
210
+ endLine: lineNumber,
211
+ endCol: Math.max(col, 0) + displayName.length,
212
+ });
213
+ });
214
+ return semantics;
215
+ }
@@ -0,0 +1,4 @@
1
+ import type { WorkspaceFileCandidate } from "../file-discovery.js";
2
+ import { type ParsedWorkspaceSemantics } from "./types.js";
3
+ export declare function parsePythonFile(file: WorkspaceFileCandidate, filePathSet: Set<string>, projectImportId: string): Promise<ParsedWorkspaceSemantics>;
4
+ //# sourceMappingURL=python.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python.d.ts","sourceRoot":"","sources":["../../src/parsers/python.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAqB5E,wBAAsB,eAAe,CACnC,IAAI,EAAE,sBAAsB,EAC5B,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,wBAAwB,CAAC,CA4LnC"}
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parsePythonFile = parsePythonFile;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const web_tree_sitter_1 = require("web-tree-sitter");
9
+ const shared_js_1 = require("./shared.js");
10
+ const types_js_1 = require("./types.js");
11
+ let parserReady = null;
12
+ function getPythonParser() {
13
+ if (!parserReady) {
14
+ parserReady = (async () => {
15
+ await web_tree_sitter_1.Parser.init();
16
+ const wasmPath = node_path_1.default.resolve(node_path_1.default.dirname(require.resolve("tree-sitter-python/package.json")), "tree-sitter-python.wasm");
17
+ const language = await web_tree_sitter_1.Language.load(wasmPath);
18
+ const parser = new web_tree_sitter_1.Parser();
19
+ parser.setLanguage(language);
20
+ return { parser, language };
21
+ })();
22
+ }
23
+ return parserReady;
24
+ }
25
+ async function parsePythonFile(file, filePathSet, projectImportId) {
26
+ const content = file.content ?? "";
27
+ if (!content.trim())
28
+ return { ...types_js_1.EMPTY_SEMANTICS };
29
+ let parser;
30
+ let language;
31
+ try {
32
+ ({ parser, language } = await getPythonParser());
33
+ }
34
+ catch {
35
+ return { ...types_js_1.EMPTY_SEMANTICS };
36
+ }
37
+ const tree = parser.parse(content);
38
+ if (!tree)
39
+ return { ...types_js_1.EMPTY_SEMANTICS };
40
+ const semantics = {
41
+ symbols: [],
42
+ imports: [],
43
+ exports: [],
44
+ relationships: [],
45
+ calls: [],
46
+ issues: [],
47
+ externalSymbols: [],
48
+ };
49
+ // Extract class and function/method definitions
50
+ const symbolQuery = new web_tree_sitter_1.Query(language, `
51
+ (class_definition name: (identifier) @name) @class
52
+ (function_definition name: (identifier) @name) @function
53
+ `);
54
+ for (const match of symbolQuery.matches(tree.rootNode)) {
55
+ const defCapture = match.captures.find((c) => c.name === "class" || c.name === "function");
56
+ const nameCapture = match.captures.find((c) => c.name === "name");
57
+ if (!defCapture || !nameCapture)
58
+ continue;
59
+ const isClass = defCapture.name === "class";
60
+ const displayName = nameCapture.node.text;
61
+ const startPos = defCapture.node.startPosition;
62
+ const endPos = defCapture.node.endPosition;
63
+ const line = startPos.row + 1;
64
+ const col = startPos.column;
65
+ const parentType = defCapture.node.parent?.type;
66
+ const grandparentType = defCapture.node.parent?.parent?.type;
67
+ const isMethod = !isClass && parentType === "block" && grandparentType === "class_definition";
68
+ const kind = isClass ? "class" : isMethod ? "method" : "function";
69
+ semantics.symbols.push({
70
+ localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, kind, displayName),
71
+ stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, kind, displayName, line),
72
+ displayName,
73
+ kind,
74
+ language: file.language,
75
+ signature: content.split("\n")[startPos.row]?.trim() ?? null,
76
+ returnType: null,
77
+ doc: null,
78
+ isExported: true,
79
+ isDefaultExport: false,
80
+ line,
81
+ col,
82
+ endLine: endPos.row + 1,
83
+ endCol: endPos.column,
84
+ });
85
+ }
86
+ // Extract imports: `import X` and `from X import Y`
87
+ const importQuery = new web_tree_sitter_1.Query(language, `
88
+ (import_statement) @stmt
89
+ (import_from_statement) @stmt
90
+ `);
91
+ const seenImportKeys = new Set();
92
+ for (const match of importQuery.matches(tree.rootNode)) {
93
+ const stmtCapture = match.captures.find((c) => c.name === "stmt");
94
+ if (!stmtCapture)
95
+ continue;
96
+ const stmtNode = stmtCapture.node;
97
+ const startPos = stmtNode.startPosition;
98
+ const endPos = stmtNode.endPosition;
99
+ const line = startPos.row + 1;
100
+ const col = startPos.column;
101
+ if (stmtNode.type === "import_statement") {
102
+ for (let i = 0; i < stmtNode.childCount; i++) {
103
+ const child = stmtNode.child(i);
104
+ if (!child || (child.type !== "dotted_name" && child.type !== "aliased_import"))
105
+ continue;
106
+ const moduleNode = child.type === "aliased_import" ? child.child(0) : child;
107
+ if (!moduleNode)
108
+ continue;
109
+ const moduleSpecifier = moduleNode.text;
110
+ const localKey = (0, shared_js_1.buildImportLocalKey)(file.path, "import", moduleSpecifier, line, col);
111
+ if (seenImportKeys.has(localKey))
112
+ continue;
113
+ seenImportKeys.add(localKey);
114
+ semantics.imports.push({
115
+ localKey,
116
+ moduleSpecifier,
117
+ importKind: "import",
118
+ isTypeOnly: false,
119
+ importedNames: [],
120
+ line,
121
+ col,
122
+ endLine: endPos.row + 1,
123
+ endCol: endPos.column,
124
+ resolutionKind: "package",
125
+ targetPathText: null,
126
+ targetExternalSymbolKey: `python:${moduleSpecifier}`,
127
+ });
128
+ semantics.externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, moduleSpecifier));
129
+ }
130
+ }
131
+ else if (stmtNode.type === "import_from_statement") {
132
+ const moduleNameNode = stmtNode.childForFieldName("module_name");
133
+ if (!moduleNameNode)
134
+ continue;
135
+ const rawSpecifier = moduleNameNode.text;
136
+ const leadingDots = rawSpecifier.match(/^(\.+)/)?.[1] ?? "";
137
+ const isRelative = leadingDots.length > 0;
138
+ const importedNames = [];
139
+ for (let i = 0; i < stmtNode.childCount; i++) {
140
+ const child = stmtNode.child(i);
141
+ if (!child)
142
+ continue;
143
+ if (child.type === "import_from_names" || child.type === "wildcard_import") {
144
+ for (let j = 0; j < child.childCount; j++) {
145
+ const nameNode = child.child(j);
146
+ if (nameNode?.type === "identifier")
147
+ importedNames.push(nameNode.text);
148
+ }
149
+ }
150
+ }
151
+ let targetPathText = null;
152
+ let resolutionKind = "package";
153
+ if (isRelative) {
154
+ const levels = leadingDots.length;
155
+ const cleanSpecifier = rawSpecifier.replace(/^\.+/, "");
156
+ let baseDir = node_path_1.default.posix.dirname(file.path);
157
+ for (let i = 1; i < levels; i++)
158
+ baseDir = node_path_1.default.posix.dirname(baseDir);
159
+ const candidate = cleanSpecifier
160
+ ? `${baseDir}/${cleanSpecifier.replace(/\./g, "/")}`.replace(/\/+/g, "/")
161
+ : baseDir;
162
+ const candidates = [`${candidate}.py`, `${candidate}/__init__.py`];
163
+ const resolved = candidates.find((c) => filePathSet.has(c));
164
+ targetPathText = resolved ?? `${candidate}.py`;
165
+ resolutionKind = resolved ? "relative_path" : "unresolved";
166
+ }
167
+ const localKey = (0, shared_js_1.buildImportLocalKey)(file.path, "import", rawSpecifier, line, col);
168
+ if (seenImportKeys.has(localKey))
169
+ continue;
170
+ seenImportKeys.add(localKey);
171
+ semantics.imports.push({
172
+ localKey,
173
+ moduleSpecifier: rawSpecifier,
174
+ importKind: "import",
175
+ isTypeOnly: false,
176
+ importedNames,
177
+ line,
178
+ col,
179
+ endLine: endPos.row + 1,
180
+ endCol: endPos.column,
181
+ resolutionKind,
182
+ targetPathText,
183
+ targetExternalSymbolKey: isRelative ? null : `python:${rawSpecifier}`,
184
+ });
185
+ if (!isRelative) {
186
+ semantics.externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, rawSpecifier));
187
+ }
188
+ else if (resolutionKind === "unresolved") {
189
+ semantics.issues.push({
190
+ projectImportId,
191
+ severity: "warning",
192
+ code: "UNRESOLVED_IMPORT",
193
+ message: `Unable to resolve import "${rawSpecifier}" from ${file.path}`,
194
+ detailJson: { filePath: file.path, moduleSpecifier: rawSpecifier },
195
+ });
196
+ }
197
+ }
198
+ }
199
+ return semantics;
200
+ }