srcly 0.1.16__tar.gz → 0.1.17__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 (313) hide show
  1. {srcly-0.1.16 → srcly-0.1.17}/PKG-INFO +2 -1
  2. {srcly-0.1.16 → srcly-0.1.17}/app/models.py +4 -0
  3. {srcly-0.1.16 → srcly-0.1.17}/app/routers/analysis.py +3 -2
  4. {srcly-0.1.16 → srcly-0.1.17}/app/routers/files.py +4 -0
  5. {srcly-0.1.16 → srcly-0.1.17}/app/services/analysis.py +29 -4
  6. srcly-0.1.17/app/services/analysis_types.py +61 -0
  7. {srcly-0.1.16/app/services → srcly-0.1.17/app/services/css}/css_analysis.py +1 -1
  8. {srcly-0.1.16 → srcly-0.1.17}/app/services/data_flow_analysis.py +1 -1
  9. {srcly-0.1.16/app/services → srcly-0.1.17/app/services/ipynb}/ipynb_analysis.py +41 -1
  10. {srcly-0.1.16/app/services → srcly-0.1.17/app/services/markdown}/markdown_analysis.py +1 -1
  11. srcly-0.1.17/app/services/python/python_analysis.py +249 -0
  12. srcly-0.1.16/app/services/tree_sitter_analysis.py → srcly-0.1.17/app/services/typescript/typescript_analysis.py +1 -57
  13. srcly-0.1.17/app/static/assets/index-BuiLB-f3.js +37 -0
  14. srcly-0.1.17/app/static/assets/index-lgAeJI5G.css +1 -0
  15. {srcly-0.1.16 → srcly-0.1.17}/app/static/index.html +2 -2
  16. {srcly-0.1.16 → srcly-0.1.17}/pyproject.toml +2 -1
  17. srcly-0.1.16/app/static/assets/index-CX6VqYPz.js +0 -37
  18. srcly-0.1.16/app/static/assets/index-DXVBnNom.css +0 -1
  19. {srcly-0.1.16 → srcly-0.1.17}/.gitignore +0 -0
  20. {srcly-0.1.16 → srcly-0.1.17}/README.md +0 -0
  21. {srcly-0.1.16 → srcly-0.1.17}/app/.gitignore +0 -0
  22. {srcly-0.1.16 → srcly-0.1.17}/app/__init__.py +0 -0
  23. {srcly-0.1.16 → srcly-0.1.17}/app/config.py +0 -0
  24. {srcly-0.1.16 → srcly-0.1.17}/app/main.py +0 -0
  25. {srcly-0.1.16 → srcly-0.1.17}/app/run.py +0 -0
  26. {srcly-0.1.16 → srcly-0.1.17}/app/services/cache.py +0 -0
  27. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/abap-BdImnpbu.js +0 -0
  28. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/actionscript-3-CfeIJUat.js +0 -0
  29. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/ada-bCR0ucgS.js +0 -0
  30. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/andromeeda-C-Jbm3Hp.js +0 -0
  31. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/angular-html-CU67Zn6k.js +0 -0
  32. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/angular-ts-BwZT4LLn.js +0 -0
  33. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/apache-Pmp26Uib.js +0 -0
  34. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/apex-DhZLUxFE.js +0 -0
  35. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/apl-dKokRX4l.js +0 -0
  36. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/applescript-Co6uUVPk.js +0 -0
  37. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/ara-BRHolxvo.js +0 -0
  38. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/asciidoc-Dv7Oe6Be.js +0 -0
  39. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/asm-D_Q5rh1f.js +0 -0
  40. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/astro-CbQHKStN.js +0 -0
  41. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/aurora-x-D-2ljcwZ.js +0 -0
  42. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/awk-DMzUqQB5.js +0 -0
  43. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/ayu-dark-Cv9koXgw.js +0 -0
  44. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/ballerina-BFfxhgS-.js +0 -0
  45. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/bat-BkioyH1T.js +0 -0
  46. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/beancount-k_qm7-4y.js +0 -0
  47. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/berry-D08WgyRC.js +0 -0
  48. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/bibtex-CHM0blh-.js +0 -0
  49. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/bicep-Bmn6On1c.js +0 -0
  50. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/blade-DVc8C-J4.js +0 -0
  51. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/bsl-BO_Y6i37.js +0 -0
  52. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/c-BIGW1oBm.js +0 -0
  53. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/cadence-Bv_4Rxtq.js +0 -0
  54. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/cairo-KRGpt6FW.js +0 -0
  55. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/catppuccin-frappe-DFWUc33u.js +0 -0
  56. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/catppuccin-latte-C9dUb6Cb.js +0 -0
  57. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/catppuccin-macchiato-DQyhUUbL.js +0 -0
  58. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/catppuccin-mocha-D87Tk5Gz.js +0 -0
  59. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/clarity-D53aC0YG.js +0 -0
  60. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/clojure-P80f7IUj.js +0 -0
  61. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/cmake-D1j8_8rp.js +0 -0
  62. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/cobol-nwyudZeR.js +0 -0
  63. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/codeowners-Bp6g37R7.js +0 -0
  64. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/codeql-DsOJ9woJ.js +0 -0
  65. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/coffee-Ch7k5sss.js +0 -0
  66. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/common-lisp-Cg-RD9OK.js +0 -0
  67. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/coq-DkFqJrB1.js +0 -0
  68. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/cpp-CofmeUqb.js +0 -0
  69. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/crystal-tKQVLTB8.js +0 -0
  70. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/csharp-CX12Zw3r.js +0 -0
  71. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/css-DPfMkruS.js +0 -0
  72. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/csv-fuZLfV_i.js +0 -0
  73. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/cue-D82EKSYY.js +0 -0
  74. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/cypher-COkxafJQ.js +0 -0
  75. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/d-85-TOEBH.js +0 -0
  76. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/dark-plus-eOWES_5F.js +0 -0
  77. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/dart-CF10PKvl.js +0 -0
  78. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/dax-CEL-wOlO.js +0 -0
  79. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/desktop-BmXAJ9_W.js +0 -0
  80. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/diff-D97Zzqfu.js +0 -0
  81. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/docker-BcOcwvcX.js +0 -0
  82. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/dotenv-Da5cRb03.js +0 -0
  83. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/dracula-BzJJZx-M.js +0 -0
  84. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/dracula-soft-BXkSAIEj.js +0 -0
  85. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/dream-maker-BtqSS_iP.js +0 -0
  86. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/edge-BkV0erSs.js +0 -0
  87. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/elixir-CDX3lj18.js +0 -0
  88. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/elm-DbKCFpqz.js +0 -0
  89. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/emacs-lisp-C9XAeP06.js +0 -0
  90. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/erb-BOJIQeun.js +0 -0
  91. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/erlang-DsQrWhSR.js +0 -0
  92. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/everforest-dark-BgDCqdQA.js +0 -0
  93. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/everforest-light-C8M2exoo.js +0 -0
  94. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/fennel-BYunw83y.js +0 -0
  95. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/fish-BvzEVeQv.js +0 -0
  96. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/fluent-C4IJs8-o.js +0 -0
  97. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/fortran-fixed-form-BZjJHVRy.js +0 -0
  98. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/fortran-free-form-D22FLkUw.js +0 -0
  99. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/fsharp-CXgrBDvD.js +0 -0
  100. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gdresource-B7Tvp0Sc.js +0 -0
  101. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gdscript-DTMYz4Jt.js +0 -0
  102. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gdshader-DkwncUOv.js +0 -0
  103. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/genie-D0YGMca9.js +0 -0
  104. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gherkin-DyxjwDmM.js +0 -0
  105. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/git-commit-F4YmCXRG.js +0 -0
  106. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/git-rebase-r7XF79zn.js +0 -0
  107. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/github-dark-DHJKELXO.js +0 -0
  108. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/github-dark-default-Cuk6v7N8.js +0 -0
  109. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/github-dark-dimmed-DH5Ifo-i.js +0 -0
  110. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/github-dark-high-contrast-E3gJ1_iC.js +0 -0
  111. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/github-light-DAi9KRSo.js +0 -0
  112. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/github-light-default-D7oLnXFd.js +0 -0
  113. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/github-light-high-contrast-BfjtVDDH.js +0 -0
  114. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gleam-BspZqrRM.js +0 -0
  115. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/glimmer-js-Rg0-pVw9.js +0 -0
  116. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/glimmer-ts-U6CK756n.js +0 -0
  117. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/glsl-DplSGwfg.js +0 -0
  118. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gnuplot-DdkO51Og.js +0 -0
  119. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/go-Dn2_MT6a.js +0 -0
  120. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/graphql-ChdNCCLP.js +0 -0
  121. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/groovy-gcz8RCvz.js +0 -0
  122. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gruvbox-dark-hard-CFHQjOhq.js +0 -0
  123. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gruvbox-dark-medium-GsRaNv29.js +0 -0
  124. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gruvbox-dark-soft-CVdnzihN.js +0 -0
  125. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gruvbox-light-hard-CH1njM8p.js +0 -0
  126. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gruvbox-light-medium-DRw_LuNl.js +0 -0
  127. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/gruvbox-light-soft-hJgmCMqR.js +0 -0
  128. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/hack-CaT9iCJl.js +0 -0
  129. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/haml-B8DHNrY2.js +0 -0
  130. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/handlebars-BL8al0AC.js +0 -0
  131. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/haskell-Df6bDoY_.js +0 -0
  132. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/haxe-CzTSHFRz.js +0 -0
  133. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/hcl-BWvSN4gD.js +0 -0
  134. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/hjson-D5-asLiD.js +0 -0
  135. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/hlsl-D3lLCCz7.js +0 -0
  136. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/houston-DnULxvSX.js +0 -0
  137. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/html-GMplVEZG.js +0 -0
  138. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/html-derivative-BFtXZ54Q.js +0 -0
  139. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/http-jrhK8wxY.js +0 -0
  140. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/hurl-irOxFIW8.js +0 -0
  141. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/hxml-Bvhsp5Yf.js +0 -0
  142. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/hy-DFXneXwc.js +0 -0
  143. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/imba-DGztddWO.js +0 -0
  144. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/ini-BEwlwnbL.js +0 -0
  145. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/java-CylS5w8V.js +0 -0
  146. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/javascript-wDzz0qaB.js +0 -0
  147. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/jinja-4LBKfQ-Z.js +0 -0
  148. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/jison-wvAkD_A8.js +0 -0
  149. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/json-Cp-IABpG.js +0 -0
  150. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/json5-C9tS-k6U.js +0 -0
  151. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/jsonc-Des-eS-w.js +0 -0
  152. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/jsonl-DcaNXYhu.js +0 -0
  153. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/jsonnet-DFQXde-d.js +0 -0
  154. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/jssm-C2t-YnRu.js +0 -0
  155. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/jsx-g9-lgVsj.js +0 -0
  156. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/julia-C8NyazO9.js +0 -0
  157. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/kanagawa-dragon-CkXjmgJE.js +0 -0
  158. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/kanagawa-lotus-CfQXZHmo.js +0 -0
  159. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/kanagawa-wave-DWedfzmr.js +0 -0
  160. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/kdl-DV7GczEv.js +0 -0
  161. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/kotlin-BdnUsdx6.js +0 -0
  162. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/kusto-BvAqAH-y.js +0 -0
  163. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/laserwave-DUszq2jm.js +0 -0
  164. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/latex-BUKiar2Z.js +0 -0
  165. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/lean-DP1Csr6i.js +0 -0
  166. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/less-B1dDrJ26.js +0 -0
  167. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/light-plus-B7mTdjB0.js +0 -0
  168. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/liquid-DYVedYrR.js +0 -0
  169. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/llvm-BtvRca6l.js +0 -0
  170. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/log-2UxHyX5q.js +0 -0
  171. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/logo-BtOb2qkB.js +0 -0
  172. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/lua-BbnMAYS6.js +0 -0
  173. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/luau-CXu1NL6O.js +0 -0
  174. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/make-CHLpvVh8.js +0 -0
  175. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/markdown-Cvjx9yec.js +0 -0
  176. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/marko-CPi9NSCl.js +0 -0
  177. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/material-theme-D5KoaKCx.js +0 -0
  178. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/material-theme-darker-BfHTSMKl.js +0 -0
  179. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/material-theme-lighter-B0m2ddpp.js +0 -0
  180. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/material-theme-ocean-CyktbL80.js +0 -0
  181. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/material-theme-palenight-Csfq5Kiy.js +0 -0
  182. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/matlab-D7o27uSR.js +0 -0
  183. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/mdc-DUICxH0z.js +0 -0
  184. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/mdx-Cmh6b_Ma.js +0 -0
  185. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/mermaid-DKYwYmdq.js +0 -0
  186. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/min-dark-CafNBF8u.js +0 -0
  187. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/min-light-CTRr51gU.js +0 -0
  188. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/mipsasm-CKIfxQSi.js +0 -0
  189. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/mojo-1DNp92w6.js +0 -0
  190. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/monokai-D4h5O-jR.js +0 -0
  191. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/move-Bu9oaDYs.js +0 -0
  192. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/narrat-DRg8JJMk.js +0 -0
  193. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/nextflow-CUEJCptM.js +0 -0
  194. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/nginx-DknmC5AR.js +0 -0
  195. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/night-owl-C39BiMTA.js +0 -0
  196. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/nim-CVrawwO9.js +0 -0
  197. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/nix-BbRYJGeE.js +0 -0
  198. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/nord-Ddv68eIx.js +0 -0
  199. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/nushell-C-sUppwS.js +0 -0
  200. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/objective-c-DXmwc3jG.js +0 -0
  201. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/objective-cpp-CLxacb5B.js +0 -0
  202. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/ocaml-C0hk2d4L.js +0 -0
  203. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/one-dark-pro-DVMEJ2y_.js +0 -0
  204. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/one-light-PoHY5YXO.js +0 -0
  205. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/pascal-D93ZcfNL.js +0 -0
  206. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/perl-C0TMdlhV.js +0 -0
  207. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/php-CDn_0X-4.js +0 -0
  208. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/pkl-u5AG7uiY.js +0 -0
  209. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/plastic-3e1v2bzS.js +0 -0
  210. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/plsql-ChMvpjG-.js +0 -0
  211. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/po-BTJTHyun.js +0 -0
  212. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/poimandres-CS3Unz2-.js +0 -0
  213. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/polar-C0HS_06l.js +0 -0
  214. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/postcss-CXtECtnM.js +0 -0
  215. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/powerquery-CEu0bR-o.js +0 -0
  216. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/powershell-Dpen1YoG.js +0 -0
  217. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/prisma-Dd19v3D-.js +0 -0
  218. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/prolog-CbFg5uaA.js +0 -0
  219. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/proto-DyJlTyXw.js +0 -0
  220. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/pug-CGlum2m_.js +0 -0
  221. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/puppet-BMWR74SV.js +0 -0
  222. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/purescript-CklMAg4u.js +0 -0
  223. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/python-B6aJPvgy.js +0 -0
  224. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/qml-3beO22l8.js +0 -0
  225. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/qmldir-C8lEn-DE.js +0 -0
  226. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/qss-IeuSbFQv.js +0 -0
  227. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/r-DiinP2Uv.js +0 -0
  228. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/racket-BqYA7rlc.js +0 -0
  229. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/raku-DXvB9xmW.js +0 -0
  230. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/razor-WgofotgN.js +0 -0
  231. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/red-bN70gL4F.js +0 -0
  232. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/reg-C-SQnVFl.js +0 -0
  233. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/regexp-CDVJQ6XC.js +0 -0
  234. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/rel-C3B-1QV4.js +0 -0
  235. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/riscv-BM1_JUlF.js +0 -0
  236. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/rose-pine-BHrmToEH.js +0 -0
  237. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/rose-pine-dawn-CnK8MTSM.js +0 -0
  238. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/rose-pine-moon-NleAzG8P.js +0 -0
  239. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/rosmsg-BJDFO7_C.js +0 -0
  240. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/rst-B0xPkSld.js +0 -0
  241. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/ruby-BvKwtOVI.js +0 -0
  242. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/rust-B1yitclQ.js +0 -0
  243. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/sas-cz2c8ADy.js +0 -0
  244. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/sass-Cj5Yp3dK.js +0 -0
  245. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/scala-C151Ov-r.js +0 -0
  246. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/scheme-C98Dy4si.js +0 -0
  247. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/scss-OYdSNvt2.js +0 -0
  248. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/sdbl-DVxCFoDh.js +0 -0
  249. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/shaderlab-Dg9Lc6iA.js +0 -0
  250. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/shellscript-Yzrsuije.js +0 -0
  251. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/shellsession-BADoaaVG.js +0 -0
  252. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/slack-dark-BthQWCQV.js +0 -0
  253. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/slack-ochin-DqwNpetd.js +0 -0
  254. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/smalltalk-BERRCDM3.js +0 -0
  255. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/snazzy-light-Bw305WKR.js +0 -0
  256. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/solarized-dark-DXbdFlpD.js +0 -0
  257. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/solarized-light-L9t79GZl.js +0 -0
  258. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/solidity-BbcW6ACK.js +0 -0
  259. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/soy-Brmx7dQM.js +0 -0
  260. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/sparql-rVzFXLq3.js +0 -0
  261. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/splunk-BtCnVYZw.js +0 -0
  262. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/sql-BLtJtn59.js +0 -0
  263. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/ssh-config-_ykCGR6B.js +0 -0
  264. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/stata-BH5u7GGu.js +0 -0
  265. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/stylus-BEDo0Tqx.js +0 -0
  266. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/svelte-3Dk4HxPD.js +0 -0
  267. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/swift-Dg5xB15N.js +0 -0
  268. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/synthwave-84-CbfX1IO0.js +0 -0
  269. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/system-verilog-CnnmHF94.js +0 -0
  270. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/systemd-4A_iFExJ.js +0 -0
  271. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/talonscript-CkByrt1z.js +0 -0
  272. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/tasl-QIJgUcNo.js +0 -0
  273. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/tcl-dwOrl1Do.js +0 -0
  274. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/templ-W15q3VgB.js +0 -0
  275. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/terraform-BETggiCN.js +0 -0
  276. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/tex-Cppo0RY3.js +0 -0
  277. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/tokyo-night-hegEt444.js +0 -0
  278. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/toml-vGWfd6FD.js +0 -0
  279. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/ts-tags-zn1MmPIZ.js +0 -0
  280. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/tsv-B_m7g4N7.js +0 -0
  281. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/tsx-COt5Ahok.js +0 -0
  282. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/turtle-BsS91CYL.js +0 -0
  283. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/twig-CO9l9SDP.js +0 -0
  284. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/typescript-BPQ3VLAy.js +0 -0
  285. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/typespec-Df68jz8_.js +0 -0
  286. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/typst-DHCkPAjA.js +0 -0
  287. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/v-BcVCzyr7.js +0 -0
  288. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vala-CsfeWuGM.js +0 -0
  289. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vb-D17OF-Vu.js +0 -0
  290. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/verilog-BQ8w6xss.js +0 -0
  291. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vesper-DU1UobuO.js +0 -0
  292. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vhdl-CeAyd5Ju.js +0 -0
  293. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/viml-CJc9bBzg.js +0 -0
  294. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vitesse-black-Bkuqu6BP.js +0 -0
  295. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vitesse-dark-D0r3Knsf.js +0 -0
  296. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vitesse-light-CVO1_9PV.js +0 -0
  297. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vue-CCoi5OLL.js +0 -0
  298. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vue-html-DAAvJJDi.js +0 -0
  299. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vue-vine-_Ih-lPRR.js +0 -0
  300. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/vyper-CDx5xZoG.js +0 -0
  301. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/wasm-CG6Dc4jp.js +0 -0
  302. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/wasm-MzD3tlZU.js +0 -0
  303. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/wenyan-BV7otONQ.js +0 -0
  304. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/wgsl-Dx-B1_4e.js +0 -0
  305. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/wikitext-BhOHFoWU.js +0 -0
  306. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/wit-5i3qLPDT.js +0 -0
  307. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/wolfram-lXgVvXCa.js +0 -0
  308. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/xml-sdJ4AIDG.js +0 -0
  309. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/xsl-CtQFsRM5.js +0 -0
  310. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/yaml-Buea-lGh.js +0 -0
  311. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/zenscript-DVFEvuxE.js +0 -0
  312. {srcly-0.1.16 → srcly-0.1.17}/app/static/assets/zig-VOosw3JB.js +0 -0
  313. {srcly-0.1.16 → srcly-0.1.17}/app/static/vite.svg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: srcly
3
- Version: 0.1.16
3
+ Version: 0.1.17
4
4
  Summary: Interactive codebase treemap and metrics viewer.
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: fastapi>=0.121.3
@@ -11,6 +11,7 @@ Requires-Dist: pydantic>=2.12.4
11
11
  Requires-Dist: pytest>=9.0.1
12
12
  Requires-Dist: tree-sitter-css==0.25.0
13
13
  Requires-Dist: tree-sitter-markdown<0.6.0,>=0.5.1
14
+ Requires-Dist: tree-sitter-python==0.25.0
14
15
  Requires-Dist: tree-sitter-scss==1.0.0
15
16
  Requires-Dist: tree-sitter-typescript>=0.23.2
16
17
  Requires-Dist: tree-sitter>=0.25.2
@@ -34,6 +34,10 @@ class Metrics(BaseModel):
34
34
  tsx_duplicated_string_count: int = 0
35
35
  ts_type_interface_count: int = 0
36
36
  ts_export_count: int = 0
37
+
38
+ # Python-specific metrics
39
+ python_import_count: int = 0
40
+
37
41
  # Markdown-specific metrics
38
42
  md_data_url_count: int = 0
39
43
 
@@ -5,7 +5,8 @@ import os
5
5
  from typing import Dict, List, Optional, Tuple
6
6
 
7
7
  from app.models import Node, DependencyGraph, DependencyNode, DependencyEdge
8
- from app.services import analysis, cache, tree_sitter_analysis
8
+ from app.services import analysis, cache
9
+ from app.services.typescript import typescript_analysis
9
10
  from app.config import IGNORE_DIRS
10
11
 
11
12
  router = APIRouter(prefix="/api/analysis", tags=["analysis"])
@@ -333,7 +334,7 @@ async def get_dependencies(path: str = None):
333
334
  if not target_path.exists():
334
335
  raise HTTPException(status_code=404, detail="Path not found")
335
336
 
336
- analyzer = tree_sitter_analysis.TreeSitterAnalyzer()
337
+ analyzer = typescript_analysis.TreeSitterAnalyzer()
337
338
  nodes = []
338
339
  edges = []
339
340
 
@@ -2,6 +2,8 @@ from fastapi import APIRouter, HTTPException, Query
2
2
  from pathlib import Path
3
3
  from fastapi.responses import PlainTextResponse
4
4
 
5
+ from app.services.analysis import get_ipynb_analyzer
6
+
5
7
  router = APIRouter(prefix="/api/files", tags=["files"])
6
8
 
7
9
  @router.get("/content", response_class=PlainTextResponse)
@@ -21,6 +23,8 @@ async def get_file_content(path: str = Query(..., description="Absolute path to
21
23
  # For this local tool, we'll allow reading any file as requested, but maybe warn?
22
24
 
23
25
  try:
26
+ if file_path.suffix == ".ipynb":
27
+ return get_ipynb_analyzer().get_virtual_content(str(file_path))
24
28
  return file_path.read_text(encoding="utf-8")
25
29
  except Exception as e:
26
30
  raise HTTPException(status_code=500, detail=f"Failed to read file: {str(e)}")
@@ -5,10 +5,10 @@ from pathlib import Path
5
5
 
6
6
  from app.models import Node, Metrics
7
7
  from app.config import IGNORE_DIRS, IGNORE_FILES, IGNORE_EXTENSIONS
8
- from app.services.tree_sitter_analysis import TreeSitterAnalyzer
9
- from app.services.markdown_analysis import MarkdownTreeSitterAnalyzer
10
- from app.services.ipynb_analysis import NotebookAnalyzer
11
- from app.services.css_analysis import CssTreeSitterAnalyzer
8
+ from app.services.typescript.typescript_analysis import TreeSitterAnalyzer
9
+ from app.services.markdown.markdown_analysis import MarkdownTreeSitterAnalyzer
10
+ from app.services.ipynb.ipynb_analysis import NotebookAnalyzer
11
+ from app.services.css.css_analysis import CssTreeSitterAnalyzer
12
12
  from pathspec import PathSpec
13
13
 
14
14
  # Maximum time allowed for analyzing a single file in a worker process.
@@ -47,6 +47,14 @@ def get_css_analyzer():
47
47
  _css_analyzer = CssTreeSitterAnalyzer()
48
48
  return _css_analyzer
49
49
 
50
+ _python_analyzer = None
51
+ def get_python_analyzer():
52
+ global _python_analyzer
53
+ if _python_analyzer is None:
54
+ from app.services.python.python_analysis import PythonTreeSitterAnalyzer
55
+ _python_analyzer = PythonTreeSitterAnalyzer()
56
+ return _python_analyzer
57
+
50
58
  def find_repo_root(start_path: Path) -> Path:
51
59
  current = start_path.resolve()
52
60
  for parent in [current, *current.parents]:
@@ -99,6 +107,9 @@ def attach_file_metrics(node: Node, file_info) -> None:
99
107
  node.metrics.todo_count = file_info.todo_count
100
108
  node.metrics.classes_count = file_info.classes_count
101
109
 
110
+ if hasattr(file_info, 'python_import_count'):
111
+ node.metrics.python_import_count = file_info.python_import_count
112
+
102
113
  # Set start/end line for the file (approximate, 1 to total lines)
103
114
  # We don't strictly have this from lizard always, but we can infer or leave 0.
104
115
  # For now, let's leave file start/end as 0 unless we want to read the file.
@@ -368,6 +379,12 @@ def aggregate_metrics(node: Node) -> Metrics:
368
379
  total_type_interface_count += child_metrics.ts_type_interface_count
369
380
  total_export_count += child_metrics.ts_export_count
370
381
  total_md_data_url_count += child_metrics.md_data_url_count
382
+ if hasattr(child_metrics, 'python_import_count'):
383
+ # Metric model has it, but child_metrics is a Metrics object so it should have it
384
+ total_python_import_count = getattr(child_metrics, 'python_import_count', 0)
385
+ # Should we add a local var for this? Yes
386
+ pass
387
+
371
388
 
372
389
  # Reconstruct total function loc from average * count
373
390
  total_function_loc += (child_metrics.average_function_length * child_metrics.function_count)
@@ -393,8 +410,10 @@ def aggregate_metrics(node: Node) -> Metrics:
393
410
  node.metrics.classes_count = total_classes_count
394
411
  node.metrics.average_function_length = total_function_loc / total_funcs if total_funcs > 0 else 0.0
395
412
  node.metrics.ts_type_interface_count = total_type_interface_count
413
+ node.metrics.ts_type_interface_count = total_type_interface_count
396
414
  node.metrics.ts_export_count = total_export_count
397
415
  node.metrics.md_data_url_count = total_md_data_url_count
416
+ node.metrics.python_import_count = total_python_import_count
398
417
 
399
418
  return node.metrics
400
419
 
@@ -416,6 +435,12 @@ def analyze_single_file(file_path: str):
416
435
  if file_path.endswith(".ipynb"):
417
436
  analyzer = get_ipynb_analyzer()
418
437
  return analyzer.analyze_file(file_path)
438
+ if file_path.endswith(".py"):
439
+ # We can cache it similarly if we want, or just instantiate.
440
+ # For consistency let's add a getter or just instantiate for now to avoid circular imports / global clutter
441
+ # actually lets follow pattern
442
+ return get_python_analyzer().analyze_file(file_path)
443
+
419
444
  return lizard.analyze_file(file_path)
420
445
  except Exception as e:
421
446
  # Return error info instead of crashing
@@ -0,0 +1,61 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import List
3
+
4
+ @dataclass
5
+ class FunctionMetrics:
6
+ name: str
7
+ cyclomatic_complexity: int
8
+ nloc: int
9
+ start_line: int
10
+ end_line: int
11
+ parameter_count: int = 0
12
+ max_nesting_depth: int = 0
13
+ comment_lines: int = 0
14
+ todo_count: int = 0
15
+ # TS/TSX Specific Metrics (per-function)
16
+ ts_type_interface_count: int = 0
17
+ # Markdown-specific metrics
18
+ md_data_url_count: int = 0
19
+ # TSX structure helpers
20
+ contains_tsx: bool = False
21
+ tsx_start_line: int = 0
22
+ tsx_end_line: int = 0
23
+ tsx_root_name: str = ""
24
+ tsx_root_is_fragment: bool = False
25
+ origin_type: str = ""
26
+ is_jsx_container: bool = False
27
+ children: List["FunctionMetrics"] = field(default_factory=list)
28
+ # We store children to represent nested functions.
29
+
30
+ @dataclass
31
+ class FileMetrics:
32
+ nloc: int
33
+ average_cyclomatic_complexity: float
34
+ function_list: List[FunctionMetrics] = field(default_factory=list)
35
+ filename: str = ""
36
+ comment_lines: int = 0
37
+ comment_density: float = 0.0
38
+ max_nesting_depth: int = 0
39
+ average_function_length: float = 0.0
40
+ parameter_count: int = 0
41
+ todo_count: int = 0
42
+ classes_count: int = 0
43
+
44
+ # TS/TSX Specific Metrics
45
+ tsx_nesting_depth: int = 0
46
+ tsx_render_branching_count: int = 0
47
+ tsx_react_use_effect_count: int = 0
48
+ tsx_anonymous_handler_count: int = 0
49
+ tsx_prop_count: int = 0
50
+ ts_any_usage_count: int = 0
51
+ ts_ignore_count: int = 0
52
+ ts_import_coupling_count: int = 0
53
+ tsx_hardcoded_string_volume: int = 0
54
+ tsx_duplicated_string_count: int = 0
55
+ ts_type_interface_count: int = 0
56
+ ts_type_interface_count: int = 0
57
+ ts_export_count: int = 0
58
+ # Python-specific metrics
59
+ python_import_count: int = 0
60
+ # Markdown-specific metrics (per-file)
61
+ md_data_url_count: int = 0
@@ -5,7 +5,7 @@ import tree_sitter_css as tscss
5
5
  import tree_sitter_scss as tsscss
6
6
  from tree_sitter import Language, Node, Parser
7
7
 
8
- from app.services.tree_sitter_analysis import FileMetrics, FunctionMetrics
8
+ from app.services.analysis_types import FileMetrics, FunctionMetrics
9
9
 
10
10
 
11
11
  CSS_LANGUAGE = Language(tscss.language())
@@ -4,7 +4,7 @@ from dataclasses import dataclass, field
4
4
  from typing import List, Dict, Optional, Any
5
5
  import uuid
6
6
 
7
- from app.services.tree_sitter_analysis import TreeSitterAnalyzer
7
+ from app.services.typescript.typescript_analysis import TreeSitterAnalyzer
8
8
 
9
9
  # Load TypeScript and TSX grammars
10
10
  TYPESCRIPT_LANGUAGE = Language(tstypescript.language_typescript())
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  from typing import List
3
3
 
4
- from app.services.tree_sitter_analysis import FileMetrics, FunctionMetrics
4
+ from app.services.analysis_types import FileMetrics, FunctionMetrics
5
5
 
6
6
 
7
7
  class NotebookAnalyzer:
@@ -86,4 +86,44 @@ class NotebookAnalyzer:
86
86
  # TS/TSX-specific metrics remain at their defaults (all zeros) for notebooks.
87
87
  )
88
88
 
89
+ def _get_non_empty_lines(self, cells: list) -> list[str]:
90
+ """
91
+ Helper to extract all non-empty lines from valid cells, exactly as
92
+ analyze_file does for counting LOC.
93
+ """
94
+ all_lines = []
95
+ for cell in cells:
96
+ source = cell.get("source", "")
97
+ if isinstance(source, list):
98
+ text = "".join(source)
99
+ else:
100
+ text = str(source)
101
+
102
+ lines = text.splitlines()
103
+ non_empty = [ln for ln in lines if ln.strip()]
104
+
105
+ # analyze_file skips cells with 0 LOC entirely
106
+ if not non_empty:
107
+ continue
108
+
109
+ all_lines.extend(non_empty)
110
+
111
+ return all_lines
112
+
113
+ def get_virtual_content(self, file_path: str) -> str:
114
+ """
115
+ Returns the "virtual" file content that corresponds to the
116
+ analysis metrics (line numbers, etc).
117
+
118
+ For notebooks, this is the concatenation of all non-empty
119
+ lines from all cells, joined by newlines.
120
+ """
121
+ with open(file_path, "r", encoding="utf-8") as f:
122
+ data = json.load(f)
123
+
124
+ cells = data.get("cells") or []
125
+ lines = self._get_non_empty_lines(cells)
126
+ return "\n".join(lines)
127
+
128
+
89
129
 
@@ -4,7 +4,7 @@ from typing import List
4
4
  import tree_sitter_markdown as tsmarkdown
5
5
  from tree_sitter import Language, Node, Parser
6
6
 
7
- from app.services.tree_sitter_analysis import FileMetrics, FunctionMetrics
7
+ from app.services.analysis_types import FileMetrics, FunctionMetrics
8
8
 
9
9
 
10
10
  MARKDOWN_LANGUAGE = Language(tsmarkdown.language())
@@ -0,0 +1,249 @@
1
+ import tree_sitter_python as tspython
2
+ from tree_sitter import Language, Parser, Node
3
+ from typing import List, Tuple
4
+
5
+ from app.services.analysis_types import FileMetrics, FunctionMetrics
6
+
7
+ # Load Python grammar
8
+ PYTHON_LANGUAGE = Language(tspython.language())
9
+
10
+ class PythonTreeSitterAnalyzer:
11
+ def __init__(self):
12
+ self.parser = Parser(PYTHON_LANGUAGE)
13
+
14
+ def analyze_file(self, file_path: str) -> FileMetrics: # dictionary or FileMetrics? based on other analyzers it returns FileMetrics
15
+ with open(file_path, 'rb') as f:
16
+ content = f.read()
17
+
18
+ tree = self.parser.parse(content)
19
+
20
+ lines = content.splitlines()
21
+ nloc = len([l for l in lines if l.strip()])
22
+
23
+ # Extract functions, classes, and other scopes
24
+ functions = self._extract_scopes(tree.root_node)
25
+
26
+ avg_complexity = 0.0
27
+ if functions:
28
+ avg_complexity = sum(f.cyclomatic_complexity for f in functions) / len(functions)
29
+
30
+ # Calculate file-level metrics
31
+ comment_lines, todo_count = self._count_comments_and_todos(tree.root_node)
32
+ comment_density = comment_lines / nloc if nloc > 0 else 0.0
33
+
34
+ max_nesting_depth = self._calculate_max_nesting_depth(tree.root_node)
35
+ classes_count = self._count_classes(tree.root_node)
36
+
37
+ # New metrics specific to Python or general
38
+ # Replicating TS metrics where applicable
39
+
40
+ # Aggregate function metrics
41
+ total_function_length = sum(f.nloc for f in functions)
42
+ average_function_length = total_function_length / len(functions) if functions else 0.0
43
+ parameter_count = sum(f.parameter_count for f in functions)
44
+
45
+ return FileMetrics(
46
+ nloc=nloc,
47
+ average_cyclomatic_complexity=avg_complexity,
48
+ function_list=functions,
49
+ filename=file_path,
50
+ comment_lines=comment_lines,
51
+ comment_density=comment_density,
52
+ max_nesting_depth=max_nesting_depth,
53
+ average_function_length=average_function_length,
54
+ parameter_count=parameter_count,
55
+ todo_count=todo_count,
56
+ classes_count=classes_count,
57
+ # Initialize other fields to valid defaults
58
+ tsx_nesting_depth=0,
59
+ tsx_render_branching_count=0,
60
+ tsx_react_use_effect_count=0,
61
+ tsx_anonymous_handler_count=0,
62
+ tsx_prop_count=0,
63
+ ts_any_usage_count=0,
64
+ ts_ignore_count=0,
65
+ ts_import_coupling_count=0,
66
+ python_import_count=self._count_imports(tree.root_node),
67
+ tsx_hardcoded_string_volume=0,
68
+ tsx_duplicated_string_count=0,
69
+ ts_type_interface_count=0,
70
+ ts_export_count=0,
71
+ md_data_url_count=0
72
+ )
73
+
74
+ def _extract_scopes(self, root_node: Node) -> List[FunctionMetrics]:
75
+ # We want to capture:
76
+ # - Functions (def)
77
+ # - Classes (class)
78
+ # - Lambdas (lambda)
79
+
80
+ scope_types = {
81
+ 'function_definition',
82
+ 'class_definition',
83
+ 'lambda',
84
+ 'async_function_definition'
85
+ }
86
+
87
+ def process_node(node: Node) -> List[FunctionMetrics]:
88
+ results = []
89
+ for child in node.children:
90
+ if child.type in scope_types:
91
+ metrics = self._calculate_scope_metrics(child)
92
+ metrics.children = process_node(child)
93
+ results.append(metrics)
94
+ else:
95
+ # Recurse but don't create scope
96
+ results.extend(process_node(child))
97
+ return results
98
+
99
+ return process_node(root_node)
100
+
101
+ def _calculate_scope_metrics(self, node: Node) -> FunctionMetrics:
102
+ name = self._get_scope_name(node)
103
+ start_line = node.start_point.row + 1
104
+ end_line = node.end_point.row + 1
105
+ nloc = end_line - start_line + 1
106
+
107
+ complexity = self._calculate_complexity(node)
108
+ parameter_count = self._count_parameters(node)
109
+ max_nesting_depth = self._calculate_max_nesting_depth(node)
110
+ comment_lines, todo_count = self._count_comments_and_todos(node)
111
+
112
+ return FunctionMetrics(
113
+ name=name,
114
+ cyclomatic_complexity=complexity,
115
+ nloc=nloc,
116
+ start_line=start_line,
117
+ end_line=end_line,
118
+ parameter_count=parameter_count,
119
+ max_nesting_depth=max_nesting_depth,
120
+ comment_lines=comment_lines,
121
+ todo_count=todo_count,
122
+ origin_type=node.type
123
+ )
124
+
125
+ def _get_scope_name(self, node: Node) -> str:
126
+ if node.type in {'function_definition', 'class_definition', 'async_function_definition'}:
127
+ name_node = node.child_by_field_name('name')
128
+ if name_node:
129
+ prefix = ""
130
+ if node.type == 'class_definition': prefix = "(class) "
131
+ if node.type == 'async_function_definition': prefix = "(async) "
132
+ return f"{prefix}{name_node.text.decode('utf-8')}"
133
+
134
+ elif node.type == 'lambda':
135
+ return "(lambda)"
136
+
137
+ return "(anonymous)"
138
+
139
+ def _calculate_complexity(self, node: Node) -> int:
140
+ complexity = 1
141
+ complexity_node_types = {
142
+ 'if_statement',
143
+ 'for_statement',
144
+ 'while_statement',
145
+ 'except_clause',
146
+ 'with_statement',
147
+ 'match_statement',
148
+ 'case_pattern'
149
+ # try_statement itself is not usually a branch, except_clauses are
150
+ }
151
+
152
+ scope_boundary_types = {
153
+ 'function_definition',
154
+ 'class_definition',
155
+ 'lambda',
156
+ 'async_function_definition'
157
+ }
158
+
159
+ def traverse(n: Node):
160
+ nonlocal complexity
161
+ # Stop if we hit a nested function/class scope (metrics are calculated separately for them)
162
+ if n != node and n.type in scope_boundary_types:
163
+ return
164
+
165
+ if n.type in complexity_node_types:
166
+ complexity += 1
167
+
168
+ # Check for boolean operators
169
+ if n.type == 'boolean_operator':
170
+ text = n.text.decode('utf-8')
171
+ if 'and' in text or 'or' in text:
172
+ complexity += 1
173
+
174
+ for child in n.children:
175
+ traverse(child)
176
+
177
+ traverse(node)
178
+ return complexity
179
+
180
+ def _count_comments_and_todos(self, node: Node) -> Tuple[int, int]:
181
+ comment_lines = 0
182
+ todo_count = 0
183
+
184
+ def traverse(n: Node):
185
+ nonlocal comment_lines, todo_count
186
+ if n.type == 'comment':
187
+ comment_lines += (n.end_point.row - n.start_point.row + 1)
188
+ text = n.text.decode('utf-8', errors='ignore')
189
+ if 'TODO' in text or 'FIXME' in text:
190
+ todo_count += 1
191
+ for child in n.children:
192
+ traverse(child)
193
+
194
+ traverse(node)
195
+ return comment_lines, todo_count
196
+
197
+ def _calculate_max_nesting_depth(self, node: Node) -> int:
198
+ max_depth = 0
199
+ nesting_types = {
200
+ 'if_statement', 'for_statement', 'while_statement', 'try_statement', 'with_statement', 'match_statement'
201
+ # function/class are also nesting, but we treat them as scopes usually
202
+ }
203
+
204
+ def traverse(n: Node, current_depth: int):
205
+ nonlocal max_depth
206
+ max_depth = max(max_depth, current_depth)
207
+ for child in n.children:
208
+ next_depth = current_depth
209
+ if child.type in nesting_types:
210
+ next_depth += 1
211
+ traverse(child, next_depth)
212
+
213
+ traverse(node, 0)
214
+ return max_depth
215
+
216
+ def _count_classes(self, node: Node) -> int:
217
+ count = 0
218
+ def traverse(n: Node):
219
+ nonlocal count
220
+ if n.type == 'class_definition':
221
+ count += 1
222
+ for child in n.children:
223
+ traverse(child)
224
+ traverse(node)
225
+ return count
226
+
227
+ def _count_parameters(self, node: Node) -> int:
228
+ # Check for 'parameters' field
229
+ params_node = node.child_by_field_name('parameters')
230
+
231
+ if params_node:
232
+ count = 0
233
+ for child in params_node.children:
234
+ # Filter punctuation
235
+ if child.type in {'identifier', 'typed_parameter', 'default_parameter', 'typed_default_parameter', 'list_splat_pattern', 'dictionary_splat_pattern'}:
236
+ count += 1
237
+ return count
238
+ return 0
239
+
240
+ def _count_imports(self, node: Node) -> int:
241
+ count = 0
242
+ def traverse(n: Node):
243
+ nonlocal count
244
+ if n.type == 'import_statement' or n.type == 'import_from_statement':
245
+ count += 1
246
+ for child in n.children:
247
+ traverse(child)
248
+ traverse(node)
249
+ return count
@@ -1,68 +1,12 @@
1
1
  import tree_sitter_typescript as tstypescript
2
2
  from tree_sitter import Language, Parser, Node
3
- from dataclasses import dataclass, field
4
3
  from typing import List
5
- import re
6
4
 
7
5
  # Load TypeScript and TSX grammars
8
6
  TYPESCRIPT_LANGUAGE = Language(tstypescript.language_typescript())
9
7
  TSX_LANGUAGE = Language(tstypescript.language_tsx())
10
8
 
11
- @dataclass
12
- class FunctionMetrics:
13
- name: str
14
- cyclomatic_complexity: int
15
- nloc: int
16
- start_line: int
17
- end_line: int
18
- parameter_count: int = 0
19
- max_nesting_depth: int = 0
20
- comment_lines: int = 0
21
- todo_count: int = 0
22
- # TS/TSX Specific Metrics (per-function)
23
- ts_type_interface_count: int = 0
24
- # Markdown-specific metrics
25
- md_data_url_count: int = 0
26
- # TSX structure helpers
27
- contains_tsx: bool = False
28
- tsx_start_line: int = 0
29
- tsx_end_line: int = 0
30
- tsx_root_name: str = ""
31
- tsx_root_is_fragment: bool = False
32
- origin_type: str = ""
33
- is_jsx_container: bool = False
34
- children: List["FunctionMetrics"] = field(default_factory=list)
35
- # We store children to represent nested functions.
36
-
37
- @dataclass
38
- class FileMetrics:
39
- nloc: int
40
- average_cyclomatic_complexity: float
41
- function_list: List[FunctionMetrics] = field(default_factory=list)
42
- filename: str = ""
43
- comment_lines: int = 0
44
- comment_density: float = 0.0
45
- max_nesting_depth: int = 0
46
- average_function_length: float = 0.0
47
- parameter_count: int = 0
48
- todo_count: int = 0
49
- classes_count: int = 0
50
-
51
- # TS/TSX Specific Metrics
52
- tsx_nesting_depth: int = 0
53
- tsx_render_branching_count: int = 0
54
- tsx_react_use_effect_count: int = 0
55
- tsx_anonymous_handler_count: int = 0
56
- tsx_prop_count: int = 0
57
- ts_any_usage_count: int = 0
58
- ts_ignore_count: int = 0
59
- ts_import_coupling_count: int = 0
60
- tsx_hardcoded_string_volume: int = 0
61
- tsx_duplicated_string_count: int = 0
62
- ts_type_interface_count: int = 0
63
- ts_export_count: int = 0
64
- # Markdown-specific metrics (per-file)
65
- md_data_url_count: int = 0
9
+ from app.services.analysis_types import FileMetrics, FunctionMetrics
66
10
 
67
11
  class TreeSitterAnalyzer:
68
12
  def __init__(self):