serpentine-parser 0.2.1__tar.gz → 0.3.0__tar.gz

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 (62) hide show
  1. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/PKG-INFO +9 -7
  2. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/README.md +6 -4
  3. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/pyproject.toml +3 -3
  4. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/Cargo.lock +95 -29
  5. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/Cargo.toml +7 -6
  6. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/graph/loaders.rs +18 -2
  7. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/lib.rs +31 -11
  8. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/python/mod.rs +107 -1
  9. serpentine_parser-0.3.0/rust/src/terraform/config.rs +47 -0
  10. serpentine_parser-0.3.0/rust/src/terraform/mod.rs +441 -0
  11. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/cli.py +57 -12
  12. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/config.py +2 -2
  13. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/skills/code-analysis.md +9 -1
  14. serpentine_parser-0.3.0/src/serpentine/static/assets/index-BPIIaXQj.js +219 -0
  15. serpentine_parser-0.3.0/src/serpentine/static/assets/index-DDwdNVWo.css +1 -0
  16. serpentine_parser-0.3.0/src/serpentine/static/assets/layoutWorker-BkzQvq4g.js +24 -0
  17. serpentine_parser-0.3.0/src/serpentine/static/assets/mermaidExport-B2JREVsY.js +3 -0
  18. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/static/index.html +2 -2
  19. serpentine_parser-0.2.1/src/serpentine/static/assets/index-BjdQg9Eg.js +0 -219
  20. serpentine_parser-0.2.1/src/serpentine/static/assets/index-Cwyiw9CD.css +0 -1
  21. serpentine_parser-0.2.1/src/serpentine/static/assets/layoutWorker-Dd3vNlDT.js +0 -24
  22. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/LICENSE +0 -0
  23. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/CLAUDE.md +0 -0
  24. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/events.rs +0 -0
  25. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/graph/builder.rs +0 -0
  26. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/graph/mod.rs +0 -0
  27. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/graph/pdg.rs +0 -0
  28. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/graph/resolvers.rs +0 -0
  29. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/javascript/config.rs +0 -0
  30. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/javascript/mod.rs +0 -0
  31. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/message_bus.rs +0 -0
  32. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/python/config.rs +0 -0
  33. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/rust_lang/config.rs +0 -0
  34. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/rust_lang/mod.rs +0 -0
  35. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/README.md +0 -0
  36. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/code_snippet.rs +0 -0
  37. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/decorators.rs +0 -0
  38. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/definitions.rs +0 -0
  39. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/event_counter.rs +0 -0
  40. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/imports.rs +0 -0
  41. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/mod.rs +0 -0
  42. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/pdg.rs +0 -0
  43. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/raw_bindings.rs +0 -0
  44. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/scope_tree.rs +0 -0
  45. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/rust/src/subscribers/uses.rs +0 -0
  46. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/__init__.py +0 -0
  47. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/_analyzer.pyi +0 -0
  48. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/cache.py +0 -0
  49. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/selector.py +0 -0
  50. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/server/__init__.py +0 -0
  51. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/server/app.py +0 -0
  52. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/server/routes.py +0 -0
  53. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/server/websocket.py +0 -0
  54. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/skills/__init__.py +0 -0
  55. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/skills/agents-md.md +0 -0
  56. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/skills/copilot-instructions.md +0 -0
  57. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/skills/cursor-rules.md +0 -0
  58. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/state.py +0 -0
  59. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/static/assets/elk-worker.min-BdOC9sib.js +0 -0
  60. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/static/assets/svgToPng-CCU1Oj7k.js +0 -0
  61. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/static/logo.svg +0 -0
  62. {serpentine_parser-0.2.1 → serpentine_parser-0.3.0}/src/serpentine/watcher.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: serpentine-parser
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Environment :: Web Environment
@@ -22,8 +22,8 @@ Requires-Dist: starlette>=0.38.0
22
22
  Requires-Dist: uvicorn[standard]>=0.30.0
23
23
  Requires-Dist: websockets>=12.0
24
24
  License-File: LICENSE
25
- Summary: Fast dependency graph analysis and visualization for Python, JavaScript/TypeScript, and Rust projects
26
- Keywords: dependency-graph,visualization,static-analysis,python,javascript,rust,tree-sitter
25
+ Summary: Fast dependency graph analysis and visualization for Python, JavaScript/TypeScript, Rust, and Terraform projects
26
+ Keywords: dependency-graph,visualization,static-analysis,python,javascript,rust,terraform,tree-sitter
27
27
  Author: Christopher Garretty
28
28
  Requires-Python: >=3.10
29
29
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
@@ -41,13 +41,13 @@ See every reference between every named entity in your codebase — functions, c
41
41
 
42
42
  ![Serpentine UI](docs/screenshot.png)
43
43
 
44
- Serpentine builds a **code reference graph** for Python, JavaScript/TypeScript, and Rust projects. A code reference is when one named entity mentions another by name — a function call, a type annotation, an assignment, an inheritance declaration. Serpentine resolves each reference through the language's scoping rules down to the exact definition it points to, then visualizes the result as an interactive graph that updates in real time as you edit.
44
+ Serpentine builds a **code reference graph** for Python, JavaScript/TypeScript, Rust, and Terraform projects. A code reference is when one named entity mentions another by name — a function call, a type annotation, an assignment, an inheritance declaration. Serpentine resolves each reference through the language's scoping rules down to the exact definition it points to, then visualizes the result as an interactive graph that updates in real time as you edit.
45
45
 
46
46
  ---
47
47
 
48
48
  ## Features
49
49
 
50
- - **Multi-language**: Python, JavaScript, TypeScript, and Rust
50
+ - **Multi-language**: Python, JavaScript, TypeScript, Rust, and Terraform
51
51
  - **Full reference resolution**: Traces calls, type annotations, assignments, and inheritance — not just import edges
52
52
  - **Interactive graph**: Expandable nodes, search, pan/zoom, collapsible modules
53
53
  - **Real-time updates**: File watcher pushes changes via WebSocket
@@ -104,7 +104,7 @@ This installs the `serpentine` CLI globally via `uv tool`, which you will need t
104
104
 
105
105
  ## Quick Start
106
106
 
107
- Navigate to any Python, JavaScript/TypeScript, or Rust project and run:
107
+ Navigate to any Python, JavaScript/TypeScript, Rust, or Terraform project and run:
108
108
 
109
109
  ```bash
110
110
  serpentine serve
@@ -153,6 +153,7 @@ Options:
153
153
  --include-standard Include stdlib nodes (default: off)
154
154
  --include-third-party Include third-party nodes (default: off)
155
155
  --state TEXT Filter by change state: modified,added,deleted
156
+ --edge-type TEXT Comma-separated edge types to include: calls,is-a,has-a,references,imports
156
157
  ```
157
158
 
158
159
  ### `serpentine catalog`
@@ -346,7 +347,7 @@ Serpentine looks for `.serpentine.yml` or `serpentine.yml` in the project root.
346
347
  ```yaml
347
348
  analysis:
348
349
  # File extensions to analyze (default: all supported)
349
- extensions: [".py", ".js", ".jsx", ".ts", ".tsx", ".rs"]
350
+ extensions: [".py", ".js", ".jsx", ".ts", ".tsx", ".rs", ".tf"]
350
351
 
351
352
  # Directories to skip
352
353
  exclude_dirs:
@@ -427,6 +428,7 @@ serpentine/
427
428
  │ ├── python/ # Python parser
428
429
  │ ├── javascript/ # JS/TS parser
429
430
  │ ├── rust_lang/ # Rust parser
431
+ │ ├── terraform/ # Terraform/HCL parser
430
432
  │ ├── subscribers/ # Event processors (imports, calls, defs)
431
433
  │ └── graph/ # Graph builder and resolvers
432
434
  ├── src/serpentine/ # Python package
@@ -8,13 +8,13 @@ See every reference between every named entity in your codebase — functions, c
8
8
 
9
9
  ![Serpentine UI](docs/screenshot.png)
10
10
 
11
- Serpentine builds a **code reference graph** for Python, JavaScript/TypeScript, and Rust projects. A code reference is when one named entity mentions another by name — a function call, a type annotation, an assignment, an inheritance declaration. Serpentine resolves each reference through the language's scoping rules down to the exact definition it points to, then visualizes the result as an interactive graph that updates in real time as you edit.
11
+ Serpentine builds a **code reference graph** for Python, JavaScript/TypeScript, Rust, and Terraform projects. A code reference is when one named entity mentions another by name — a function call, a type annotation, an assignment, an inheritance declaration. Serpentine resolves each reference through the language's scoping rules down to the exact definition it points to, then visualizes the result as an interactive graph that updates in real time as you edit.
12
12
 
13
13
  ---
14
14
 
15
15
  ## Features
16
16
 
17
- - **Multi-language**: Python, JavaScript, TypeScript, and Rust
17
+ - **Multi-language**: Python, JavaScript, TypeScript, Rust, and Terraform
18
18
  - **Full reference resolution**: Traces calls, type annotations, assignments, and inheritance — not just import edges
19
19
  - **Interactive graph**: Expandable nodes, search, pan/zoom, collapsible modules
20
20
  - **Real-time updates**: File watcher pushes changes via WebSocket
@@ -71,7 +71,7 @@ This installs the `serpentine` CLI globally via `uv tool`, which you will need t
71
71
 
72
72
  ## Quick Start
73
73
 
74
- Navigate to any Python, JavaScript/TypeScript, or Rust project and run:
74
+ Navigate to any Python, JavaScript/TypeScript, Rust, or Terraform project and run:
75
75
 
76
76
  ```bash
77
77
  serpentine serve
@@ -120,6 +120,7 @@ Options:
120
120
  --include-standard Include stdlib nodes (default: off)
121
121
  --include-third-party Include third-party nodes (default: off)
122
122
  --state TEXT Filter by change state: modified,added,deleted
123
+ --edge-type TEXT Comma-separated edge types to include: calls,is-a,has-a,references,imports
123
124
  ```
124
125
 
125
126
  ### `serpentine catalog`
@@ -313,7 +314,7 @@ Serpentine looks for `.serpentine.yml` or `serpentine.yml` in the project root.
313
314
  ```yaml
314
315
  analysis:
315
316
  # File extensions to analyze (default: all supported)
316
- extensions: [".py", ".js", ".jsx", ".ts", ".tsx", ".rs"]
317
+ extensions: [".py", ".js", ".jsx", ".ts", ".tsx", ".rs", ".tf"]
317
318
 
318
319
  # Directories to skip
319
320
  exclude_dirs:
@@ -394,6 +395,7 @@ serpentine/
394
395
  │ ├── python/ # Python parser
395
396
  │ ├── javascript/ # JS/TS parser
396
397
  │ ├── rust_lang/ # Rust parser
398
+ │ ├── terraform/ # Terraform/HCL parser
397
399
  │ ├── subscribers/ # Event processors (imports, calls, defs)
398
400
  │ └── graph/ # Graph builder and resolvers
399
401
  ├── src/serpentine/ # Python package
@@ -1,14 +1,14 @@
1
1
  [project]
2
2
  name = "serpentine-parser"
3
- version = "0.2.1"
4
- description = "Fast dependency graph analysis and visualization for Python, JavaScript/TypeScript, and Rust projects"
3
+ version = "0.3.0"
4
+ description = "Fast dependency graph analysis and visualization for Python, JavaScript/TypeScript, Rust, and Terraform projects"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
7
7
  license = { file = "LICENSE" }
8
8
  authors = [
9
9
  { name = "Christopher Garretty" },
10
10
  ]
11
- keywords = ["dependency-graph", "visualization", "static-analysis", "python", "javascript", "rust", "tree-sitter"]
11
+ keywords = ["dependency-graph", "visualization", "static-analysis", "python", "javascript", "rust", "terraform", "tree-sitter"]
12
12
  classifiers = [
13
13
  "Development Status :: 4 - Beta",
14
14
  "Environment :: Console",
@@ -13,9 +13,13 @@ dependencies = [
13
13
 
14
14
  [[package]]
15
15
  name = "cc"
16
- version = "1.0.106"
16
+ version = "1.2.63"
17
17
  source = "registry+https://github.com/rust-lang/crates.io-index"
18
- checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2"
18
+ checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
19
+ dependencies = [
20
+ "find-msvc-tools",
21
+ "shlex",
22
+ ]
19
23
 
20
24
  [[package]]
21
25
  name = "crossbeam-deque"
@@ -44,9 +48,27 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
44
48
 
45
49
  [[package]]
46
50
  name = "either"
47
- version = "1.15.0"
51
+ version = "1.16.0"
52
+ source = "registry+https://github.com/rust-lang/crates.io-index"
53
+ checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
54
+
55
+ [[package]]
56
+ name = "equivalent"
57
+ version = "1.0.2"
48
58
  source = "registry+https://github.com/rust-lang/crates.io-index"
49
- checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
59
+ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
60
+
61
+ [[package]]
62
+ name = "find-msvc-tools"
63
+ version = "0.1.9"
64
+ source = "registry+https://github.com/rust-lang/crates.io-index"
65
+ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
66
+
67
+ [[package]]
68
+ name = "hashbrown"
69
+ version = "0.17.1"
70
+ source = "registry+https://github.com/rust-lang/crates.io-index"
71
+ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
50
72
 
51
73
  [[package]]
52
74
  name = "heck"
@@ -54,23 +76,33 @@ version = "0.5.0"
54
76
  source = "registry+https://github.com/rust-lang/crates.io-index"
55
77
  checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
56
78
 
79
+ [[package]]
80
+ name = "indexmap"
81
+ version = "2.14.0"
82
+ source = "registry+https://github.com/rust-lang/crates.io-index"
83
+ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
84
+ dependencies = [
85
+ "equivalent",
86
+ "hashbrown",
87
+ ]
88
+
57
89
  [[package]]
58
90
  name = "itoa"
59
- version = "1.0.17"
91
+ version = "1.0.18"
60
92
  source = "registry+https://github.com/rust-lang/crates.io-index"
61
- checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
93
+ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
62
94
 
63
95
  [[package]]
64
96
  name = "libc"
65
- version = "0.2.183"
97
+ version = "0.2.186"
66
98
  source = "registry+https://github.com/rust-lang/crates.io-index"
67
- checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
99
+ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
68
100
 
69
101
  [[package]]
70
102
  name = "memchr"
71
- version = "2.8.0"
103
+ version = "2.8.1"
72
104
  source = "registry+https://github.com/rust-lang/crates.io-index"
73
- checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
105
+ checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
74
106
 
75
107
  [[package]]
76
108
  name = "once_cell"
@@ -162,9 +194,9 @@ dependencies = [
162
194
 
163
195
  [[package]]
164
196
  name = "rayon"
165
- version = "1.11.0"
197
+ version = "1.12.0"
166
198
  source = "registry+https://github.com/rust-lang/crates.io-index"
167
- checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
199
+ checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
168
200
  dependencies = [
169
201
  "either",
170
202
  "rayon-core",
@@ -241,10 +273,11 @@ dependencies = [
241
273
 
242
274
  [[package]]
243
275
  name = "serde_json"
244
- version = "1.0.149"
276
+ version = "1.0.150"
245
277
  source = "registry+https://github.com/rust-lang/crates.io-index"
246
- checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
278
+ checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
247
279
  dependencies = [
280
+ "indexmap",
248
281
  "itoa",
249
282
  "memchr",
250
283
  "serde",
@@ -254,19 +287,32 @@ dependencies = [
254
287
 
255
288
  [[package]]
256
289
  name = "serpentine"
257
- version = "0.2.1"
290
+ version = "0.3.0"
258
291
  dependencies = [
259
292
  "pyo3",
260
293
  "rayon",
261
294
  "serde",
262
295
  "serde_json",
263
296
  "tree-sitter",
297
+ "tree-sitter-hcl",
264
298
  "tree-sitter-javascript",
265
299
  "tree-sitter-python",
266
300
  "tree-sitter-rust",
267
301
  "tree-sitter-typescript",
268
302
  ]
269
303
 
304
+ [[package]]
305
+ name = "shlex"
306
+ version = "2.0.1"
307
+ source = "registry+https://github.com/rust-lang/crates.io-index"
308
+ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
309
+
310
+ [[package]]
311
+ name = "streaming-iterator"
312
+ version = "0.1.9"
313
+ source = "registry+https://github.com/rust-lang/crates.io-index"
314
+ checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
315
+
270
316
  [[package]]
271
317
  name = "syn"
272
318
  version = "2.0.117"
@@ -286,52 +332,72 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
286
332
 
287
333
  [[package]]
288
334
  name = "tree-sitter"
289
- version = "0.20.10"
335
+ version = "0.25.10"
290
336
  source = "registry+https://github.com/rust-lang/crates.io-index"
291
- checksum = "e747b1f9b7b931ed39a548c1fae149101497de3c1fc8d9e18c62c1a66c683d3d"
337
+ checksum = "78f873475d258561b06f1c595d93308a7ed124d9977cb26b148c2084a4a3cc87"
292
338
  dependencies = [
293
339
  "cc",
294
340
  "regex",
341
+ "regex-syntax",
342
+ "serde_json",
343
+ "streaming-iterator",
344
+ "tree-sitter-language",
345
+ ]
346
+
347
+ [[package]]
348
+ name = "tree-sitter-hcl"
349
+ version = "1.1.0"
350
+ source = "registry+https://github.com/rust-lang/crates.io-index"
351
+ checksum = "5a7b2cc3d7121553b84309fab9d11b3ff3d420403eef9ae50f9fd1cd9d9cf012"
352
+ dependencies = [
353
+ "cc",
354
+ "tree-sitter-language",
295
355
  ]
296
356
 
297
357
  [[package]]
298
358
  name = "tree-sitter-javascript"
299
- version = "0.20.4"
359
+ version = "0.25.0"
300
360
  source = "registry+https://github.com/rust-lang/crates.io-index"
301
- checksum = "d015c02ea98b62c806f7329ff71c383286dfc3a7a7da0cc484f6e42922f73c2c"
361
+ checksum = "68204f2abc0627a90bdf06e605f5c470aa26fdcb2081ea553a04bdad756693f5"
302
362
  dependencies = [
303
363
  "cc",
304
- "tree-sitter",
364
+ "tree-sitter-language",
305
365
  ]
306
366
 
367
+ [[package]]
368
+ name = "tree-sitter-language"
369
+ version = "0.1.7"
370
+ source = "registry+https://github.com/rust-lang/crates.io-index"
371
+ checksum = "009994f150cc0cd50ff54917d5bc8bffe8cad10ca10d81c34da2ec421ae61782"
372
+
307
373
  [[package]]
308
374
  name = "tree-sitter-python"
309
- version = "0.20.4"
375
+ version = "0.25.0"
310
376
  source = "registry+https://github.com/rust-lang/crates.io-index"
311
- checksum = "e6c93b1b1fbd0d399db3445f51fd3058e43d0b4dcff62ddbdb46e66550978aa5"
377
+ checksum = "6bf85fd39652e740bf60f46f4cda9492c3a9ad75880575bf14960f775cb74a1c"
312
378
  dependencies = [
313
379
  "cc",
314
- "tree-sitter",
380
+ "tree-sitter-language",
315
381
  ]
316
382
 
317
383
  [[package]]
318
384
  name = "tree-sitter-rust"
319
- version = "0.20.4"
385
+ version = "0.24.2"
320
386
  source = "registry+https://github.com/rust-lang/crates.io-index"
321
- checksum = "b0832309b0b2b6d33760ce5c0e818cb47e1d72b468516bfe4134408926fa7594"
387
+ checksum = "439e577dbe07423ec2582ac62c7531120dbfccfa6e5f92406f93dd271a120e45"
322
388
  dependencies = [
323
389
  "cc",
324
- "tree-sitter",
390
+ "tree-sitter-language",
325
391
  ]
326
392
 
327
393
  [[package]]
328
394
  name = "tree-sitter-typescript"
329
- version = "0.20.5"
395
+ version = "0.23.2"
330
396
  source = "registry+https://github.com/rust-lang/crates.io-index"
331
- checksum = "c8bc1d2c24276a48ef097a71b56888ac9db63717e8f8d0b324668a27fd619670"
397
+ checksum = "6c5f76ed8d947a75cc446d5fccd8b602ebf0cde64ccf2ffa434d873d7a575eff"
332
398
  dependencies = [
333
399
  "cc",
334
- "tree-sitter",
400
+ "tree-sitter-language",
335
401
  ]
336
402
 
337
403
  [[package]]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "serpentine"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  edition = "2021"
5
5
  license = "Apache-2.0"
6
6
 
@@ -9,11 +9,12 @@ name = "serpentine"
9
9
  crate-type = ["cdylib", "rlib"]
10
10
 
11
11
  [dependencies]
12
- tree-sitter = "0.20"
13
- tree-sitter-python = { version = "0.20", default-features = false }
14
- tree-sitter-javascript = { version = "0.20", default-features = false }
15
- tree-sitter-typescript = { version = "0.20", default-features = false }
16
- tree-sitter-rust = { version = "0.20", default-features = false }
12
+ tree-sitter = "0.25"
13
+ tree-sitter-python = { version = "0.25", default-features = false }
14
+ tree-sitter-javascript = { version = "0.25", default-features = false }
15
+ tree-sitter-typescript = { version = "0.23", default-features = false }
16
+ tree-sitter-rust = { version = "0.24", default-features = false }
17
+ tree-sitter-hcl = "1.1.0"
17
18
  pyo3 = { version = "0.28", features = ["extension-module", "abi3-py310"] }
18
19
  rayon = "1.10"
19
20
  serde = { version = "1.0", features = ["derive"] }
@@ -478,6 +478,18 @@ impl GraphBuilder {
478
478
 
479
479
  let resolved_module = source_module.to_string();
480
480
 
481
+ // Only emit imports edges for project-local targets. Terraform (.tf) files
482
+ // are exempt: their source paths are already normalized from "./" prefixes
483
+ // by normalize_source_path, so all surviving sources are local-by-intent
484
+ // even when the target file is not in the analyzed set.
485
+ let is_terraform = file.ends_with(".tf");
486
+ if !is_terraform {
487
+ use crate::graph::Origin;
488
+ if self.classify_module(&resolved_module) != Origin::Local {
489
+ continue;
490
+ }
491
+ }
492
+
481
493
  // Determine what to create an edge to
482
494
  if imported_names.is_empty() {
483
495
  // `import foo` or `import foo.bar` - edge to the module itself
@@ -963,7 +975,12 @@ impl GraphBuilder {
963
975
  // For method calls on local variables (`obj.method(arg)`), emit
964
976
  // `obj --references--> arg` for each local-variable argument.
965
977
  // This captures the data-flow dependency: obj "uses" its arguments.
966
- if caller != scope && callee_text.contains('.') {
978
+ // Guard: only fire when the receiver (`caller`) is a local variable in
979
+ // the current scope — i.e. its qualname starts with the scope prefix.
980
+ // Without this, `ClassName.static_method(arg)` from another module
981
+ // falsely emits `ClassName --references--> arg`.
982
+ let scope_prefix = format!("{}.", scope);
983
+ if caller != scope && callee_text.contains('.') && caller.starts_with(&scope_prefix) {
967
984
  let call_args: Vec<String> = target_obj
968
985
  .get("arguments")
969
986
  .and_then(|a| a.as_array())
@@ -973,7 +990,6 @@ impl GraphBuilder {
973
990
  .collect()
974
991
  })
975
992
  .unwrap_or_default();
976
- let scope_prefix = format!("{}.", scope);
977
993
  for arg_text in &call_args {
978
994
  let idents: Vec<String> =
979
995
  if arg_text.chars().all(|c| c.is_alphanumeric() || c == '_') {
@@ -14,6 +14,7 @@ mod message_bus;
14
14
  mod python;
15
15
  mod rust_lang;
16
16
  mod subscribers;
17
+ mod terraform;
17
18
 
18
19
  use crate::javascript::{parse as parse_javascript, JsLang};
19
20
  use crate::javascript::config::JsConfig;
@@ -22,6 +23,8 @@ use crate::graph::GraphBuilder;
22
23
  use crate::python::parse as parse_python;
23
24
  use crate::python::config::PythonConfig;
24
25
  use crate::rust_lang::parse as parse_rust;
26
+ use crate::terraform::parse as parse_terraform;
27
+ use crate::terraform::config::TerraformConfig;
25
28
  use crate::subscribers::{
26
29
  DecoratorsSubscriberFactory, PdgSubscriberFactory, CodeSnippetSubscriberFactory,
27
30
  DefinitionsSubscriberFactory, EventCounterSubscriberFactory, ImportsSubscriberFactory,
@@ -39,10 +42,11 @@ use pyo3::types::{PyDict, PyList};
39
42
 
40
43
  use tree_sitter::{Language, Parser, Tree};
41
44
 
42
- use tree_sitter_javascript::language as javascript_language;
43
- use tree_sitter_python::language as python_language;
44
- use tree_sitter_rust::language as rust_language;
45
- use tree_sitter_typescript::{language_tsx, language_typescript};
45
+ use tree_sitter_javascript::LANGUAGE as JAVASCRIPT_LANGUAGE;
46
+ use tree_sitter_python::LANGUAGE as PYTHON_LANGUAGE;
47
+ use tree_sitter_rust::LANGUAGE as RUST_LANGUAGE;
48
+ use tree_sitter_typescript::{LANGUAGE_TSX, LANGUAGE_TYPESCRIPT};
49
+
46
50
 
47
51
  // ============================================================================
48
52
  // Language Support
@@ -60,6 +64,8 @@ enum Lang {
60
64
  Tsx,
61
65
  /// Rust (.rs)
62
66
  Rust,
67
+ /// Terraform/HCL (.tf)
68
+ Terraform,
63
69
  }
64
70
 
65
71
  impl Lang {
@@ -71,6 +77,7 @@ impl Lang {
71
77
  Some("ts") => Some(Lang::TypeScript),
72
78
  Some("tsx") => Some(Lang::Tsx),
73
79
  Some("rs") => Some(Lang::Rust),
80
+ Some("tf") => Some(Lang::Terraform),
74
81
  _ => None,
75
82
  }
76
83
  }
@@ -78,11 +85,12 @@ impl Lang {
78
85
  /// Get the tree-sitter language for parsing.
79
86
  fn language(&self) -> Language {
80
87
  match self {
81
- Lang::Python => python_language(),
82
- Lang::JavaScript => javascript_language(),
83
- Lang::TypeScript => language_typescript(),
84
- Lang::Tsx => language_tsx(),
85
- Lang::Rust => rust_language(),
88
+ Lang::Python => PYTHON_LANGUAGE.into(),
89
+ Lang::JavaScript => JAVASCRIPT_LANGUAGE.into(),
90
+ Lang::TypeScript => LANGUAGE_TYPESCRIPT.into(),
91
+ Lang::Tsx => LANGUAGE_TSX.into(),
92
+ Lang::Rust => RUST_LANGUAGE.into(),
93
+ Lang::Terraform => tree_sitter_hcl::LANGUAGE.into(),
86
94
  }
87
95
  }
88
96
 
@@ -92,7 +100,7 @@ impl Lang {
92
100
  Lang::JavaScript => Some(JsLang::JavaScript),
93
101
  Lang::TypeScript => Some(JsLang::TypeScript),
94
102
  Lang::Tsx => Some(JsLang::Tsx),
95
- Lang::Python | Lang::Rust => None,
103
+ Lang::Python | Lang::Rust | Lang::Terraform => None,
96
104
  }
97
105
  }
98
106
  }
@@ -117,7 +125,7 @@ struct FileEntry {
117
125
  impl FileEntry {
118
126
  fn new(lang: Lang, source: String, file_path: String) -> Self {
119
127
  let mut parser = Parser::new();
120
- parser.set_language(lang.language()).unwrap();
128
+ parser.set_language(&lang.language()).unwrap();
121
129
  let source_hash = Self::compute_hash(&source);
122
130
  let tree = parser.parse(&source, None);
123
131
 
@@ -177,6 +185,7 @@ impl FileEntry {
177
185
  parse_javascript(&self.source, &self.tree, &self.file_path, js_lang)
178
186
  }
179
187
  Lang::Rust => parse_rust(&self.source, &self.tree, &self.file_path),
188
+ Lang::Terraform => parse_terraform(&self.source, &self.tree, &self.file_path),
180
189
  };
181
190
 
182
191
  self.cached_results = self.message_bus.publish_events(events)?;
@@ -287,6 +296,7 @@ impl FileManager {
287
296
  Box::new(PythonConfig::new()),
288
297
  Box::new(JsConfig::new()),
289
298
  Box::new(crate::rust_lang::config::RustConfig::new()),
299
+ Box::new(TerraformConfig::new()),
290
300
  ];
291
301
 
292
302
  // Collect all subscriber data from all files (already cached from parsing)
@@ -323,6 +333,16 @@ impl FileManager {
323
333
  for data in all_definitions {
324
334
  builder.load_definitions(&data);
325
335
  }
336
+
337
+ // Populate local_prefixes from the set of analyzed file paths so that
338
+ // classify_module can distinguish project-local modules from third-party.
339
+ for path in self.files.keys() {
340
+ let module_path = builder.file_to_module(&path.to_string_lossy());
341
+ let top = module_path.split('.').next().unwrap_or(&module_path).to_string();
342
+ if !top.is_empty() {
343
+ builder.local_prefixes.insert(top);
344
+ }
345
+ }
326
346
  for data in all_pdgs {
327
347
  builder.load_pdgs(&data);
328
348
  }