pi-read-map 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.
@@ -0,0 +1,138 @@
1
+ import { exec } from "node:child_process";
2
+ import { stat } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { promisify } from "node:util";
6
+
7
+ import type { FileMap, FileSymbol } from "../types.js";
8
+
9
+ import { DetailLevel, SymbolKind } from "../enums.js";
10
+
11
+ const execAsync = promisify(exec);
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const SCRIPT_PATH = join(__dirname, "../../scripts/python_outline.py");
15
+
16
+ interface PythonSymbol {
17
+ name: string;
18
+ kind: string;
19
+ startLine: number;
20
+ endLine: number;
21
+ signature?: string;
22
+ modifiers?: string[];
23
+ children?: PythonSymbol[];
24
+ }
25
+
26
+ interface PythonOutlineResult {
27
+ imports?: string[];
28
+ symbols: PythonSymbol[];
29
+ error?: string;
30
+ }
31
+
32
+ function mapKind(kind: string): SymbolKind {
33
+ switch (kind) {
34
+ case "class": {
35
+ return SymbolKind.Class;
36
+ }
37
+ case "function": {
38
+ return SymbolKind.Function;
39
+ }
40
+ case "method": {
41
+ return SymbolKind.Method;
42
+ }
43
+ case "constant": {
44
+ return SymbolKind.Constant;
45
+ }
46
+ case "variable": {
47
+ return SymbolKind.Variable;
48
+ }
49
+ default: {
50
+ return SymbolKind.Unknown;
51
+ }
52
+ }
53
+ }
54
+
55
+ function convertSymbol(ps: PythonSymbol): FileSymbol {
56
+ const symbol: FileSymbol = {
57
+ name: ps.name,
58
+ kind: mapKind(ps.kind),
59
+ startLine: ps.startLine,
60
+ endLine: ps.endLine,
61
+ };
62
+
63
+ if (ps.signature) {
64
+ symbol.signature = ps.signature;
65
+ }
66
+
67
+ if (ps.modifiers && ps.modifiers.length > 0) {
68
+ symbol.modifiers = ps.modifiers;
69
+ }
70
+
71
+ if (ps.children && ps.children.length > 0) {
72
+ symbol.children = ps.children.map(convertSymbol);
73
+ }
74
+
75
+ return symbol;
76
+ }
77
+
78
+ /**
79
+ * Generate a file map for a Python file using AST parsing.
80
+ */
81
+ export async function pythonMapper(
82
+ filePath: string,
83
+ signal?: AbortSignal
84
+ ): Promise<FileMap | null> {
85
+ try {
86
+ // Get file stats
87
+ const stats = await stat(filePath);
88
+ const totalBytes = stats.size;
89
+
90
+ // Count lines
91
+ const { stdout: wcOutput } = await execAsync(`wc -l < "${filePath}"`, {
92
+ signal,
93
+ });
94
+ const totalLines = Number.parseInt(wcOutput.trim(), 10) || 0;
95
+
96
+ // Run Python script
97
+ const { stdout, stderr } = await execAsync(
98
+ `python3 "${SCRIPT_PATH}" "${filePath}"`,
99
+ {
100
+ signal,
101
+ timeout: 10_000,
102
+ }
103
+ );
104
+
105
+ if (stderr && !stdout) {
106
+ console.error(`Python mapper stderr: ${stderr}`);
107
+ return null;
108
+ }
109
+
110
+ const result: PythonOutlineResult = JSON.parse(stdout);
111
+
112
+ if (result.error) {
113
+ console.error(`Python mapper error: ${result.error}`);
114
+ return null;
115
+ }
116
+
117
+ const fileMap: FileMap = {
118
+ path: filePath,
119
+ totalLines,
120
+ totalBytes,
121
+ language: "Python",
122
+ symbols: result.symbols.map(convertSymbol),
123
+ detailLevel: DetailLevel.Full,
124
+ };
125
+
126
+ if (result.imports && result.imports.length > 0) {
127
+ fileMap.imports = result.imports;
128
+ }
129
+
130
+ return fileMap;
131
+ } catch (error) {
132
+ if (signal?.aborted) {
133
+ return null;
134
+ }
135
+ console.error(`Python mapper failed: ${error}`);
136
+ return null;
137
+ }
138
+ }