Extract functions, classes, methods, and using statements from a .ps1 file.
(path: Path)
| 9132 | # ── PowerShell ──────────────────────────────────────────────────────────────── |
| 9133 | |
| 9134 | def extract_powershell(path: Path) -> dict: |
| 9135 | """Extract functions, classes, methods, and using statements from a .ps1 file.""" |
| 9136 | try: |
| 9137 | import tree_sitter_powershell as tsps |
| 9138 | from tree_sitter import Language, Parser |
| 9139 | except ImportError: |
| 9140 | return {"nodes": [], "edges": [], "error": "tree_sitter_powershell not installed"} |
| 9141 | |
| 9142 | try: |
| 9143 | language = Language(tsps.language()) |
| 9144 | parser = Parser(language) |
| 9145 | source = path.read_bytes() |
| 9146 | tree = parser.parse(source) |
| 9147 | root = tree.root_node |
| 9148 | except Exception as e: |
| 9149 | return {"nodes": [], "edges": [], "error": str(e)} |
| 9150 | |
| 9151 | stem = _file_stem(path) |
| 9152 | str_path = str(path) |
| 9153 | nodes: list[dict] = [] |
| 9154 | edges: list[dict] = [] |
| 9155 | seen_ids: set[str] = set() |
| 9156 | function_bodies: list[tuple[str, Any]] = [] |
| 9157 | |
| 9158 | def add_node(nid: str, label: str, line: int) -> None: |
| 9159 | if nid not in seen_ids: |
| 9160 | seen_ids.add(nid) |
| 9161 | nodes.append({"id": nid, "label": label, "file_type": "code", |
| 9162 | "source_file": str_path, "source_location": f"L{line}"}) |
| 9163 | |
| 9164 | def add_edge(src: str, tgt: str, relation: str, line: int, |
| 9165 | confidence: str = "EXTRACTED", weight: float = 1.0, |
| 9166 | context: str | None = None) -> None: |
| 9167 | edge = {"source": src, "target": tgt, "relation": relation, |
| 9168 | "confidence": confidence, "source_file": str_path, |
| 9169 | "source_location": f"L{line}", "weight": weight} |
| 9170 | if context: |
| 9171 | edge["context"] = context |
| 9172 | edges.append(edge) |
| 9173 | |
| 9174 | file_nid = _make_id(str(path)) |
| 9175 | add_node(file_nid, path.name, 1) |
| 9176 | |
| 9177 | _PS_SKIP = frozenset({ |
| 9178 | "using", "return", "if", "else", "elseif", "foreach", "for", |
| 9179 | "while", "do", "switch", "try", "catch", "finally", "throw", |
| 9180 | "break", "continue", "exit", "param", "begin", "process", "end", |
| 9181 | # Import commands — handled as import edges, not function calls |
| 9182 | "import-module", |
| 9183 | }) |
| 9184 | |
| 9185 | def _find_script_block_body(node): |
| 9186 | for child in node.children: |
| 9187 | if child.type == "script_block": |
| 9188 | for sc in child.children: |
| 9189 | if sc.type == "script_block_body": |
| 9190 | return sc |
| 9191 | return child |