entrygraph 0.1.7__tar.gz → 0.1.8__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 (172) hide show
  1. {entrygraph-0.1.7 → entrygraph-0.1.8}/PKG-INFO +1 -1
  2. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/_version.py +2 -2
  3. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/javascript.py +65 -0
  4. entrygraph-0.1.8/tests/fixtures/javascript/commonjs_app/server.js +15 -0
  5. entrygraph-0.1.8/tests/test_commonjs.py +41 -0
  6. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_extract_javascript.py +26 -0
  7. {entrygraph-0.1.7 → entrygraph-0.1.8}/.github/workflows/ci.yml +0 -0
  8. {entrygraph-0.1.7 → entrygraph-0.1.8}/.github/workflows/release.yml +0 -0
  9. {entrygraph-0.1.7 → entrygraph-0.1.8}/.gitignore +0 -0
  10. {entrygraph-0.1.7 → entrygraph-0.1.8}/LICENSE +0 -0
  11. {entrygraph-0.1.7 → entrygraph-0.1.8}/README.md +0 -0
  12. {entrygraph-0.1.7 → entrygraph-0.1.8}/RELEASING.md +0 -0
  13. {entrygraph-0.1.7 → entrygraph-0.1.8}/pyproject.toml +0 -0
  14. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/__init__.py +0 -0
  15. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/__main__.py +0 -0
  16. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/api.py +0 -0
  17. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/cli/__init__.py +0 -0
  18. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/cli/main.py +0 -0
  19. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/cli/render.py +0 -0
  20. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/data/sinks/csharp.toml +0 -0
  21. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/data/sinks/go.toml +0 -0
  22. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/data/sinks/java.toml +0 -0
  23. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/data/sinks/javascript.toml +0 -0
  24. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/data/sinks/lib_javascript.toml +0 -0
  25. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/data/sinks/lib_python.toml +0 -0
  26. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/data/sinks/php.toml +0 -0
  27. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/data/sinks/python.toml +0 -0
  28. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/data/sinks/ruby.toml +0 -0
  29. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/data/sinks/rust.toml +0 -0
  30. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/db/__init__.py +0 -0
  31. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/db/engine.py +0 -0
  32. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/db/meta.py +0 -0
  33. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/db/models.py +0 -0
  34. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/db/queries.py +0 -0
  35. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/__init__.py +0 -0
  36. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/__init__.py +0 -0
  37. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/base.py +0 -0
  38. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/configs.py +0 -0
  39. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/csharp.py +0 -0
  40. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/golang.py +0 -0
  41. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/java.py +0 -0
  42. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/javascript.py +0 -0
  43. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/php.py +0 -0
  44. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/python.py +0 -0
  45. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/ruby.py +0 -0
  46. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/entrypoints/rust.py +0 -0
  47. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/frameworks.py +0 -0
  48. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/manifests.py +0 -0
  49. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/detect/taint.py +0 -0
  50. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/errors.py +0 -0
  51. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/__init__.py +0 -0
  52. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/base.py +0 -0
  53. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/csharp.py +0 -0
  54. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/golang.py +0 -0
  55. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/ir.py +0 -0
  56. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/java.py +0 -0
  57. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/php.py +0 -0
  58. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/python.py +0 -0
  59. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/registry.py +0 -0
  60. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/ruby.py +0 -0
  61. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/extract/rust.py +0 -0
  62. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/fs/__init__.py +0 -0
  63. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/fs/hashing.py +0 -0
  64. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/fs/lang.py +0 -0
  65. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/fs/walker.py +0 -0
  66. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/graph/__init__.py +0 -0
  67. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/graph/adjacency.py +0 -0
  68. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/graph/cte.py +0 -0
  69. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/graph/scoring.py +0 -0
  70. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/kinds.py +0 -0
  71. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/parsing/__init__.py +0 -0
  72. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/parsing/parsers.py +0 -0
  73. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/parsing/queries.py +0 -0
  74. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/pipeline/__init__.py +0 -0
  75. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/pipeline/scanner.py +0 -0
  76. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/pipeline/worker.py +0 -0
  77. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/pipeline/writer.py +0 -0
  78. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/py.typed +0 -0
  79. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/csharp/calls.scm +0 -0
  80. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/csharp/definitions.scm +0 -0
  81. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/csharp/imports.scm +0 -0
  82. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/go/calls.scm +0 -0
  83. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/go/definitions.scm +0 -0
  84. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/go/imports.scm +0 -0
  85. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/java/calls.scm +0 -0
  86. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/java/definitions.scm +0 -0
  87. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/java/imports.scm +0 -0
  88. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/javascript/calls.scm +0 -0
  89. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/javascript/definitions.scm +0 -0
  90. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/javascript/imports.scm +0 -0
  91. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/php/calls.scm +0 -0
  92. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/php/definitions.scm +0 -0
  93. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/php/imports.scm +0 -0
  94. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/python/calls.scm +0 -0
  95. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/python/definitions.scm +0 -0
  96. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/python/imports.scm +0 -0
  97. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/ruby/calls.scm +0 -0
  98. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/ruby/definitions.scm +0 -0
  99. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/ruby/imports.scm +0 -0
  100. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/rust/calls.scm +0 -0
  101. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/rust/definitions.scm +0 -0
  102. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/queries/rust/imports.scm +0 -0
  103. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/resolve/__init__.py +0 -0
  104. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/resolve/externals.py +0 -0
  105. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/resolve/hierarchy.py +0 -0
  106. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/resolve/resolver.py +0 -0
  107. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/resolve/symbol_table.py +0 -0
  108. {entrygraph-0.1.7 → entrygraph-0.1.8}/src/entrygraph/results.py +0 -0
  109. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/conftest.py +0 -0
  110. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/csharp/aspnet_app/Controllers/ReportsController.cs +0 -0
  111. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/csharp/aspnet_app/Program.cs +0 -0
  112. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/csharp/aspnet_app/Services/ReportService.cs +0 -0
  113. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/csharp/aspnet_app/app.csproj +0 -0
  114. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/go/gin_app/go.mod +0 -0
  115. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/go/gin_app/main.go +0 -0
  116. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/go/gin_app/service.go +0 -0
  117. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/java/spring_app/pom.xml +0 -0
  118. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/java/spring_app/src/main/java/com/example/Application.java +0 -0
  119. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportRunner.java +0 -0
  120. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportService.java +0 -0
  121. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/java/spring_app/src/main/java/com/example/UserController.java +0 -0
  122. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/javascript/express_app/package.json +0 -0
  123. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/javascript/express_app/src/routes.js +0 -0
  124. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/javascript/express_app/src/services.js +0 -0
  125. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/php/laravel_app/app/Http/Controllers/ReportController.php +0 -0
  126. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/php/laravel_app/artisan +0 -0
  127. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/php/laravel_app/composer.json +0 -0
  128. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/php/laravel_app/routes/web.php +0 -0
  129. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/python/flask_app/app/__init__.py +0 -0
  130. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/python/flask_app/app/db.py +0 -0
  131. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/python/flask_app/app/routes.py +0 -0
  132. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/python/flask_app/app/services.py +0 -0
  133. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/python/flask_app/cli.py +0 -0
  134. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/python/flask_app/requirements.txt +0 -0
  135. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/python/sanitizer/app.py +0 -0
  136. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/python/taint_source/handler.py +0 -0
  137. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/ruby/sinatra_app/Gemfile +0 -0
  138. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/ruby/sinatra_app/app.rb +0 -0
  139. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/ruby/sinatra_app/services/runner.rb +0 -0
  140. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/rust/axum_app/Cargo.toml +0 -0
  141. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/rust/axum_app/src/handlers.rs +0 -0
  142. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/fixtures/rust/axum_app/src/main.rs +0 -0
  143. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_api.py +0 -0
  144. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_cli.py +0 -0
  145. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_engine_pragmas.py +0 -0
  146. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_entrypoint_expansion.py +0 -0
  147. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_entrypoints.py +0 -0
  148. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_extract_csharp.py +0 -0
  149. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_extract_go.py +0 -0
  150. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_extract_java.py +0 -0
  151. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_extract_php.py +0 -0
  152. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_extract_python.py +0 -0
  153. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_extract_ruby.py +0 -0
  154. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_extract_rust.py +0 -0
  155. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_frameworks.py +0 -0
  156. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_hardening.py +0 -0
  157. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_incremental.py +0 -0
  158. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_indexer.py +0 -0
  159. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_lang.py +0 -0
  160. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_manifests.py +0 -0
  161. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_models.py +0 -0
  162. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_pool.py +0 -0
  163. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_reachability.py +0 -0
  164. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_render.py +0 -0
  165. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_resolver.py +0 -0
  166. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_scoring.py +0 -0
  167. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_sink_catalog.py +0 -0
  168. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_taint.py +0 -0
  169. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_taint_sanitizers.py +0 -0
  170. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_taint_sources.py +0 -0
  171. {entrygraph-0.1.7 → entrygraph-0.1.8}/tests/test_walker.py +0 -0
  172. {entrygraph-0.1.7 → entrygraph-0.1.8}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: entrygraph
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: Language-agnostic code graph: query symbols, entrypoints, and source-to-sink call paths from a SQLite index
5
5
  Project-URL: Repository, https://github.com/brettbergin/entrygraph
6
6
  Author-email: Brett Bergin <brettberginbc@yahoo.com>
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.1.7'
22
- __version_tuple__ = version_tuple = (0, 1, 7)
21
+ __version__ = version = '0.1.8'
22
+ __version_tuple__ = version_tuple = (0, 1, 8)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -238,12 +238,77 @@ class JavaScriptExtractor:
238
238
  )
239
239
  for node in caps.get("export.from", []):
240
240
  self._reexport(node, ctx, out)
241
+ for node in caps.get("require", []):
242
+ self._commonjs_require(node, ctx, out)
241
243
 
242
244
  for imp in out.imports:
243
245
  out.framework_signals.append(
244
246
  ("import", imp.module.split(".")[0] if "." in imp.module else imp.module)
245
247
  )
246
248
 
249
+ def _commonjs_require(self, call, ctx, out) -> None:
250
+ """CommonJS `require('m')` bindings -> RawImport, mirroring ESM handling.
251
+
252
+ ``const cp = require('m')`` -> whole module bound to ``cp``
253
+ ``const { x, y: z } = require('m')`` -> named imports ``x`` and ``y`` (as ``z``)
254
+ bare / chained / reassigned -> module-only (side-effect) import,
255
+ which still yields the framework signal even when no alias can be bound.
256
+ """
257
+ args = call.child_by_field_name("arguments")
258
+ spec = None
259
+ if args is not None:
260
+ for child in args.named_children:
261
+ if child.type == "string":
262
+ spec = node_text(child).strip("'\"`")
263
+ break
264
+ if not spec:
265
+ return # dynamic require (variable/template arg) — target unknowable
266
+ module = self._resolve_module(spec, ctx)
267
+ span = span_of(call)
268
+
269
+ parent = call.parent
270
+ name_node = None
271
+ if parent is not None and parent.type == "variable_declarator":
272
+ name_node = parent.child_by_field_name("name")
273
+ elif parent is not None and parent.type == "assignment_expression":
274
+ name_node = parent.child_by_field_name("left")
275
+
276
+ if name_node is not None and name_node.type == "identifier":
277
+ # Bind the alias to the module itself (same as an ESM default import),
278
+ # so `const cp = require('child_process'); cp.exec()` canonicalizes to
279
+ # child_process.exec.
280
+ out.imports.append(
281
+ RawImport(module=module, imported_name=None, alias=node_text(name_node), span=span)
282
+ )
283
+ return
284
+ if name_node is not None and name_node.type == "object_pattern":
285
+ for child in name_node.named_children:
286
+ if child.type == "shorthand_property_identifier_pattern": # { exec }
287
+ nm = node_text(child)
288
+ out.imports.append(
289
+ RawImport(module=module, imported_name=nm, alias=nm, span=span)
290
+ )
291
+ elif child.type == "pair_pattern": # { exec: run }
292
+ key = child.child_by_field_name("key")
293
+ value = child.child_by_field_name("value")
294
+ if key is None:
295
+ continue
296
+ alias = (
297
+ node_text(value)
298
+ if value is not None and value.type == "identifier"
299
+ else node_text(key)
300
+ )
301
+ out.imports.append(
302
+ RawImport(
303
+ module=module, imported_name=node_text(key), alias=alias, span=span
304
+ )
305
+ )
306
+ return
307
+ # bare `require('m')`, `require('m').foo`, `module.exports = require('m')`, etc.
308
+ out.imports.append(
309
+ RawImport(module=module, imported_name=None, alias=module.split(".")[-1], span=span)
310
+ )
311
+
247
312
  def _reexport(self, node, ctx, out) -> None:
248
313
  """`export { X as Y } from "./m"` / `export * from "./m"` -> RawReexport."""
249
314
  source = node.child_by_field_name("source")
@@ -0,0 +1,15 @@
1
+ const cp = require('child_process');
2
+ const { exec } = require('child_process');
3
+ const express = require('express');
4
+
5
+ function runDefault(input) {
6
+ cp.execSync(input);
7
+ }
8
+
9
+ function runNamed(input) {
10
+ exec(input);
11
+ }
12
+
13
+ const app = express();
14
+ app.get('/a', (req, res) => runDefault(req.query.cmd));
15
+ app.get('/b', (req, res) => runNamed(req.query.cmd));
@@ -0,0 +1,41 @@
1
+ """End-to-end CommonJS `require()` support (Phase 1.3).
2
+
3
+ Regression: the JS extractor dropped the `require()` captures entirely, so a
4
+ pure-CommonJS app produced no imports — no framework detection and no
5
+ canonicalization of `cp.execSync` to a `child_process` sink.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+
12
+ import pytest
13
+
14
+ from entrygraph import CodeGraph
15
+
16
+ COMMONJS_APP = Path(__file__).parent / "fixtures" / "javascript" / "commonjs_app"
17
+
18
+
19
+ @pytest.fixture(scope="module")
20
+ def graph(tmp_path_factory) -> CodeGraph:
21
+ db = tmp_path_factory.mktemp("db") / "graph.db"
22
+ g = CodeGraph.index(COMMONJS_APP, db=db)
23
+ yield g
24
+ g.close()
25
+
26
+
27
+ def test_commonjs_require_enables_framework_detection(graph):
28
+ names = {f.name for f in graph.detect().frameworks}
29
+ assert "express" in names
30
+
31
+
32
+ def test_commonjs_require_canonicalizes_command_sinks(graph):
33
+ # both the default-bound (cp.execSync) and destructured (exec) forms must
34
+ # resolve to child_process command_exec sinks
35
+ assert graph.stats().sink_edges >= 2
36
+ sinks = {
37
+ row["dst_qname"]
38
+ for row in graph.sql("SELECT dst_qname FROM edges WHERE sink_id IS NOT NULL")
39
+ }
40
+ assert "js:child_process.execSync" in sinks
41
+ assert "js:child_process.exec" in sinks
@@ -72,6 +72,32 @@ def test_relative_import_resolution():
72
72
  assert any(i.module == "db.conn" for i in x.imports)
73
73
 
74
74
 
75
+ def test_commonjs_require_bindings():
76
+ x = extract(
77
+ "const cp = require('child_process');\n"
78
+ "const { exec, spawn: launch } = require('child_process');\n"
79
+ "const express = require('express');\n"
80
+ "require('./side-effect');\n"
81
+ "const rel = require('../db/conn');\n",
82
+ path="src/routes/users.js",
83
+ )
84
+ imports = {(i.module, i.imported_name, i.alias) for i in x.imports}
85
+ assert ("child_process", None, "cp") in imports # default -> module binding
86
+ assert ("child_process", "exec", "exec") in imports # destructured
87
+ assert ("child_process", "spawn", "launch") in imports # destructured + rename
88
+ assert ("express", None, "express") in imports
89
+ assert ("db.conn", None, "rel") in imports # relative resolved to dotted, bound to `rel`
90
+ # side-effect require still registers the module (framework signal source)
91
+ assert any(m == "routes.side-effect" and n is None for m, n, _ in imports)
92
+
93
+
94
+ def test_commonjs_require_emits_framework_signal():
95
+ # regression: CommonJS `require('express')` used to yield no import at all,
96
+ # so express was undetectable in pure-CJS apps.
97
+ x = extract("const express = require('express');\n")
98
+ assert ("import", "express") in x.framework_signals
99
+
100
+
75
101
  def test_calls_with_receivers():
76
102
  x = extract(
77
103
  "import cp from 'child_process';\nfunction run() { cp.execSync('ls'); helper(); }\n"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes