srcly 0.1.16__tar.gz → 0.1.18__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.
- {srcly-0.1.16 → srcly-0.1.18}/PKG-INFO +2 -1
- {srcly-0.1.16 → srcly-0.1.18}/app/models.py +4 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/routers/analysis.py +3 -2
- {srcly-0.1.16 → srcly-0.1.18}/app/routers/files.py +4 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/services/analysis.py +29 -4
- srcly-0.1.18/app/services/analysis_types.py +61 -0
- {srcly-0.1.16/app/services → srcly-0.1.18/app/services/css}/css_analysis.py +1 -1
- {srcly-0.1.16 → srcly-0.1.18}/app/services/data_flow_analysis.py +1 -1
- {srcly-0.1.16/app/services → srcly-0.1.18/app/services/ipynb}/ipynb_analysis.py +41 -1
- {srcly-0.1.16/app/services → srcly-0.1.18/app/services/markdown}/markdown_analysis.py +1 -1
- srcly-0.1.18/app/services/python/python_analysis.py +249 -0
- srcly-0.1.16/app/services/tree_sitter_analysis.py → srcly-0.1.18/app/services/typescript/typescript_analysis.py +1 -57
- srcly-0.1.18/app/static/assets/index-BuiLB-f3.js +37 -0
- srcly-0.1.18/app/static/assets/index-lgAeJI5G.css +1 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/index.html +2 -2
- {srcly-0.1.16 → srcly-0.1.18}/pyproject.toml +2 -1
- srcly-0.1.16/app/static/assets/index-CX6VqYPz.js +0 -37
- srcly-0.1.16/app/static/assets/index-DXVBnNom.css +0 -1
- {srcly-0.1.16 → srcly-0.1.18}/.gitignore +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/README.md +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/.gitignore +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/__init__.py +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/config.py +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/main.py +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/run.py +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/services/cache.py +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/abap-BdImnpbu.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/actionscript-3-CfeIJUat.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/ada-bCR0ucgS.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/andromeeda-C-Jbm3Hp.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/angular-html-CU67Zn6k.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/angular-ts-BwZT4LLn.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/apache-Pmp26Uib.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/apex-DhZLUxFE.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/apl-dKokRX4l.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/applescript-Co6uUVPk.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/ara-BRHolxvo.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/asciidoc-Dv7Oe6Be.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/asm-D_Q5rh1f.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/astro-CbQHKStN.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/aurora-x-D-2ljcwZ.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/awk-DMzUqQB5.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/ayu-dark-Cv9koXgw.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/ballerina-BFfxhgS-.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/bat-BkioyH1T.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/beancount-k_qm7-4y.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/berry-D08WgyRC.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/bibtex-CHM0blh-.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/bicep-Bmn6On1c.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/blade-DVc8C-J4.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/bsl-BO_Y6i37.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/c-BIGW1oBm.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/cadence-Bv_4Rxtq.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/cairo-KRGpt6FW.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/catppuccin-frappe-DFWUc33u.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/catppuccin-latte-C9dUb6Cb.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/catppuccin-macchiato-DQyhUUbL.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/catppuccin-mocha-D87Tk5Gz.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/clarity-D53aC0YG.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/clojure-P80f7IUj.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/cmake-D1j8_8rp.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/cobol-nwyudZeR.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/codeowners-Bp6g37R7.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/codeql-DsOJ9woJ.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/coffee-Ch7k5sss.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/common-lisp-Cg-RD9OK.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/coq-DkFqJrB1.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/cpp-CofmeUqb.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/crystal-tKQVLTB8.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/csharp-CX12Zw3r.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/css-DPfMkruS.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/csv-fuZLfV_i.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/cue-D82EKSYY.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/cypher-COkxafJQ.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/d-85-TOEBH.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/dark-plus-eOWES_5F.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/dart-CF10PKvl.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/dax-CEL-wOlO.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/desktop-BmXAJ9_W.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/diff-D97Zzqfu.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/docker-BcOcwvcX.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/dotenv-Da5cRb03.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/dracula-BzJJZx-M.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/dracula-soft-BXkSAIEj.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/dream-maker-BtqSS_iP.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/edge-BkV0erSs.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/elixir-CDX3lj18.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/elm-DbKCFpqz.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/emacs-lisp-C9XAeP06.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/erb-BOJIQeun.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/erlang-DsQrWhSR.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/everforest-dark-BgDCqdQA.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/everforest-light-C8M2exoo.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/fennel-BYunw83y.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/fish-BvzEVeQv.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/fluent-C4IJs8-o.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/fortran-fixed-form-BZjJHVRy.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/fortran-free-form-D22FLkUw.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/fsharp-CXgrBDvD.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gdresource-B7Tvp0Sc.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gdscript-DTMYz4Jt.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gdshader-DkwncUOv.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/genie-D0YGMca9.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gherkin-DyxjwDmM.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/git-commit-F4YmCXRG.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/git-rebase-r7XF79zn.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/github-dark-DHJKELXO.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/github-dark-default-Cuk6v7N8.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/github-dark-dimmed-DH5Ifo-i.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/github-dark-high-contrast-E3gJ1_iC.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/github-light-DAi9KRSo.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/github-light-default-D7oLnXFd.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/github-light-high-contrast-BfjtVDDH.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gleam-BspZqrRM.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/glimmer-js-Rg0-pVw9.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/glimmer-ts-U6CK756n.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/glsl-DplSGwfg.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gnuplot-DdkO51Og.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/go-Dn2_MT6a.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/graphql-ChdNCCLP.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/groovy-gcz8RCvz.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gruvbox-dark-hard-CFHQjOhq.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gruvbox-dark-medium-GsRaNv29.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gruvbox-dark-soft-CVdnzihN.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gruvbox-light-hard-CH1njM8p.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gruvbox-light-medium-DRw_LuNl.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/gruvbox-light-soft-hJgmCMqR.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/hack-CaT9iCJl.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/haml-B8DHNrY2.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/handlebars-BL8al0AC.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/haskell-Df6bDoY_.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/haxe-CzTSHFRz.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/hcl-BWvSN4gD.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/hjson-D5-asLiD.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/hlsl-D3lLCCz7.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/houston-DnULxvSX.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/html-GMplVEZG.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/html-derivative-BFtXZ54Q.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/http-jrhK8wxY.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/hurl-irOxFIW8.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/hxml-Bvhsp5Yf.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/hy-DFXneXwc.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/imba-DGztddWO.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/ini-BEwlwnbL.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/java-CylS5w8V.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/javascript-wDzz0qaB.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/jinja-4LBKfQ-Z.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/jison-wvAkD_A8.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/json-Cp-IABpG.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/json5-C9tS-k6U.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/jsonc-Des-eS-w.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/jsonl-DcaNXYhu.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/jsonnet-DFQXde-d.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/jssm-C2t-YnRu.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/jsx-g9-lgVsj.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/julia-C8NyazO9.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/kanagawa-dragon-CkXjmgJE.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/kanagawa-lotus-CfQXZHmo.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/kanagawa-wave-DWedfzmr.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/kdl-DV7GczEv.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/kotlin-BdnUsdx6.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/kusto-BvAqAH-y.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/laserwave-DUszq2jm.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/latex-BUKiar2Z.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/lean-DP1Csr6i.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/less-B1dDrJ26.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/light-plus-B7mTdjB0.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/liquid-DYVedYrR.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/llvm-BtvRca6l.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/log-2UxHyX5q.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/logo-BtOb2qkB.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/lua-BbnMAYS6.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/luau-CXu1NL6O.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/make-CHLpvVh8.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/markdown-Cvjx9yec.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/marko-CPi9NSCl.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/material-theme-D5KoaKCx.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/material-theme-darker-BfHTSMKl.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/material-theme-lighter-B0m2ddpp.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/material-theme-ocean-CyktbL80.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/material-theme-palenight-Csfq5Kiy.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/matlab-D7o27uSR.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/mdc-DUICxH0z.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/mdx-Cmh6b_Ma.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/mermaid-DKYwYmdq.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/min-dark-CafNBF8u.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/min-light-CTRr51gU.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/mipsasm-CKIfxQSi.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/mojo-1DNp92w6.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/monokai-D4h5O-jR.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/move-Bu9oaDYs.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/narrat-DRg8JJMk.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/nextflow-CUEJCptM.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/nginx-DknmC5AR.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/night-owl-C39BiMTA.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/nim-CVrawwO9.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/nix-BbRYJGeE.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/nord-Ddv68eIx.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/nushell-C-sUppwS.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/objective-c-DXmwc3jG.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/objective-cpp-CLxacb5B.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/ocaml-C0hk2d4L.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/one-dark-pro-DVMEJ2y_.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/one-light-PoHY5YXO.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/pascal-D93ZcfNL.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/perl-C0TMdlhV.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/php-CDn_0X-4.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/pkl-u5AG7uiY.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/plastic-3e1v2bzS.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/plsql-ChMvpjG-.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/po-BTJTHyun.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/poimandres-CS3Unz2-.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/polar-C0HS_06l.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/postcss-CXtECtnM.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/powerquery-CEu0bR-o.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/powershell-Dpen1YoG.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/prisma-Dd19v3D-.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/prolog-CbFg5uaA.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/proto-DyJlTyXw.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/pug-CGlum2m_.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/puppet-BMWR74SV.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/purescript-CklMAg4u.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/python-B6aJPvgy.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/qml-3beO22l8.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/qmldir-C8lEn-DE.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/qss-IeuSbFQv.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/r-DiinP2Uv.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/racket-BqYA7rlc.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/raku-DXvB9xmW.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/razor-WgofotgN.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/red-bN70gL4F.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/reg-C-SQnVFl.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/regexp-CDVJQ6XC.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/rel-C3B-1QV4.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/riscv-BM1_JUlF.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/rose-pine-BHrmToEH.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/rose-pine-dawn-CnK8MTSM.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/rose-pine-moon-NleAzG8P.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/rosmsg-BJDFO7_C.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/rst-B0xPkSld.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/ruby-BvKwtOVI.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/rust-B1yitclQ.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/sas-cz2c8ADy.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/sass-Cj5Yp3dK.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/scala-C151Ov-r.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/scheme-C98Dy4si.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/scss-OYdSNvt2.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/sdbl-DVxCFoDh.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/shaderlab-Dg9Lc6iA.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/shellscript-Yzrsuije.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/shellsession-BADoaaVG.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/slack-dark-BthQWCQV.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/slack-ochin-DqwNpetd.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/smalltalk-BERRCDM3.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/snazzy-light-Bw305WKR.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/solarized-dark-DXbdFlpD.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/solarized-light-L9t79GZl.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/solidity-BbcW6ACK.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/soy-Brmx7dQM.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/sparql-rVzFXLq3.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/splunk-BtCnVYZw.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/sql-BLtJtn59.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/ssh-config-_ykCGR6B.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/stata-BH5u7GGu.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/stylus-BEDo0Tqx.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/svelte-3Dk4HxPD.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/swift-Dg5xB15N.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/synthwave-84-CbfX1IO0.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/system-verilog-CnnmHF94.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/systemd-4A_iFExJ.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/talonscript-CkByrt1z.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/tasl-QIJgUcNo.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/tcl-dwOrl1Do.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/templ-W15q3VgB.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/terraform-BETggiCN.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/tex-Cppo0RY3.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/tokyo-night-hegEt444.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/toml-vGWfd6FD.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/ts-tags-zn1MmPIZ.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/tsv-B_m7g4N7.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/tsx-COt5Ahok.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/turtle-BsS91CYL.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/twig-CO9l9SDP.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/typescript-BPQ3VLAy.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/typespec-Df68jz8_.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/typst-DHCkPAjA.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/v-BcVCzyr7.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vala-CsfeWuGM.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vb-D17OF-Vu.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/verilog-BQ8w6xss.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vesper-DU1UobuO.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vhdl-CeAyd5Ju.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/viml-CJc9bBzg.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vitesse-black-Bkuqu6BP.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vitesse-dark-D0r3Knsf.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vitesse-light-CVO1_9PV.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vue-CCoi5OLL.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vue-html-DAAvJJDi.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vue-vine-_Ih-lPRR.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/vyper-CDx5xZoG.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/wasm-CG6Dc4jp.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/wasm-MzD3tlZU.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/wenyan-BV7otONQ.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/wgsl-Dx-B1_4e.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/wikitext-BhOHFoWU.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/wit-5i3qLPDT.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/wolfram-lXgVvXCa.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/xml-sdJ4AIDG.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/xsl-CtQFsRM5.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/yaml-Buea-lGh.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/zenscript-DVFEvuxE.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/assets/zig-VOosw3JB.js +0 -0
- {srcly-0.1.16 → srcly-0.1.18}/app/static/vite.svg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: srcly
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.18
|
|
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
|
|
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 =
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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):
|