@pubinfo-pr/devtools 0.189.2 → 0.203.2
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.
- package/dist/client/assets/Navbar-D8ux538U.css +18 -0
- package/dist/client/assets/Navbar-DCdslOKs.js +219 -0
- package/dist/client/assets/PanelGrids-Bq_2z9g3.js +9 -0
- package/dist/client/assets/SelectTabs-Chnu7x16.js +171 -0
- package/dist/client/assets/_plugin-vue_export-helper-D8E0syuh.js +6 -0
- package/dist/client/assets/component-Qe9twpoz.js +468 -0
- package/dist/client/assets/{server-router-lMc2o_SM.js → core-D6JqxgYq.js} +562 -4435
- package/dist/client/assets/css-D-vtiAqw.js +2 -0
- package/dist/client/assets/engine-oniguruma-n-bafM3H.js +392 -0
- package/dist/client/assets/{fetch-ivePQGil.js → fetch-CwAFBmMG.js} +229 -127
- package/dist/client/assets/html-74krLQbR.js +2 -0
- package/dist/client/assets/{html-Bieo8lKA.js → html-Tu6OX5Eq.js} +1 -1
- package/dist/client/assets/import-DV9l8S2T.js +375 -0
- package/dist/client/assets/{index-D1hS5xpb.js → index-ByOJyids.js} +2008 -1841
- package/dist/client/assets/index-DNthkvua.css +443 -0
- package/dist/client/assets/issue-BHrGN1_d.css +10 -0
- package/dist/client/assets/issue-MdUzHAB3.js +130 -0
- package/dist/client/assets/json-WgRhEAOB.js +2 -0
- package/dist/client/assets/pages-D4LiKgxN.js +285 -0
- package/dist/client/assets/pages-DQ8FtB9Y.css +17 -0
- package/dist/client/assets/{server-router-BiZSSnZD.css → server-router-B1AB70as.css} +2 -20
- package/dist/client/assets/server-router-DuEpkdvh.js +2049 -0
- package/dist/client/assets/typescript-C5tShSk5.js +2 -0
- package/dist/client/assets/vue-DZSPIjN6.js +27 -0
- package/dist/client/component.svg +1 -0
- package/dist/client/import.svg +1 -0
- package/dist/client/index.html +2 -2
- package/dist/client/issue.svg +1 -0
- package/dist/client/logo.svg +1 -0
- package/dist/{constants.d.ts → constants.d.mts} +1 -2
- package/dist/{dirs2.js → dirs.mjs} +1 -1
- package/dist/index.d.mts +6 -0
- package/dist/index.mjs +2508 -0
- package/dist/panel/{index.d.ts → index.d.mts} +1 -0
- package/dist/panel/index.mjs +151 -0
- package/package.json +39 -28
- package/dist/client/assets/abap-C4yRz_Vm.js +0 -2
- package/dist/client/assets/actionscript-3-CKZBsEJ7.js +0 -2
- package/dist/client/assets/ada-C3S8BVqd.js +0 -2
- package/dist/client/assets/andromeeda--PyjP4Vw.js +0 -2
- package/dist/client/assets/angular-html-Cn84m4r6.js +0 -2
- package/dist/client/assets/angular-html-DMAfzvoK.js +0 -22
- package/dist/client/assets/angular-ts-Bmg_Yndu.js +0 -21
- package/dist/client/assets/apache-CcuXWvTQ.js +0 -2
- package/dist/client/assets/apex-CDLJR-L-.js +0 -2
- package/dist/client/assets/apl-BV2Pqeyz.js +0 -15
- package/dist/client/assets/applescript-LAEuNJUm.js +0 -2
- package/dist/client/assets/ara-DarDo4Rh.js +0 -2
- package/dist/client/assets/asciidoc-B6fvDPBR.js +0 -2
- package/dist/client/assets/asm-BhHPpHEe.js +0 -2
- package/dist/client/assets/astro-Bv3Ci3Ef.js +0 -17
- package/dist/client/assets/aurora-x-Dd5GiV6c.js +0 -2
- package/dist/client/assets/awk-brgpYH3W.js +0 -2
- package/dist/client/assets/ayu-dark-CxG_4pQO.js +0 -2
- package/dist/client/assets/ballerina-Dgbivx-S.js +0 -2
- package/dist/client/assets/bat-tWFkNZJc.js +0 -2
- package/dist/client/assets/beancount-Tyb60Wcr.js +0 -2
- package/dist/client/assets/berry-DUxqu-Yw.js +0 -2
- package/dist/client/assets/bibtex-BPtnjAmG.js +0 -2
- package/dist/client/assets/bicep-CGLW9Upz.js +0 -2
- package/dist/client/assets/blade-CLAuu2bN.js +0 -19
- package/dist/client/assets/bsl-VyzHF-R4.js +0 -4
- package/dist/client/assets/c-DXXaAAzT.js +0 -2
- package/dist/client/assets/c-D_IBjAfc.js +0 -2
- package/dist/client/assets/cadence-CUT5CLY6.js +0 -2
- package/dist/client/assets/cairo-DuZF6whY.js +0 -4
- package/dist/client/assets/catppuccin-frappe-HeBtbi6Y.js +0 -2
- package/dist/client/assets/catppuccin-latte-BG-xrhIb.js +0 -2
- package/dist/client/assets/catppuccin-macchiato-DnRSgXl3.js +0 -2
- package/dist/client/assets/catppuccin-mocha-yXSPF4nl.js +0 -2
- package/dist/client/assets/clarity-DqSC35SX.js +0 -2
- package/dist/client/assets/clojure-BZtS8UQS.js +0 -2
- package/dist/client/assets/cmake-o2KFVZsk.js +0 -2
- package/dist/client/assets/cobol-BHLrWv6w.js +0 -9
- package/dist/client/assets/codeowners-tdICZVC5.js +0 -2
- package/dist/client/assets/codeql-BZid4SQS.js +0 -2
- package/dist/client/assets/coffee-C5___ngf.js +0 -4
- package/dist/client/assets/common-lisp-DIm6wDvc.js +0 -2
- package/dist/client/assets/coq-CwPY4Eyo.js +0 -2
- package/dist/client/assets/cpp-B7t2k5-5.js +0 -19
- package/dist/client/assets/cpp-BM5VrcWq.js +0 -2
- package/dist/client/assets/crystal-C8Dwwy6W.js +0 -17
- package/dist/client/assets/csharp-DH3zrwKA.js +0 -2
- package/dist/client/assets/css-FkBLLRUu.js +0 -2
- package/dist/client/assets/csv-DXn8kTD7.js +0 -2
- package/dist/client/assets/cue-3XLcXtdk.js +0 -2
- package/dist/client/assets/cypher-BC872mTV.js +0 -2
- package/dist/client/assets/d-DGFFiwLE.js +0 -2
- package/dist/client/assets/dark-plus-D-GJgvtj.js +0 -2
- package/dist/client/assets/dart-CWKC80aE.js +0 -2
- package/dist/client/assets/dax-bvEEbNAK.js +0 -2
- package/dist/client/assets/desktop-XhOYrgrI.js +0 -2
- package/dist/client/assets/diff-U-x6iVIx.js +0 -2
- package/dist/client/assets/docker-B7cyRvbG.js +0 -2
- package/dist/client/assets/dotenv-D_9fGa07.js +0 -2
- package/dist/client/assets/dracula-BISwCS2n.js +0 -2
- package/dist/client/assets/dracula-soft-WhTQpJ81.js +0 -2
- package/dist/client/assets/dream-maker-Bdm7BhsZ.js +0 -2
- package/dist/client/assets/edge-BL7B074g.js +0 -11
- package/dist/client/assets/elixir-BeYNyax5.js +0 -4
- package/dist/client/assets/elm-rybyhq4V.js +0 -4
- package/dist/client/assets/emacs-lisp-Cy-_xiO2.js +0 -2
- package/dist/client/assets/erb-Dvs8j3zz.js +0 -9
- package/dist/client/assets/erlang-CWJ5EBhz.js +0 -4
- package/dist/client/assets/everforest-dark-d3qrbHMi.js +0 -2
- package/dist/client/assets/everforest-light-D0My6uss.js +0 -2
- package/dist/client/assets/fennel-BNqS--y3.js +0 -2
- package/dist/client/assets/fish-BBnq9bEt.js +0 -2
- package/dist/client/assets/fluent-oY7_tZF8.js +0 -2
- package/dist/client/assets/fortran-fixed-form-0peVr9Yb.js +0 -4
- package/dist/client/assets/fortran-free-form-BEQ27g3E.js +0 -2
- package/dist/client/assets/fsharp-BOj5a2vM.js +0 -4
- package/dist/client/assets/gdresource-Ch1u7SF7.js +0 -9
- package/dist/client/assets/gdscript-BhhtywAa.js +0 -2
- package/dist/client/assets/gdshader-BDnAJwOj.js +0 -2
- package/dist/client/assets/genie-Cd2lLzpm.js +0 -2
- package/dist/client/assets/gherkin-CwXypCQe.js +0 -2
- package/dist/client/assets/git-commit-CFfXzer2.js +0 -4
- package/dist/client/assets/git-rebase-DG0qmgCf.js +0 -4
- package/dist/client/assets/github-dark-DN66FzyL.js +0 -2
- package/dist/client/assets/github-dark-default-DQvsk902.js +0 -2
- package/dist/client/assets/github-dark-dimmed-Njf4dUfx.js +0 -2
- package/dist/client/assets/github-dark-high-contrast-BQnmPqTu.js +0 -2
- package/dist/client/assets/github-light-default-BrZf7E_d.js +0 -2
- package/dist/client/assets/github-light-gaCYdU0_.js +0 -2
- package/dist/client/assets/github-light-high-contrast-DJM0D1FU.js +0 -2
- package/dist/client/assets/gleam-Dfph-OFw.js +0 -2
- package/dist/client/assets/glimmer-js-DxVMOGMY.js +0 -13
- package/dist/client/assets/glimmer-ts-CPM08s8U.js +0 -13
- package/dist/client/assets/glsl-416x4rq0.js +0 -4
- package/dist/client/assets/glsl-Dt1TVrzb.js +0 -2
- package/dist/client/assets/gnuplot-BdTyKzf-.js +0 -2
- package/dist/client/assets/go-BCxV4YsM.js +0 -2
- package/dist/client/assets/graphql-CcseMMny.js +0 -2
- package/dist/client/assets/graphql-D3vBqXJE.js +0 -13
- package/dist/client/assets/groovy-Cy7l6UGB.js +0 -2
- package/dist/client/assets/gruvbox-dark-hard-DZatEion.js +0 -2
- package/dist/client/assets/gruvbox-dark-medium-6l84Vseq.js +0 -2
- package/dist/client/assets/gruvbox-dark-soft-CjCWiqUJ.js +0 -2
- package/dist/client/assets/gruvbox-light-hard-Ci9ZMVVA.js +0 -2
- package/dist/client/assets/gruvbox-light-medium-vuFc7IgU.js +0 -2
- package/dist/client/assets/gruvbox-light-soft-BMc7AIW5.js +0 -2
- package/dist/client/assets/hack-Dne48p1m.js +0 -9
- package/dist/client/assets/haml-D-tmAqdp.js +0 -9
- package/dist/client/assets/haml-DoXvVBFP.js +0 -2
- package/dist/client/assets/handlebars-Jfc88P1Q.js +0 -13
- package/dist/client/assets/haskell-CB6gNPpR.js +0 -2
- package/dist/client/assets/haxe-Cbrtif6E.js +0 -2
- package/dist/client/assets/hcl-gkV_38jF.js +0 -2
- package/dist/client/assets/hjson-Bt79zj56.js +0 -2
- package/dist/client/assets/hlsl-Cq-bbtEM.js +0 -2
- package/dist/client/assets/houston-B50hvrbm.js +0 -2
- package/dist/client/assets/html-Crtt6vRf.js +0 -2
- package/dist/client/assets/html-derivative-5ylkdQOk.js +0 -4
- package/dist/client/assets/html-derivative-KSsgXIhX.js +0 -2
- package/dist/client/assets/http-ChH5xHQK.js +0 -13
- package/dist/client/assets/hurl-10MML7cL.js +0 -11
- package/dist/client/assets/hxml-tJu5NW0B.js +0 -4
- package/dist/client/assets/hy-Be9tp-ZM.js +0 -2
- package/dist/client/assets/imba-B-QVb_a4.js +0 -2
- package/dist/client/assets/index-Co6hzA6u.css +0 -443
- package/dist/client/assets/ini-CQJRLei8.js +0 -2
- package/dist/client/assets/java-C-daPB_o.js +0 -2
- package/dist/client/assets/java-Cs-EdD8w.js +0 -2
- package/dist/client/assets/jinja-B6ZjKqt-.js +0 -6
- package/dist/client/assets/jison-hgxzSL4P.js +0 -4
- package/dist/client/assets/json-CZvQXtwG.js +0 -2
- package/dist/client/assets/json5-D0C1B7l9.js +0 -2
- package/dist/client/assets/jsonc-B0f0uTR-.js +0 -2
- package/dist/client/assets/jsonl-rrn3pXpA.js +0 -2
- package/dist/client/assets/jsonnet-DWKQ3es7.js +0 -2
- package/dist/client/assets/jssm-BPKNebRE.js +0 -2
- package/dist/client/assets/jsx--Vhk3JR0.js +0 -2
- package/dist/client/assets/jsx-BPBfDC8G.js +0 -2
- package/dist/client/assets/julia-Bl_xQs5i.js +0 -15
- package/dist/client/assets/kanagawa-dragon-BNh5Br7k.js +0 -2
- package/dist/client/assets/kanagawa-lotus-BBsJOMvi.js +0 -2
- package/dist/client/assets/kanagawa-wave-ouS5fij6.js +0 -2
- package/dist/client/assets/kdl-F0m2ZhAD.js +0 -2
- package/dist/client/assets/kotlin-La5Qv-cP.js +0 -2
- package/dist/client/assets/kusto-t2Fo0FVP.js +0 -2
- package/dist/client/assets/laserwave-DYFEyShp.js +0 -2
- package/dist/client/assets/latex-BHqYCFCs.js +0 -4
- package/dist/client/assets/lean-DLusw1eC.js +0 -2
- package/dist/client/assets/less-C9jqjrjf.js +0 -2
- package/dist/client/assets/light-plus-CroBFUPD.js +0 -2
- package/dist/client/assets/liquid-CWSQtxRO.js +0 -13
- package/dist/client/assets/llvm-Do8PimHF.js +0 -2
- package/dist/client/assets/log-DcUikSJo.js +0 -2
- package/dist/client/assets/logo-BbssGAy6.js +0 -2
- package/dist/client/assets/lua-BOayniRF.js +0 -4
- package/dist/client/assets/lua-DmBLQvwZ.js +0 -2
- package/dist/client/assets/luau-CLP6V1lC.js +0 -2
- package/dist/client/assets/make-CH096rgi.js +0 -2
- package/dist/client/assets/markdown-CEnXrHFM.js +0 -2
- package/dist/client/assets/marko-g1H15jhu.js +0 -13
- package/dist/client/assets/material-theme-Dj-QIsXM.js +0 -2
- package/dist/client/assets/material-theme-darker-BquH-FBp.js +0 -2
- package/dist/client/assets/material-theme-lighter-C7x4dAbs.js +0 -2
- package/dist/client/assets/material-theme-ocean-BtlX7ouJ.js +0 -2
- package/dist/client/assets/material-theme-palenight-Up2QRioJ.js +0 -2
- package/dist/client/assets/matlab-C8-OBOXt.js +0 -2
- package/dist/client/assets/mdc-X-Q62jZE.js +0 -11
- package/dist/client/assets/mdx-9O-a3QeT.js +0 -2
- package/dist/client/assets/mermaid-BbwNpgQZ.js +0 -2
- package/dist/client/assets/min-dark-2_-s08D1.js +0 -2
- package/dist/client/assets/min-light-DldaIPDx.js +0 -2
- package/dist/client/assets/mipsasm-Cw0tctoa.js +0 -2
- package/dist/client/assets/mojo-B1_s-UsE.js +0 -2
- package/dist/client/assets/monokai-Cl9qFpjv.js +0 -2
- package/dist/client/assets/move-Bj6Zb8y4.js +0 -2
- package/dist/client/assets/narrat-CmcwvZwb.js +0 -2
- package/dist/client/assets/nextflow-C8b_RKlg.js +0 -2
- package/dist/client/assets/nginx-DZc9-Tp6.js +0 -4
- package/dist/client/assets/night-owl-B5V1WZkp.js +0 -2
- package/dist/client/assets/nim-7v2Y99DS.js +0 -19
- package/dist/client/assets/nix-BwXsP5pA.js +0 -2
- package/dist/client/assets/nord-Btx7uc0u.js +0 -2
- package/dist/client/assets/nushell-BtZNOG2q.js +0 -2
- package/dist/client/assets/objective-c-BemUaH4e.js +0 -2
- package/dist/client/assets/objective-cpp-DlBDqgG3.js +0 -2
- package/dist/client/assets/ocaml-CJb6Uk1q.js +0 -2
- package/dist/client/assets/one-dark-pro-lexsOLIl.js +0 -2
- package/dist/client/assets/one-light-B8VjztUu.js +0 -2
- package/dist/client/assets/pages-bp1a9NBa.js +0 -20
- package/dist/client/assets/pascal-DlgTpK5e.js +0 -2
- package/dist/client/assets/perl-BeVMkbag.js +0 -15
- package/dist/client/assets/php-C6iKy-Ct.js +0 -17
- package/dist/client/assets/pkl-Di_e35rA.js +0 -2
- package/dist/client/assets/plastic-BZOIXfgJ.js +0 -2
- package/dist/client/assets/plsql-onDHxOm7.js +0 -2
- package/dist/client/assets/po-y_cLFUS0.js +0 -2
- package/dist/client/assets/poimandres-BiZZscc4.js +0 -2
- package/dist/client/assets/polar-CHjSLFFr.js +0 -2
- package/dist/client/assets/postcss-CX2EaU1R.js +0 -2
- package/dist/client/assets/powerquery-oxPwTqud.js +0 -2
- package/dist/client/assets/powershell-B0zZnlEM.js +0 -2
- package/dist/client/assets/prisma-ZOIUNjhk.js +0 -2
- package/dist/client/assets/prolog-n_5l0qJw.js +0 -2
- package/dist/client/assets/proto-B4oN1zyC.js +0 -2
- package/dist/client/assets/pug-C2f4TGT8.js +0 -11
- package/dist/client/assets/puppet-D6RdqmmO.js +0 -2
- package/dist/client/assets/purescript-uD8x65wA.js +0 -2
- package/dist/client/assets/python-DhGWz8au.js +0 -2
- package/dist/client/assets/qml-XUmYl05X.js +0 -4
- package/dist/client/assets/qmldir-DMBYbgER.js +0 -2
- package/dist/client/assets/qss-0YYw7rH_.js +0 -2
- package/dist/client/assets/r-3G2Sa7Lt.js +0 -2
- package/dist/client/assets/r-j_8gjFkr.js +0 -2
- package/dist/client/assets/racket--4VV2q-z.js +0 -2
- package/dist/client/assets/raku-CGJzHQNZ.js +0 -2
- package/dist/client/assets/razor-CylxxiO_.js +0 -9
- package/dist/client/assets/red-9ZL-7q2K.js +0 -2
- package/dist/client/assets/reg-Bm98aNob.js +0 -2
- package/dist/client/assets/regexp-BPcLhQg6.js +0 -2
- package/dist/client/assets/regexp-DhxLCx4Q.js +0 -2
- package/dist/client/assets/rel-ByZY4xG9.js +0 -2
- package/dist/client/assets/riscv-NyBqTLqf.js +0 -2
- package/dist/client/assets/rose-pine-BOSxnGL0.js +0 -2
- package/dist/client/assets/rose-pine-dawn-Bid2Zry0.js +0 -2
- package/dist/client/assets/rose-pine-moon-BC-OrH_E.js +0 -2
- package/dist/client/assets/rosmsg-Bp817qPt.js +0 -2
- package/dist/client/assets/rst-C9F4xiBz.js +0 -21
- package/dist/client/assets/ruby-DEIXHl5I.js +0 -29
- package/dist/client/assets/rust-C7nEo4AU.js +0 -2
- package/dist/client/assets/sas-CCRtK_GG.js +0 -4
- package/dist/client/assets/sass-DVup--Ip.js +0 -2
- package/dist/client/assets/scala-DGN7vr-3.js +0 -2
- package/dist/client/assets/scheme-DiWp84R_.js +0 -2
- package/dist/client/assets/scss-B8KTULjf.js +0 -2
- package/dist/client/assets/scss-CW08jUJu.js +0 -4
- package/dist/client/assets/sdbl-cYh-3vef.js +0 -2
- package/dist/client/assets/shaderlab-D7WlLOHH.js +0 -4
- package/dist/client/assets/shellscript-BPygS74z.js +0 -2
- package/dist/client/assets/shellscript-UjHQny9Z.js +0 -2
- package/dist/client/assets/shellsession-CDps1aZr.js +0 -4
- package/dist/client/assets/slack-dark-BrTliQeh.js +0 -2
- package/dist/client/assets/slack-ochin-CNWqQ7tu.js +0 -2
- package/dist/client/assets/smalltalk-BR3Rc3FQ.js +0 -2
- package/dist/client/assets/snazzy-light-BzYZd1LT.js +0 -2
- package/dist/client/assets/solarized-dark-C2rK4QQr.js +0 -2
- package/dist/client/assets/solarized-light-C2OUqawP.js +0 -2
- package/dist/client/assets/solidity-CEMyxnr-.js +0 -2
- package/dist/client/assets/soy-C2BR-KXG.js +0 -4
- package/dist/client/assets/sparql-BR-folLj.js +0 -4
- package/dist/client/assets/splunk-DltYhNgS.js +0 -2
- package/dist/client/assets/sql-Cq-9x9KN.js +0 -2
- package/dist/client/assets/sql-VyWTpbEC.js +0 -2
- package/dist/client/assets/ssh-config-DcyfvJBR.js +0 -2
- package/dist/client/assets/stata-BqtFlCjT.js +0 -4
- package/dist/client/assets/stylus-q8BlsKAR.js +0 -2
- package/dist/client/assets/svelte-syJa_SYv.js +0 -13
- package/dist/client/assets/swift-CXYuMLSq.js +0 -2
- package/dist/client/assets/synthwave-84-CTJAFRYu.js +0 -2
- package/dist/client/assets/system-verilog-ByZAK7jW.js +0 -2
- package/dist/client/assets/systemd-B-jaYYZt.js +0 -2
- package/dist/client/assets/talonscript-dVxmeTiZ.js +0 -2
- package/dist/client/assets/tasl-Dbo9haLk.js +0 -2
- package/dist/client/assets/tcl-TCXACfIo.js +0 -2
- package/dist/client/assets/templ-Bn7XEiq4.js +0 -11
- package/dist/client/assets/terraform-u83YSQWU.js +0 -2
- package/dist/client/assets/tex-A3DsLgBt.js +0 -4
- package/dist/client/assets/tokyo-night-BYFrqgEu.js +0 -2
- package/dist/client/assets/toml-CdHGk6CD.js +0 -2
- package/dist/client/assets/ts-tags-DS6YXVjU.js +0 -47
- package/dist/client/assets/tsv-CG8xqyDf.js +0 -2
- package/dist/client/assets/tsx-CtGp4mPG.js +0 -2
- package/dist/client/assets/tsx-D8ovzp-F.js +0 -2
- package/dist/client/assets/turtle-BR5cmI8X.js +0 -2
- package/dist/client/assets/twig-Bs73rqSt.js +0 -17
- package/dist/client/assets/typescript-8wVgZc72.js +0 -2
- package/dist/client/assets/typespec-1Ft3a0J2.js +0 -2
- package/dist/client/assets/typst-Bxkj5bxp.js +0 -2
- package/dist/client/assets/v-Iwj7EXF-.js +0 -2
- package/dist/client/assets/vala-CZEJpdXE.js +0 -2
- package/dist/client/assets/vb-DGJXK2m2.js +0 -2
- package/dist/client/assets/verilog-B0z1nW7x.js +0 -2
- package/dist/client/assets/vesper-DCEWjXOj.js +0 -2
- package/dist/client/assets/vhdl-D-H8gNu8.js +0 -2
- package/dist/client/assets/viml-BhuF_Zk_.js +0 -2
- package/dist/client/assets/vitesse-black-DvuZKsyv.js +0 -2
- package/dist/client/assets/vue-D0gzEcGR.js +0 -26
- package/dist/client/assets/vue-html-DZ7mz5hW.js +0 -9
- package/dist/client/assets/vue-vine-OrllDKqt.js +0 -19
- package/dist/client/assets/vyper-BrlMwrCX.js +0 -2
- package/dist/client/assets/wasm-DowiYQZZ.js +0 -2
- package/dist/client/assets/wenyan-CTq8bTW-.js +0 -2
- package/dist/client/assets/wgsl-CXboDnow.js +0 -2
- package/dist/client/assets/wikitext-B149YDlp.js +0 -2
- package/dist/client/assets/wit-ZgolYj9k.js +0 -2
- package/dist/client/assets/wolfram-DgdmCw3o.js +0 -2
- package/dist/client/assets/xml-B_PekTMZ.js +0 -4
- package/dist/client/assets/xml-Bs5cbwVm.js +0 -2
- package/dist/client/assets/xsl-BGwkM6bU.js +0 -4
- package/dist/client/assets/yaml-5uKUGHTB.js +0 -2
- package/dist/client/assets/yaml-Df3gGNfW.js +0 -2
- package/dist/client/assets/zenscript-Cb7SjAom.js +0 -2
- package/dist/client/assets/zig-EXcHINRa.js +0 -2
- package/dist/dirs.js +0 -3
- package/dist/index.d.ts +0 -21
- package/dist/index.js +0 -592
- package/dist/panel/index.js +0 -3852
- /package/dist/client/assets/{css-C4ljcP-J.js → css-DbgpCiOq.js} +0 -0
- /package/dist/client/assets/{javascript-0fjGG2XX.js → javascript-QnJdw8u5.js} +0 -0
- /package/dist/client/assets/{json-txB7NGmR.js → json-CzPoZe0r.js} +0 -0
- /package/dist/client/assets/{typescript-BiIPZBKd.js → typescript-BEfC8S_c.js} +0 -0
- /package/dist/client/assets/{vitesse-dark-CQ-7ZO72.js → vitesse-dark-Bz6RXfkV.js} +0 -0
- /package/dist/client/assets/{vitesse-light-BnsOKvoJ.js → vitesse-light-NvDlvJl2.js} +0 -0
- /package/dist/client/assets/{wasm-CDzXCmZQ.js → wasm-D0Echd05.js} +0 -0
- /package/dist/{constants.js → constants.mjs} +0 -0
- /package/dist/{dirs.d.ts → dirs.d.mts} +0 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2508 @@
|
|
|
1
|
+
import { DIR_CLIENT } from "./dirs.mjs";
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
3
|
+
import sirv from "sirv";
|
|
4
|
+
import { basename, dirname, extname, join, relative } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
7
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
9
|
+
import { arch, platform, release } from "node:os";
|
|
10
|
+
import process from "node:process";
|
|
11
|
+
import { Lang, parse } from "@ast-grep/napi";
|
|
12
|
+
|
|
13
|
+
//#region src/server/controller/api.ts
|
|
14
|
+
const REQUEST_MODULE_EXTENSIONS = [
|
|
15
|
+
".ts",
|
|
16
|
+
".mts",
|
|
17
|
+
".cts",
|
|
18
|
+
".tsx",
|
|
19
|
+
".js",
|
|
20
|
+
".mjs",
|
|
21
|
+
".cjs",
|
|
22
|
+
".jsx"
|
|
23
|
+
];
|
|
24
|
+
function escapeRegExp(value) {
|
|
25
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26
|
+
}
|
|
27
|
+
function normalizePath(filePath) {
|
|
28
|
+
return filePath.replace(/\\/g, "/");
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 构造别名解析器集合。
|
|
32
|
+
*
|
|
33
|
+
* 支持以下映射:
|
|
34
|
+
* - '@/x'、'~/x' → <root>/src/x
|
|
35
|
+
* - tsconfig.json 中 compilerOptions.paths 定义的别名(含 * 通配符)
|
|
36
|
+
*
|
|
37
|
+
* 实现采用正则表驱动:传入 specifier 后逐条匹配并映射为绝对路径字符串。
|
|
38
|
+
*/
|
|
39
|
+
function createAliasResolver(root) {
|
|
40
|
+
const entries = [{
|
|
41
|
+
match: /^@\/(.+)$/,
|
|
42
|
+
map: (match) => join(root, "src", match[1])
|
|
43
|
+
}, {
|
|
44
|
+
match: /^~\/(.+)$/,
|
|
45
|
+
map: (match) => join(root, "src", match[1])
|
|
46
|
+
}];
|
|
47
|
+
const tsconfigCandidates = [join(root, "tsconfig.json"), join(root, "tsconfig.app.json")];
|
|
48
|
+
for (const configPath of tsconfigCandidates) {
|
|
49
|
+
if (!existsSync(configPath)) continue;
|
|
50
|
+
try {
|
|
51
|
+
const compilerOptions = JSON.parse(readFileSync(configPath, "utf-8"))?.compilerOptions ?? {};
|
|
52
|
+
const baseUrl = compilerOptions.baseUrl ? join(root, compilerOptions.baseUrl) : root;
|
|
53
|
+
const paths = compilerOptions.paths;
|
|
54
|
+
if (!paths || typeof paths !== "object") continue;
|
|
55
|
+
for (const [key, values] of Object.entries(paths)) {
|
|
56
|
+
const targets = Array.isArray(values) ? values : [values];
|
|
57
|
+
const hasWildcard = key.includes("*");
|
|
58
|
+
const pattern = new RegExp(`^${escapeRegExp(key).replace(/\\\*/g, "(.+)")}$`);
|
|
59
|
+
targets.forEach((targetPath) => {
|
|
60
|
+
if (typeof targetPath !== "string") return;
|
|
61
|
+
const normalizedTarget = targetPath.replace(/\\/g, "/");
|
|
62
|
+
entries.push({
|
|
63
|
+
match: pattern,
|
|
64
|
+
map: (match) => {
|
|
65
|
+
if (hasWildcard) {
|
|
66
|
+
const wildcard = match[1] ?? "";
|
|
67
|
+
return join(baseUrl, normalizedTarget.replace(/\*/g, wildcard));
|
|
68
|
+
}
|
|
69
|
+
return join(baseUrl, normalizedTarget);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
} catch {}
|
|
75
|
+
}
|
|
76
|
+
return entries;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 在若干后缀(ts/tsx/js/...)与 index 文件中尝试解析具体文件。
|
|
80
|
+
*
|
|
81
|
+
* - candidate 可能未带扩展名,函数会尝试附加扩展与目录下 index.xxx。
|
|
82
|
+
* - 仅返回存在且确认为文件的路径;失败返回 undefined。
|
|
83
|
+
*/
|
|
84
|
+
function resolveWithExtensions(candidate) {
|
|
85
|
+
const candidates = [candidate];
|
|
86
|
+
if (!/\.[^/\\]+$/.test(candidate)) REQUEST_MODULE_EXTENSIONS.forEach((ext) => {
|
|
87
|
+
candidates.push(`${candidate}${ext}`);
|
|
88
|
+
candidates.push(join(candidate, `index${ext}`));
|
|
89
|
+
});
|
|
90
|
+
for (const file of candidates) if (existsSync(file)) try {
|
|
91
|
+
if (statSync(file).isFile()) return file;
|
|
92
|
+
} catch {}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 依据导入路径与导入者位置,解析出相对于项目根目录的模块文件路径。
|
|
96
|
+
*
|
|
97
|
+
* 支持:
|
|
98
|
+
* - 相对路径(./x、../x)
|
|
99
|
+
* - 绝对路径(/x → <root>/x)
|
|
100
|
+
* - 别名(来自 createAliasResolver 的 entries)
|
|
101
|
+
* - 退化支持 '@/x' 或 '~/x' 当 paths 未配置但文件位于 <root>/src 下
|
|
102
|
+
*/
|
|
103
|
+
function resolveRequestModulePath(specifier, importerPath, root, aliasEntries) {
|
|
104
|
+
const importerDir = dirname(importerPath);
|
|
105
|
+
let absoluteCandidate;
|
|
106
|
+
if (specifier.startsWith(".")) absoluteCandidate = join(importerDir, specifier);
|
|
107
|
+
else if (specifier.startsWith("/")) absoluteCandidate = join(root, specifier.replace(/^\//, ""));
|
|
108
|
+
else for (const entry of aliasEntries) {
|
|
109
|
+
const match = entry.match.exec(specifier);
|
|
110
|
+
if (match) {
|
|
111
|
+
absoluteCandidate = entry.map(match);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!absoluteCandidate && (specifier.startsWith("@/") || specifier.startsWith("~/"))) {
|
|
116
|
+
const markerIndex = normalizePath(importerPath).lastIndexOf("/src/");
|
|
117
|
+
if (markerIndex !== -1) absoluteCandidate = join(importerPath.slice(0, markerIndex), "src", specifier.slice(2));
|
|
118
|
+
}
|
|
119
|
+
if (!absoluteCandidate) return;
|
|
120
|
+
const resolved = resolveWithExtensions(absoluteCandidate);
|
|
121
|
+
if (!resolved) return;
|
|
122
|
+
return normalizePath(relative(root, resolved));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 查找项目根目录的 OpenAPI 配置文件。
|
|
126
|
+
*
|
|
127
|
+
* 约定优于配置:默认仅查找根目录下的 `openapi.config.ts`。
|
|
128
|
+
* 如需扩展为支持更多命名/位置,可在此集中改造,避免影响调用方。
|
|
129
|
+
*
|
|
130
|
+
* @param root 项目根目录的绝对路径(即 Vite server.config.root)
|
|
131
|
+
* @returns 如果存在则返回绝对路径,否则返回 null
|
|
132
|
+
*/
|
|
133
|
+
async function findOpenapiConfig(root) {
|
|
134
|
+
const configPath = join(root, "openapi.config.ts");
|
|
135
|
+
if (existsSync(configPath)) return configPath;
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 动态加载 OpenAPI 配置文件。
|
|
140
|
+
*
|
|
141
|
+
* 之所以使用动态 import 而非 require:
|
|
142
|
+
* - 支持 ESM 项目
|
|
143
|
+
* - 避免被打包时静态分析(保持运行时可替换)
|
|
144
|
+
*
|
|
145
|
+
* 注意:此函数在 devtools 插件的 Vite 环境中执行,允许使用 file:// URL。
|
|
146
|
+
*
|
|
147
|
+
* @param configPath 配置文件的绝对路径
|
|
148
|
+
* @returns 解析后的配置对象(优先使用 module.default),失败时返回 null
|
|
149
|
+
*/
|
|
150
|
+
async function loadOpenapiConfig(configPath) {
|
|
151
|
+
try {
|
|
152
|
+
const fileUrl = pathToFileURL(configPath).href;
|
|
153
|
+
const extension = extname(configPath);
|
|
154
|
+
if (extension === ".ts" || extension === ".mts" || extension === ".cts") {
|
|
155
|
+
const { loadConfig } = await import("unconfig");
|
|
156
|
+
const result = await loadConfig({
|
|
157
|
+
cwd: dirname(configPath),
|
|
158
|
+
sources: [{
|
|
159
|
+
files: basename(configPath, extension),
|
|
160
|
+
extensions: [extension.slice(1)]
|
|
161
|
+
}],
|
|
162
|
+
merge: false
|
|
163
|
+
});
|
|
164
|
+
if (typeof result?.config === "undefined") return null;
|
|
165
|
+
return await Promise.resolve(result.config);
|
|
166
|
+
} else {
|
|
167
|
+
const module = await import(fileUrl);
|
|
168
|
+
return module.default || module;
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error(`加载配置文件失败: ${error}`);
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 从 TypeScript 源文件内容中提取导出的 API 函数的元信息。
|
|
177
|
+
*
|
|
178
|
+
* 约定:
|
|
179
|
+
* - 仅识别 `export function foo(...)` 的导出形式;若有 `const foo = () => {}` 请改为函数导出或扩展正则。
|
|
180
|
+
* - 通过 JSDoc 约定读取三类元数据:
|
|
181
|
+
* - @description 文本说明
|
|
182
|
+
* - @url 接口路径(例如 /user/list)
|
|
183
|
+
* - @method 请求方法(GET/POST/...)
|
|
184
|
+
* - 通过参数签名解析类型信息:
|
|
185
|
+
* - params: API.Xxx → 视为 query
|
|
186
|
+
* - body: API.Xxx → 视为 body
|
|
187
|
+
* - 泛型参数中的 `R = API.Xxx` → 视为返回类型
|
|
188
|
+
*
|
|
189
|
+
* 限制与注意:
|
|
190
|
+
* - 该实现基于正则进行快速解析,无法覆盖所有 TS 语法边界;遇到复杂写法可改为 AST(ts-morph / @typescript-eslint/typescript-estree)。
|
|
191
|
+
* - 若同一文件存在多种导出形式,请保持上述约定以获得稳定结果。
|
|
192
|
+
*
|
|
193
|
+
* @param content 源文件文本
|
|
194
|
+
* @param filePath 相对于项目根目录的文件相对路径(用于展示)
|
|
195
|
+
* @param requestClient 若源码中以 `import { foo as request }` 形式命名的请求实例名,用于偏好匹配应用端实例
|
|
196
|
+
* @returns 解析得到的 API 函数列表
|
|
197
|
+
*/
|
|
198
|
+
function extractApiFunctions(content, filePath, requestClient, requestModulePath, requestModuleResolvedPath) {
|
|
199
|
+
const functions = [];
|
|
200
|
+
const functionRegex = /\/\*\*\s*\n([^*]*(?:\*(?!\/)[^*]*)*)\*\/\s*export\s+function\s+(\w+)[^(]*\(([^)]*)\)/g;
|
|
201
|
+
let match = functionRegex.exec(content);
|
|
202
|
+
while (match !== null) {
|
|
203
|
+
const [, jsdoc, functionName, params] = match;
|
|
204
|
+
const descMatch = /@description\s+(.+)/.exec(jsdoc);
|
|
205
|
+
const description = descMatch ? descMatch[1].trim() : void 0;
|
|
206
|
+
const urlMatch = /@url\s+(.+)/.exec(jsdoc);
|
|
207
|
+
const url = urlMatch ? urlMatch[1].trim() : "";
|
|
208
|
+
const methodMatch = /@method\s+(.+)/.exec(jsdoc);
|
|
209
|
+
const method = methodMatch ? methodMatch[1].trim() : "UNKNOWN";
|
|
210
|
+
let paramType;
|
|
211
|
+
let paramLocation;
|
|
212
|
+
const paramsMatch = /params:\s*(API\.\w+)/.exec(params);
|
|
213
|
+
const bodyMatch = /body:\s*(API\.\w+)/.exec(params);
|
|
214
|
+
if (paramsMatch) {
|
|
215
|
+
paramType = paramsMatch[1];
|
|
216
|
+
paramLocation = "query";
|
|
217
|
+
} else if (bodyMatch) {
|
|
218
|
+
paramType = bodyMatch[1];
|
|
219
|
+
paramLocation = "body";
|
|
220
|
+
}
|
|
221
|
+
let returnType;
|
|
222
|
+
const returnTypeMatch = /R\s*=\s*(API\.\w+)/.exec(params);
|
|
223
|
+
if (returnTypeMatch) returnType = returnTypeMatch[1];
|
|
224
|
+
functions.push({
|
|
225
|
+
name: functionName,
|
|
226
|
+
description,
|
|
227
|
+
url,
|
|
228
|
+
method,
|
|
229
|
+
file: filePath,
|
|
230
|
+
paramType,
|
|
231
|
+
paramLocation,
|
|
232
|
+
returnType,
|
|
233
|
+
requestClient,
|
|
234
|
+
requestModulePath,
|
|
235
|
+
requestModuleResolvedPath
|
|
236
|
+
});
|
|
237
|
+
match = functionRegex.exec(content);
|
|
238
|
+
}
|
|
239
|
+
return functions;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* 将 TypeScript 类型(字面量对象类型)解析为简化的 JSON Schema 片段。
|
|
243
|
+
*
|
|
244
|
+
* 目前仅对如下简单场景做支持:
|
|
245
|
+
* - `type Xxx = { foo: string; bar?: number }`
|
|
246
|
+
* - 行内 JSDoc `/** desc *\/` 写在属性前可作为 description。
|
|
247
|
+
* - 属性类型仅做基础映射(string/number/boolean/Record/any/数组)
|
|
248
|
+
*
|
|
249
|
+
* 不支持/暂不支持:
|
|
250
|
+
* - 嵌套类型、联合/交叉、索引类型、条件类型、接口继承等复杂语法。
|
|
251
|
+
* - interface 的 body 解析(可按需扩展正则)。
|
|
252
|
+
*
|
|
253
|
+
* 若需要更完整能力,建议切换到 TS AST 或 json-schema-generator 工具链。
|
|
254
|
+
*
|
|
255
|
+
* @param content typings.d.ts 文件全文
|
|
256
|
+
* @param typeName 形如 `API.UserQuery` 的带命名空间类型名
|
|
257
|
+
* @returns 简化的 JSON Schema 对象;无法解析时返回 null
|
|
258
|
+
*/
|
|
259
|
+
function parseTypeToSchema(content, typeName) {
|
|
260
|
+
try {
|
|
261
|
+
const match = new RegExp(`type\\s+${typeName.split(".").pop()}\\s*=\\s*\\{([^}]*)\\}`, "s").exec(content);
|
|
262
|
+
if (!match) return null;
|
|
263
|
+
const typeBody = match[1];
|
|
264
|
+
const properties = {};
|
|
265
|
+
const required = [];
|
|
266
|
+
const lines = typeBody.split("\n");
|
|
267
|
+
for (const line of lines) {
|
|
268
|
+
const trimmed = line.trim();
|
|
269
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*")) continue;
|
|
270
|
+
const propWithComment = /^\/\*\*\s*(.+?)\s*\*\/\s*(\w+)(\??):\s*(.+?);?$/.exec(trimmed);
|
|
271
|
+
const propWithoutComment = /^(\w+)(\??):\s*(.+?);?$/.exec(trimmed);
|
|
272
|
+
if (propWithComment) {
|
|
273
|
+
const [, description, propertyName, optional, propertyType] = propWithComment;
|
|
274
|
+
const isOptional = optional === "?";
|
|
275
|
+
const prop = { type: mapTsTypeToJsonType(propertyType) };
|
|
276
|
+
if (description) prop.description = description;
|
|
277
|
+
properties[propertyName] = prop;
|
|
278
|
+
if (!isOptional) required.push(propertyName);
|
|
279
|
+
} else if (propWithoutComment) {
|
|
280
|
+
const [, propertyName, optional, propertyType] = propWithoutComment;
|
|
281
|
+
const isOptional = optional === "?";
|
|
282
|
+
properties[propertyName] = { type: mapTsTypeToJsonType(propertyType) };
|
|
283
|
+
if (!isOptional) required.push(propertyName);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
type: "object",
|
|
288
|
+
properties,
|
|
289
|
+
required: required.length > 0 ? required : void 0
|
|
290
|
+
};
|
|
291
|
+
} catch (error) {
|
|
292
|
+
console.error(`解析类型 ${typeName} 失败:`, error);
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* 将常见的 TypeScript 基础类型映射为 JSON Schema 的 `type`。
|
|
298
|
+
*
|
|
299
|
+
* @param tsType TypeScript 类型字面量(如 'string' | 'number[]' | 'Record<string, any>')
|
|
300
|
+
* @returns JSON Schema 的基础类型字符串
|
|
301
|
+
*/
|
|
302
|
+
function mapTsTypeToJsonType(tsType) {
|
|
303
|
+
const type = tsType.trim();
|
|
304
|
+
if (type === "string") return "string";
|
|
305
|
+
if (type === "number") return "number";
|
|
306
|
+
if (type === "boolean") return "boolean";
|
|
307
|
+
if (type === "any" || type.startsWith("Record<")) return "object";
|
|
308
|
+
if (type.endsWith("[]")) return "array";
|
|
309
|
+
return "string";
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* 从 `typings.d.ts` 中提取类型清单(仅定位名称,不展开定义)。
|
|
313
|
+
*
|
|
314
|
+
* 约定:
|
|
315
|
+
* - 在 `declare namespace Xxx { ... }` 范围内提取 `type` 与 `interface` 的标识符。
|
|
316
|
+
* - 仅收集行首的声明行,记录其名称与文件路径,便于 UI 展示与 schema 进一步解析。
|
|
317
|
+
*
|
|
318
|
+
* @param content typings.d.ts 文件全文
|
|
319
|
+
* @param filePath 相对项目根的文件路径
|
|
320
|
+
* @returns 类型条目列表
|
|
321
|
+
*/
|
|
322
|
+
function extractTypes(content, filePath) {
|
|
323
|
+
const types = [];
|
|
324
|
+
const lines = content.split("\n");
|
|
325
|
+
let currentNamespace = "";
|
|
326
|
+
let inNamespace = false;
|
|
327
|
+
let braceCount = 0;
|
|
328
|
+
for (const line of lines) {
|
|
329
|
+
const namespaceMatch = /declare\s+namespace\s+(\w+)/.exec(line);
|
|
330
|
+
if (namespaceMatch) {
|
|
331
|
+
currentNamespace = namespaceMatch[1];
|
|
332
|
+
inNamespace = true;
|
|
333
|
+
braceCount = 0;
|
|
334
|
+
}
|
|
335
|
+
if (inNamespace) {
|
|
336
|
+
braceCount += (line.match(/\{/g) || []).length;
|
|
337
|
+
braceCount -= (line.match(/\}/g) || []).length;
|
|
338
|
+
if (braceCount === 0 && line.includes("}")) inNamespace = false;
|
|
339
|
+
}
|
|
340
|
+
if (inNamespace && currentNamespace) {
|
|
341
|
+
const typeMatch = /^\s*(type|interface)\s+(\w+)/.exec(line);
|
|
342
|
+
if (typeMatch) {
|
|
343
|
+
const typeName = typeMatch[2];
|
|
344
|
+
types.push({
|
|
345
|
+
name: `${currentNamespace}.${typeName}`,
|
|
346
|
+
file: filePath,
|
|
347
|
+
content: line.trim()
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return types;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* 递归扫描给定目录以收集 API 函数与类型定义。
|
|
356
|
+
*
|
|
357
|
+
* 扫描规则:
|
|
358
|
+
* - 识别 `typings.d.ts` → 提取类型并尝试为同目录的 API 函数生成参数/返回 schema。
|
|
359
|
+
* - 识别 `*.ts`(排除 index.ts)→ 通过 JSDoc 与参数签名提取 API 函数信息。
|
|
360
|
+
* - 递归子目录以形成模块(module.path 为相对目录)。
|
|
361
|
+
*
|
|
362
|
+
* @param dir 要扫描的目录绝对路径(通常来自配置的 output)
|
|
363
|
+
* @param root 项目根目录绝对路径,用于计算相对路径与分组
|
|
364
|
+
* @returns 模块列表,每个模块包含 functions 与 types
|
|
365
|
+
*/
|
|
366
|
+
async function scanApiDirectory(dir, root, aliasEntries) {
|
|
367
|
+
const modules = [];
|
|
368
|
+
if (!existsSync(dir)) return modules;
|
|
369
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
370
|
+
let typingsContent = null;
|
|
371
|
+
for (const entry of entries) if (entry.isFile() && entry.name === "typings.d.ts") {
|
|
372
|
+
typingsContent = readFileSync(join(dir, entry.name), "utf-8");
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
for (const entry of entries) {
|
|
376
|
+
const fullPath = join(dir, entry.name);
|
|
377
|
+
if (entry.isDirectory()) {
|
|
378
|
+
const subModules = await scanApiDirectory(fullPath, root, aliasEntries);
|
|
379
|
+
modules.push(...subModules);
|
|
380
|
+
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
381
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
382
|
+
const relativePath = relative(root, fullPath);
|
|
383
|
+
const requestImportMatch = /import\s*\{\s*([\w$]+)\s+as\s+request\s*\}\s*from\s*['"]([^'"]+)['"]/.exec(content);
|
|
384
|
+
const requestClient = requestImportMatch ? requestImportMatch[1] : void 0;
|
|
385
|
+
const requestModulePath = requestImportMatch ? requestImportMatch[2] : void 0;
|
|
386
|
+
const requestModuleResolvedPath = requestModulePath ? resolveRequestModulePath(requestModulePath, fullPath, root, aliasEntries) : void 0;
|
|
387
|
+
if (entry.name === "typings.d.ts") {
|
|
388
|
+
const types = extractTypes(content, relativePath);
|
|
389
|
+
if (types.length > 0) {
|
|
390
|
+
const moduleDir = relative(root, dir);
|
|
391
|
+
let module = modules.find((m) => m.path === moduleDir);
|
|
392
|
+
if (!module) {
|
|
393
|
+
module = {
|
|
394
|
+
path: moduleDir,
|
|
395
|
+
functions: [],
|
|
396
|
+
types: []
|
|
397
|
+
};
|
|
398
|
+
modules.push(module);
|
|
399
|
+
}
|
|
400
|
+
module.types.push(...types);
|
|
401
|
+
}
|
|
402
|
+
} else if (entry.name !== "index.ts") {
|
|
403
|
+
const functions = extractApiFunctions(content, relativePath, requestClient, requestModulePath, requestModuleResolvedPath);
|
|
404
|
+
if (functions.length > 0) {
|
|
405
|
+
const moduleDir = relative(root, dir);
|
|
406
|
+
let module = modules.find((m) => m.path === moduleDir);
|
|
407
|
+
if (!module) {
|
|
408
|
+
module = {
|
|
409
|
+
path: moduleDir,
|
|
410
|
+
functions: [],
|
|
411
|
+
types: []
|
|
412
|
+
};
|
|
413
|
+
modules.push(module);
|
|
414
|
+
}
|
|
415
|
+
if (typingsContent) for (const func of functions) {
|
|
416
|
+
if (func.paramType) {
|
|
417
|
+
const schema = parseTypeToSchema(typingsContent, func.paramType);
|
|
418
|
+
if (schema) func.paramSchema = schema;
|
|
419
|
+
}
|
|
420
|
+
if (func.returnType) {
|
|
421
|
+
const schema = parseTypeToSchema(typingsContent, func.returnType);
|
|
422
|
+
if (schema) func.returnSchema = schema;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
module.functions.push(...functions);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return modules;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* 根据 OpenAPI 配置计算需要扫描的 API 目录集合。
|
|
434
|
+
*
|
|
435
|
+
* 兼容策略:
|
|
436
|
+
* - 当存在 `batch` 时收集每项的 `output`。
|
|
437
|
+
* - 否则尝试使用顶层 `output`。
|
|
438
|
+
* - 以上都不存在时,回退到默认 `src/api` 目录。
|
|
439
|
+
*
|
|
440
|
+
* 注意:配置中的相对路径会被解析到项目根目录下。
|
|
441
|
+
*
|
|
442
|
+
* @param config 解析后的 OpenAPI 配置对象(可能为 null)
|
|
443
|
+
* @param root 项目根目录绝对路径
|
|
444
|
+
* @returns 需要扫描的目录绝对路径数组(去重后)
|
|
445
|
+
*/
|
|
446
|
+
function extractApiDirectories(config, root) {
|
|
447
|
+
const directories = /* @__PURE__ */ new Set();
|
|
448
|
+
if (!config) {
|
|
449
|
+
directories.add(join(root, "src", "api"));
|
|
450
|
+
return Array.from(directories);
|
|
451
|
+
}
|
|
452
|
+
if (config.batch && Array.isArray(config.batch)) {
|
|
453
|
+
for (const item of config.batch) if (item.output) {
|
|
454
|
+
const fullPath = join(root, item.output.replace(/^\.\//, ""));
|
|
455
|
+
directories.add(fullPath);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (config.output) {
|
|
459
|
+
const fullPath = join(root, config.output.replace(/^\.\//, ""));
|
|
460
|
+
directories.add(fullPath);
|
|
461
|
+
}
|
|
462
|
+
if (directories.size === 0) directories.add(join(root, "src", "api"));
|
|
463
|
+
return Array.from(directories);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* 扫描项目并返回 API 概览与配置快照。
|
|
467
|
+
*
|
|
468
|
+
* 执行流程:
|
|
469
|
+
* 1) 在项目根查找并加载 `openapi.config.ts`(若存在)。
|
|
470
|
+
* 2) 基于配置解析出需要扫描的目录(支持 batch/output)。
|
|
471
|
+
* 3) 对每个目录进行递归扫描,收集 API 函数与类型信息。
|
|
472
|
+
*
|
|
473
|
+
* @param root 项目根目录绝对路径
|
|
474
|
+
* @returns {ApiScanResult} 包含配置(可能为 null)与所有模块的扫描结果
|
|
475
|
+
*/
|
|
476
|
+
async function getRequestList(root) {
|
|
477
|
+
const configPath = await findOpenapiConfig(root);
|
|
478
|
+
let config = null;
|
|
479
|
+
if (configPath) config = await loadOpenapiConfig(configPath);
|
|
480
|
+
const apiDirectories = extractApiDirectories(config, root);
|
|
481
|
+
const aliasEntries = createAliasResolver(root);
|
|
482
|
+
const allModules = [];
|
|
483
|
+
for (const apiDir of apiDirectories) {
|
|
484
|
+
const modules = await scanApiDirectory(apiDir, root, aliasEntries);
|
|
485
|
+
allModules.push(...modules);
|
|
486
|
+
}
|
|
487
|
+
return {
|
|
488
|
+
config,
|
|
489
|
+
modules: allModules
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region src/server/controller/issue.ts
|
|
495
|
+
/**
|
|
496
|
+
* 安全执行 git 命令,失败时返回 null
|
|
497
|
+
*/
|
|
498
|
+
function execGit(command, cwd) {
|
|
499
|
+
try {
|
|
500
|
+
return execSync(command, {
|
|
501
|
+
cwd,
|
|
502
|
+
encoding: "utf-8",
|
|
503
|
+
timeout: 5e3
|
|
504
|
+
}).trim() || null;
|
|
505
|
+
} catch {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* 采集 Git 信息
|
|
511
|
+
*/
|
|
512
|
+
function collectGitInfo(root) {
|
|
513
|
+
return {
|
|
514
|
+
userName: execGit("git config user.name", root),
|
|
515
|
+
userEmail: execGit("git config user.email", root),
|
|
516
|
+
branch: execGit("git rev-parse --abbrev-ref HEAD", root),
|
|
517
|
+
commitHash: execGit("git rev-parse --short HEAD", root),
|
|
518
|
+
remoteUrl: execGit("git remote get-url origin", root)
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* 读取项目 package.json
|
|
523
|
+
*/
|
|
524
|
+
function collectPackageJson(root) {
|
|
525
|
+
try {
|
|
526
|
+
const pkgPath = join(root, "package.json");
|
|
527
|
+
if (!existsSync(pkgPath)) return {};
|
|
528
|
+
return JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
529
|
+
} catch {
|
|
530
|
+
return {};
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* 采集完整的环境信息
|
|
535
|
+
*/
|
|
536
|
+
function getEnvironmentInfo(root) {
|
|
537
|
+
return {
|
|
538
|
+
nodeVersion: process.version,
|
|
539
|
+
platform: platform(),
|
|
540
|
+
arch: arch(),
|
|
541
|
+
osVersion: release(),
|
|
542
|
+
packageJson: collectPackageJson(root),
|
|
543
|
+
git: collectGitInfo(root)
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* 将环境信息格式化为 Markdown 文本,用于 Issue body
|
|
548
|
+
*/
|
|
549
|
+
function formatEnvironmentMarkdown(env) {
|
|
550
|
+
const envObj = {
|
|
551
|
+
node: env.nodeVersion,
|
|
552
|
+
os: `${env.platform} ${env.arch} (${env.osVersion})`,
|
|
553
|
+
git: {
|
|
554
|
+
branch: env.git.branch,
|
|
555
|
+
commit: env.git.commitHash,
|
|
556
|
+
user: env.git.userName ? `${env.git.userName}${env.git.userEmail ? ` <${env.git.userEmail}>` : ""}` : null,
|
|
557
|
+
remote: env.git.remoteUrl
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
const lines = [];
|
|
561
|
+
lines.push(`### 环境信息`);
|
|
562
|
+
lines.push("");
|
|
563
|
+
lines.push("```json");
|
|
564
|
+
lines.push(JSON.stringify(envObj, null, 2));
|
|
565
|
+
lines.push("```");
|
|
566
|
+
lines.push("");
|
|
567
|
+
lines.push(`### package.json`);
|
|
568
|
+
lines.push("");
|
|
569
|
+
lines.push("```json");
|
|
570
|
+
lines.push(JSON.stringify(env.packageJson, null, 2));
|
|
571
|
+
lines.push("```");
|
|
572
|
+
return lines.join("\n");
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* 将表单数据格式化为 Issue body(Markdown)
|
|
576
|
+
*/
|
|
577
|
+
function formatIssueBody(body, environment) {
|
|
578
|
+
const sections = [];
|
|
579
|
+
sections.push(`## 问题描述`);
|
|
580
|
+
sections.push(body.description || "_未填写_");
|
|
581
|
+
sections.push("");
|
|
582
|
+
if (body.steps) {
|
|
583
|
+
sections.push(`## 复现步骤`);
|
|
584
|
+
sections.push(body.steps);
|
|
585
|
+
sections.push("");
|
|
586
|
+
}
|
|
587
|
+
if (body.expected) {
|
|
588
|
+
sections.push(`## 期望行为`);
|
|
589
|
+
sections.push(body.expected);
|
|
590
|
+
sections.push("");
|
|
591
|
+
}
|
|
592
|
+
if (body.actual) {
|
|
593
|
+
sections.push(`## 实际行为`);
|
|
594
|
+
sections.push(body.actual);
|
|
595
|
+
sections.push("");
|
|
596
|
+
}
|
|
597
|
+
sections.push(formatEnvironmentMarkdown(environment));
|
|
598
|
+
return sections.join("\n");
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* 处理 Issue 提交,采集环境信息后转发到 issue.elonehoo.cn
|
|
602
|
+
*/
|
|
603
|
+
async function submitIssue(root, body) {
|
|
604
|
+
const issueBody = formatIssueBody(body, getEnvironmentInfo(root));
|
|
605
|
+
const response = await fetch("https://issue.elonehoo.cn/issue", {
|
|
606
|
+
method: "POST",
|
|
607
|
+
headers: { "Content-Type": "application/json" },
|
|
608
|
+
body: JSON.stringify({
|
|
609
|
+
title: `[Bug] ${body.title}`,
|
|
610
|
+
body: issueBody
|
|
611
|
+
})
|
|
612
|
+
});
|
|
613
|
+
if (!response.ok) throw new Error(`Issue API responded with ${response.status}: ${response.statusText}`);
|
|
614
|
+
const result = await response.json();
|
|
615
|
+
if (!result.success) throw new Error("Issue API returned success: false");
|
|
616
|
+
return result;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
//#endregion
|
|
620
|
+
//#region src/@data/pubinfo.ts
|
|
621
|
+
const pubinfoImport = [
|
|
622
|
+
{
|
|
623
|
+
type: "import",
|
|
624
|
+
name: "createPubinfoProvider",
|
|
625
|
+
package: "pubinfo-pr",
|
|
626
|
+
meta: { category: "provider" }
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
type: "import",
|
|
630
|
+
name: "pubinfoInjectionKey",
|
|
631
|
+
package: "pubinfo-pr",
|
|
632
|
+
meta: { category: "provider" }
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
type: "import",
|
|
636
|
+
name: "createPubinfoContext",
|
|
637
|
+
package: "pubinfo-pr",
|
|
638
|
+
meta: { category: "context" }
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
type: "import",
|
|
642
|
+
name: "usePubinfoContext",
|
|
643
|
+
package: "pubinfo-pr",
|
|
644
|
+
meta: { category: "context" }
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
type: "import",
|
|
648
|
+
name: "createContext",
|
|
649
|
+
package: "pubinfo-pr",
|
|
650
|
+
meta: { category: "context" }
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
type: "import",
|
|
654
|
+
name: "createRawContext",
|
|
655
|
+
package: "pubinfo-pr",
|
|
656
|
+
meta: { category: "context" }
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
type: "import",
|
|
660
|
+
name: "createPubinfo",
|
|
661
|
+
package: "pubinfo-pr",
|
|
662
|
+
meta: { category: "provider" }
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
type: "import",
|
|
666
|
+
name: "createRequest",
|
|
667
|
+
package: "pubinfo-pr",
|
|
668
|
+
meta: { category: "request" }
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
type: "import",
|
|
672
|
+
name: "defineIconModule",
|
|
673
|
+
package: "pubinfo-pr",
|
|
674
|
+
meta: { category: "module" }
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
type: "import",
|
|
678
|
+
name: "defineRouteModule",
|
|
679
|
+
package: "pubinfo-pr",
|
|
680
|
+
meta: { category: "module" }
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
type: "import",
|
|
684
|
+
name: "getAllIconModules",
|
|
685
|
+
package: "pubinfo-pr",
|
|
686
|
+
meta: { category: "module" }
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
type: "import",
|
|
690
|
+
name: "getAllRouteModules",
|
|
691
|
+
package: "pubinfo-pr",
|
|
692
|
+
meta: { category: "module" }
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
type: "import",
|
|
696
|
+
name: "readProjectModules",
|
|
697
|
+
package: "pubinfo-pr",
|
|
698
|
+
meta: { category: "module" }
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
type: "import",
|
|
702
|
+
name: "updateWatermark",
|
|
703
|
+
package: "pubinfo-pr",
|
|
704
|
+
meta: { category: "utility" }
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
type: "import",
|
|
708
|
+
name: "cleanup",
|
|
709
|
+
package: "pubinfo-pr",
|
|
710
|
+
meta: { category: "utility" }
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
type: "import",
|
|
714
|
+
name: "cleanupWithoutUser",
|
|
715
|
+
package: "pubinfo-pr",
|
|
716
|
+
meta: { category: "utility" }
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
type: "import",
|
|
720
|
+
name: "wrapProxy",
|
|
721
|
+
package: "pubinfo-pr",
|
|
722
|
+
meta: { category: "utility" }
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
type: "import",
|
|
726
|
+
name: "watchDiffObject",
|
|
727
|
+
package: "pubinfo-pr",
|
|
728
|
+
meta: { category: "utility" }
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
type: "import",
|
|
732
|
+
name: "clearPersistedState",
|
|
733
|
+
package: "pubinfo-pr",
|
|
734
|
+
meta: { category: "storage" }
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
type: "import",
|
|
738
|
+
name: "getPersistedState",
|
|
739
|
+
package: "pubinfo-pr",
|
|
740
|
+
meta: { category: "storage" }
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
type: "import",
|
|
744
|
+
name: "setPersistedState",
|
|
745
|
+
package: "pubinfo-pr",
|
|
746
|
+
meta: { category: "storage" }
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
type: "import",
|
|
750
|
+
name: "storage",
|
|
751
|
+
package: "pubinfo-pr",
|
|
752
|
+
meta: { category: "storage" }
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
type: "import",
|
|
756
|
+
name: "getPubinfoNamespace",
|
|
757
|
+
package: "pubinfo-pr",
|
|
758
|
+
meta: { category: "storage" }
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
type: "import",
|
|
762
|
+
name: "publicKeyEncryption",
|
|
763
|
+
package: "pubinfo-pr",
|
|
764
|
+
meta: { category: "crypto" }
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
type: "import",
|
|
768
|
+
name: "useAuth",
|
|
769
|
+
package: "pubinfo-pr",
|
|
770
|
+
meta: { category: "composable" }
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
type: "import",
|
|
774
|
+
name: "useError",
|
|
775
|
+
package: "pubinfo-pr",
|
|
776
|
+
meta: { category: "composable" }
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
type: "import",
|
|
780
|
+
name: "useSuccess",
|
|
781
|
+
package: "pubinfo-pr",
|
|
782
|
+
meta: { category: "composable" }
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
type: "import",
|
|
786
|
+
name: "useWarn",
|
|
787
|
+
package: "pubinfo-pr",
|
|
788
|
+
meta: { category: "composable" }
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
type: "import",
|
|
792
|
+
name: "useInfo",
|
|
793
|
+
package: "pubinfo-pr",
|
|
794
|
+
meta: { category: "composable" }
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
type: "import",
|
|
798
|
+
name: "useStart",
|
|
799
|
+
package: "pubinfo-pr",
|
|
800
|
+
meta: { category: "composable" }
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
type: "import",
|
|
804
|
+
name: "useBox",
|
|
805
|
+
package: "pubinfo-pr",
|
|
806
|
+
meta: { category: "composable" }
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
type: "import",
|
|
810
|
+
name: "usePartyLogin",
|
|
811
|
+
package: "pubinfo-pr",
|
|
812
|
+
meta: { category: "composable" }
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
type: "import",
|
|
816
|
+
name: "useTheme",
|
|
817
|
+
package: "pubinfo-pr",
|
|
818
|
+
meta: { category: "composable" }
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
type: "import",
|
|
822
|
+
name: "createLog",
|
|
823
|
+
package: "pubinfo-pr",
|
|
824
|
+
meta: { category: "log" }
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
type: "import",
|
|
828
|
+
name: "defineSystemRoutes",
|
|
829
|
+
package: "pubinfo-pr",
|
|
830
|
+
meta: { category: "route" }
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
type: "import",
|
|
834
|
+
name: "getSystemRoutes",
|
|
835
|
+
package: "pubinfo-pr",
|
|
836
|
+
meta: { category: "route" }
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
type: "import",
|
|
840
|
+
name: "defineAsyncRoutes",
|
|
841
|
+
package: "pubinfo-pr",
|
|
842
|
+
meta: { category: "route" }
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
type: "import",
|
|
846
|
+
name: "getAsyncRoutes",
|
|
847
|
+
package: "pubinfo-pr",
|
|
848
|
+
meta: { category: "route" }
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
type: "import",
|
|
852
|
+
name: "formatBackRoutes",
|
|
853
|
+
package: "pubinfo-pr",
|
|
854
|
+
meta: { category: "route" }
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
type: "import",
|
|
858
|
+
name: "filterEmptyComponentRoutes",
|
|
859
|
+
package: "pubinfo-pr",
|
|
860
|
+
meta: { category: "route" }
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
type: "import",
|
|
864
|
+
name: "defineSettings",
|
|
865
|
+
package: "pubinfo-pr",
|
|
866
|
+
meta: { category: "settings" }
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
type: "import",
|
|
870
|
+
name: "getSettings",
|
|
871
|
+
package: "pubinfo-pr",
|
|
872
|
+
meta: { category: "settings" }
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
type: "import",
|
|
876
|
+
name: "useFavoritesStore",
|
|
877
|
+
package: "pubinfo-pr",
|
|
878
|
+
meta: { category: "store" }
|
|
879
|
+
},
|
|
880
|
+
{
|
|
881
|
+
type: "import",
|
|
882
|
+
name: "useIframeStore",
|
|
883
|
+
package: "pubinfo-pr",
|
|
884
|
+
meta: { category: "store" }
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
type: "import",
|
|
888
|
+
name: "useKeepAliveStore",
|
|
889
|
+
package: "pubinfo-pr",
|
|
890
|
+
meta: { category: "store" }
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
type: "import",
|
|
894
|
+
name: "useMenuStore",
|
|
895
|
+
package: "pubinfo-pr",
|
|
896
|
+
meta: { category: "store" }
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
type: "import",
|
|
900
|
+
name: "useRouteStore",
|
|
901
|
+
package: "pubinfo-pr",
|
|
902
|
+
meta: { category: "store" }
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
type: "import",
|
|
906
|
+
name: "useSettingsStore",
|
|
907
|
+
package: "pubinfo-pr",
|
|
908
|
+
meta: { category: "store" }
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
type: "import",
|
|
912
|
+
name: "useTabbarStore",
|
|
913
|
+
package: "pubinfo-pr",
|
|
914
|
+
meta: { category: "store" }
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
type: "import",
|
|
918
|
+
name: "useUserStore",
|
|
919
|
+
package: "pubinfo-pr",
|
|
920
|
+
meta: { category: "store" }
|
|
921
|
+
}
|
|
922
|
+
];
|
|
923
|
+
const pubinfoComponent = [
|
|
924
|
+
{
|
|
925
|
+
type: "component",
|
|
926
|
+
name: "PubinfoDev",
|
|
927
|
+
package: "pubinfo-pr",
|
|
928
|
+
meta: { category: "dev" }
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
type: "component",
|
|
932
|
+
name: "Copyright",
|
|
933
|
+
package: "pubinfo-pr",
|
|
934
|
+
meta: { category: "layout" }
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
type: "component",
|
|
938
|
+
name: "Layout",
|
|
939
|
+
package: "pubinfo-pr",
|
|
940
|
+
meta: { category: "layout" }
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
type: "component",
|
|
944
|
+
name: "LayoutHeader",
|
|
945
|
+
package: "pubinfo-pr",
|
|
946
|
+
meta: { category: "layout" }
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
type: "component",
|
|
950
|
+
name: "LayoutContent",
|
|
951
|
+
package: "pubinfo-pr",
|
|
952
|
+
meta: { category: "layout" }
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
type: "component",
|
|
956
|
+
name: "LayoutProvider",
|
|
957
|
+
package: "pubinfo-pr",
|
|
958
|
+
meta: { category: "layout" }
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
type: "component",
|
|
962
|
+
name: "LayoutSidebar",
|
|
963
|
+
package: "pubinfo-pr",
|
|
964
|
+
meta: { category: "layout" }
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
type: "component",
|
|
968
|
+
name: "LayoutTopbar",
|
|
969
|
+
package: "pubinfo-pr",
|
|
970
|
+
meta: { category: "layout" }
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
type: "component",
|
|
974
|
+
name: "Logo",
|
|
975
|
+
package: "pubinfo-pr",
|
|
976
|
+
meta: { category: "layout" }
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
type: "component",
|
|
980
|
+
name: "NotAllowed",
|
|
981
|
+
package: "pubinfo-pr",
|
|
982
|
+
meta: { category: "layout" }
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
type: "component",
|
|
986
|
+
name: "Tool",
|
|
987
|
+
package: "pubinfo-pr",
|
|
988
|
+
meta: { category: "layout" }
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
type: "component",
|
|
992
|
+
name: "Tools",
|
|
993
|
+
package: "pubinfo-pr",
|
|
994
|
+
meta: { category: "layout" }
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
type: "component",
|
|
998
|
+
name: "PageHeader",
|
|
999
|
+
package: "pubinfo-pr",
|
|
1000
|
+
meta: { category: "page" }
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
type: "component",
|
|
1004
|
+
name: "PageMain",
|
|
1005
|
+
package: "pubinfo-pr",
|
|
1006
|
+
meta: { category: "page" }
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
type: "component",
|
|
1010
|
+
name: "PartyLoginModal",
|
|
1011
|
+
package: "pubinfo-pr",
|
|
1012
|
+
meta: { category: "auth" }
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
type: "component",
|
|
1016
|
+
name: "PassStrengthValidator",
|
|
1017
|
+
package: "pubinfo-pr",
|
|
1018
|
+
meta: { category: "auth" }
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
type: "component",
|
|
1022
|
+
name: "PubinfoApp",
|
|
1023
|
+
package: "pubinfo-pr",
|
|
1024
|
+
meta: { category: "core" }
|
|
1025
|
+
},
|
|
1026
|
+
{
|
|
1027
|
+
type: "component",
|
|
1028
|
+
name: "PubinfoIcon",
|
|
1029
|
+
package: "pubinfo-pr",
|
|
1030
|
+
meta: { category: "icon" }
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
type: "component",
|
|
1034
|
+
name: "PubinfoIconPrismBox",
|
|
1035
|
+
package: "pubinfo-pr",
|
|
1036
|
+
meta: { category: "icon" }
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
type: "component",
|
|
1040
|
+
name: "PubinfoIconSquareBox",
|
|
1041
|
+
package: "pubinfo-pr",
|
|
1042
|
+
meta: { category: "icon" }
|
|
1043
|
+
},
|
|
1044
|
+
{
|
|
1045
|
+
type: "component",
|
|
1046
|
+
name: "PubinfoProvider",
|
|
1047
|
+
package: "pubinfo-pr",
|
|
1048
|
+
meta: { category: "provider" }
|
|
1049
|
+
}
|
|
1050
|
+
];
|
|
1051
|
+
|
|
1052
|
+
//#endregion
|
|
1053
|
+
//#region src/@data/vue.ts
|
|
1054
|
+
const VUE_DOCS_BASE = "https://cn.vuejs.org/api";
|
|
1055
|
+
const vueImport = [
|
|
1056
|
+
{
|
|
1057
|
+
type: "import",
|
|
1058
|
+
name: "onActivated",
|
|
1059
|
+
package: "vue",
|
|
1060
|
+
meta: {
|
|
1061
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onactivated`,
|
|
1062
|
+
category: "lifecycle"
|
|
1063
|
+
}
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
type: "import",
|
|
1067
|
+
name: "onBeforeMount",
|
|
1068
|
+
package: "vue",
|
|
1069
|
+
meta: {
|
|
1070
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onbeforemount`,
|
|
1071
|
+
category: "lifecycle"
|
|
1072
|
+
}
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
type: "import",
|
|
1076
|
+
name: "onBeforeUnmount",
|
|
1077
|
+
package: "vue",
|
|
1078
|
+
meta: {
|
|
1079
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onbeforeunmount`,
|
|
1080
|
+
category: "lifecycle"
|
|
1081
|
+
}
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
type: "import",
|
|
1085
|
+
name: "onBeforeUpdate",
|
|
1086
|
+
package: "vue",
|
|
1087
|
+
meta: {
|
|
1088
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onbeforeupdate`,
|
|
1089
|
+
category: "lifecycle"
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
type: "import",
|
|
1094
|
+
name: "onErrorCaptured",
|
|
1095
|
+
package: "vue",
|
|
1096
|
+
meta: {
|
|
1097
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onerrorcaptured`,
|
|
1098
|
+
category: "lifecycle"
|
|
1099
|
+
}
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
type: "import",
|
|
1103
|
+
name: "onDeactivated",
|
|
1104
|
+
package: "vue",
|
|
1105
|
+
meta: {
|
|
1106
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#ondeactivated`,
|
|
1107
|
+
category: "lifecycle"
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
1110
|
+
{
|
|
1111
|
+
type: "import",
|
|
1112
|
+
name: "onMounted",
|
|
1113
|
+
package: "vue",
|
|
1114
|
+
meta: {
|
|
1115
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onmounted`,
|
|
1116
|
+
category: "lifecycle"
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
type: "import",
|
|
1121
|
+
name: "onServerPrefetch",
|
|
1122
|
+
package: "vue",
|
|
1123
|
+
meta: {
|
|
1124
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onserverprefetch`,
|
|
1125
|
+
category: "lifecycle"
|
|
1126
|
+
}
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
type: "import",
|
|
1130
|
+
name: "onUnmounted",
|
|
1131
|
+
package: "vue",
|
|
1132
|
+
meta: {
|
|
1133
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onunmounted`,
|
|
1134
|
+
category: "lifecycle"
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
{
|
|
1138
|
+
type: "import",
|
|
1139
|
+
name: "onUpdated",
|
|
1140
|
+
package: "vue",
|
|
1141
|
+
meta: {
|
|
1142
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onupdated`,
|
|
1143
|
+
category: "lifecycle"
|
|
1144
|
+
}
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
type: "import",
|
|
1148
|
+
name: "useAttrs",
|
|
1149
|
+
package: "vue",
|
|
1150
|
+
meta: {
|
|
1151
|
+
docs: `${VUE_DOCS_BASE}/composition-api-helpers.html#useattrs`,
|
|
1152
|
+
category: "helper"
|
|
1153
|
+
}
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
type: "import",
|
|
1157
|
+
name: "useSlots",
|
|
1158
|
+
package: "vue",
|
|
1159
|
+
meta: {
|
|
1160
|
+
docs: `${VUE_DOCS_BASE}/composition-api-helpers.html#useslots`,
|
|
1161
|
+
category: "helper"
|
|
1162
|
+
}
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
type: "import",
|
|
1166
|
+
name: "computed",
|
|
1167
|
+
package: "vue",
|
|
1168
|
+
meta: {
|
|
1169
|
+
docs: `${VUE_DOCS_BASE}/reactivity-core.html#computed`,
|
|
1170
|
+
category: "reactivity"
|
|
1171
|
+
}
|
|
1172
|
+
},
|
|
1173
|
+
{
|
|
1174
|
+
type: "import",
|
|
1175
|
+
name: "customRef",
|
|
1176
|
+
package: "vue",
|
|
1177
|
+
meta: {
|
|
1178
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#customref`,
|
|
1179
|
+
category: "reactivity"
|
|
1180
|
+
}
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
type: "import",
|
|
1184
|
+
name: "isReadonly",
|
|
1185
|
+
package: "vue",
|
|
1186
|
+
meta: {
|
|
1187
|
+
docs: `${VUE_DOCS_BASE}/reactivity-utilities.html#isreadonly`,
|
|
1188
|
+
category: "reactivity"
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
type: "import",
|
|
1193
|
+
name: "isRef",
|
|
1194
|
+
package: "vue",
|
|
1195
|
+
meta: {
|
|
1196
|
+
docs: `${VUE_DOCS_BASE}/reactivity-utilities.html#isref`,
|
|
1197
|
+
category: "reactivity"
|
|
1198
|
+
}
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
type: "import",
|
|
1202
|
+
name: "isShallow",
|
|
1203
|
+
package: "vue",
|
|
1204
|
+
meta: {
|
|
1205
|
+
docs: `${VUE_DOCS_BASE}/reactivity-utilities.html#isshallow`,
|
|
1206
|
+
category: "reactivity"
|
|
1207
|
+
}
|
|
1208
|
+
},
|
|
1209
|
+
{
|
|
1210
|
+
type: "import",
|
|
1211
|
+
name: "isProxy",
|
|
1212
|
+
package: "vue",
|
|
1213
|
+
meta: {
|
|
1214
|
+
docs: `${VUE_DOCS_BASE}/reactivity-utilities.html#isproxy`,
|
|
1215
|
+
category: "reactivity"
|
|
1216
|
+
}
|
|
1217
|
+
},
|
|
1218
|
+
{
|
|
1219
|
+
type: "import",
|
|
1220
|
+
name: "isReactive",
|
|
1221
|
+
package: "vue",
|
|
1222
|
+
meta: {
|
|
1223
|
+
docs: `${VUE_DOCS_BASE}/reactivity-utilities.html#isreactive`,
|
|
1224
|
+
category: "reactivity"
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
type: "import",
|
|
1229
|
+
name: "markRaw",
|
|
1230
|
+
package: "vue",
|
|
1231
|
+
meta: {
|
|
1232
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#markraw`,
|
|
1233
|
+
category: "reactivity"
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
type: "import",
|
|
1238
|
+
name: "reactive",
|
|
1239
|
+
package: "vue",
|
|
1240
|
+
meta: {
|
|
1241
|
+
docs: `${VUE_DOCS_BASE}/reactivity-core.html#reactive`,
|
|
1242
|
+
category: "reactivity"
|
|
1243
|
+
}
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
type: "import",
|
|
1247
|
+
name: "readonly",
|
|
1248
|
+
package: "vue",
|
|
1249
|
+
meta: {
|
|
1250
|
+
docs: `${VUE_DOCS_BASE}/reactivity-core.html#readonly`,
|
|
1251
|
+
category: "reactivity"
|
|
1252
|
+
}
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
type: "import",
|
|
1256
|
+
name: "ref",
|
|
1257
|
+
package: "vue",
|
|
1258
|
+
meta: {
|
|
1259
|
+
docs: `${VUE_DOCS_BASE}/reactivity-core.html#ref`,
|
|
1260
|
+
category: "reactivity"
|
|
1261
|
+
}
|
|
1262
|
+
},
|
|
1263
|
+
{
|
|
1264
|
+
type: "import",
|
|
1265
|
+
name: "shallowReactive",
|
|
1266
|
+
package: "vue",
|
|
1267
|
+
meta: {
|
|
1268
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#shallowreactive`,
|
|
1269
|
+
category: "reactivity"
|
|
1270
|
+
}
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
type: "import",
|
|
1274
|
+
name: "shallowReadonly",
|
|
1275
|
+
package: "vue",
|
|
1276
|
+
meta: {
|
|
1277
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#shallowreadonly`,
|
|
1278
|
+
category: "reactivity"
|
|
1279
|
+
}
|
|
1280
|
+
},
|
|
1281
|
+
{
|
|
1282
|
+
type: "import",
|
|
1283
|
+
name: "shallowRef",
|
|
1284
|
+
package: "vue",
|
|
1285
|
+
meta: {
|
|
1286
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#shallowref`,
|
|
1287
|
+
category: "reactivity"
|
|
1288
|
+
}
|
|
1289
|
+
},
|
|
1290
|
+
{
|
|
1291
|
+
type: "import",
|
|
1292
|
+
name: "triggerRef",
|
|
1293
|
+
package: "vue",
|
|
1294
|
+
meta: {
|
|
1295
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#triggerref`,
|
|
1296
|
+
category: "reactivity"
|
|
1297
|
+
}
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
type: "import",
|
|
1301
|
+
name: "toRaw",
|
|
1302
|
+
package: "vue",
|
|
1303
|
+
meta: {
|
|
1304
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#toraw`,
|
|
1305
|
+
category: "reactivity"
|
|
1306
|
+
}
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
type: "import",
|
|
1310
|
+
name: "toRef",
|
|
1311
|
+
package: "vue",
|
|
1312
|
+
meta: {
|
|
1313
|
+
docs: `${VUE_DOCS_BASE}/reactivity-utilities.html#toref`,
|
|
1314
|
+
category: "reactivity"
|
|
1315
|
+
}
|
|
1316
|
+
},
|
|
1317
|
+
{
|
|
1318
|
+
type: "import",
|
|
1319
|
+
name: "toRefs",
|
|
1320
|
+
package: "vue",
|
|
1321
|
+
meta: {
|
|
1322
|
+
docs: `${VUE_DOCS_BASE}/reactivity-utilities.html#torefs`,
|
|
1323
|
+
category: "reactivity"
|
|
1324
|
+
}
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
type: "import",
|
|
1328
|
+
name: "toValue",
|
|
1329
|
+
package: "vue",
|
|
1330
|
+
meta: {
|
|
1331
|
+
docs: `${VUE_DOCS_BASE}/reactivity-utilities.html#tovalue`,
|
|
1332
|
+
category: "reactivity"
|
|
1333
|
+
}
|
|
1334
|
+
},
|
|
1335
|
+
{
|
|
1336
|
+
type: "import",
|
|
1337
|
+
name: "unref",
|
|
1338
|
+
package: "vue",
|
|
1339
|
+
meta: {
|
|
1340
|
+
docs: `${VUE_DOCS_BASE}/reactivity-utilities.html#unref`,
|
|
1341
|
+
category: "reactivity"
|
|
1342
|
+
}
|
|
1343
|
+
},
|
|
1344
|
+
{
|
|
1345
|
+
type: "import",
|
|
1346
|
+
name: "watch",
|
|
1347
|
+
package: "vue",
|
|
1348
|
+
meta: {
|
|
1349
|
+
docs: `${VUE_DOCS_BASE}/reactivity-core.html#watch`,
|
|
1350
|
+
category: "reactivity"
|
|
1351
|
+
}
|
|
1352
|
+
},
|
|
1353
|
+
{
|
|
1354
|
+
type: "import",
|
|
1355
|
+
name: "watchEffect",
|
|
1356
|
+
package: "vue",
|
|
1357
|
+
meta: {
|
|
1358
|
+
docs: `${VUE_DOCS_BASE}/reactivity-core.html#watcheffect`,
|
|
1359
|
+
category: "reactivity"
|
|
1360
|
+
}
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
type: "import",
|
|
1364
|
+
name: "watchPostEffect",
|
|
1365
|
+
package: "vue",
|
|
1366
|
+
meta: {
|
|
1367
|
+
docs: `${VUE_DOCS_BASE}/reactivity-core.html#watchposteffect`,
|
|
1368
|
+
category: "reactivity"
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1371
|
+
{
|
|
1372
|
+
type: "import",
|
|
1373
|
+
name: "watchSyncEffect",
|
|
1374
|
+
package: "vue",
|
|
1375
|
+
meta: {
|
|
1376
|
+
docs: `${VUE_DOCS_BASE}/reactivity-core.html#watchsynceffect`,
|
|
1377
|
+
category: "reactivity"
|
|
1378
|
+
}
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
type: "import",
|
|
1382
|
+
name: "defineComponent",
|
|
1383
|
+
package: "vue",
|
|
1384
|
+
meta: {
|
|
1385
|
+
docs: `${VUE_DOCS_BASE}/general.html#definecomponent`,
|
|
1386
|
+
category: "component"
|
|
1387
|
+
}
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
type: "import",
|
|
1391
|
+
name: "defineAsyncComponent",
|
|
1392
|
+
package: "vue",
|
|
1393
|
+
meta: {
|
|
1394
|
+
docs: `${VUE_DOCS_BASE}/general.html#defineasynccomponent`,
|
|
1395
|
+
category: "component"
|
|
1396
|
+
}
|
|
1397
|
+
},
|
|
1398
|
+
{
|
|
1399
|
+
type: "import",
|
|
1400
|
+
name: "getCurrentInstance",
|
|
1401
|
+
package: "vue",
|
|
1402
|
+
meta: {
|
|
1403
|
+
docs: `${VUE_DOCS_BASE}/composition-api-helpers.html#getcurrentinstance`,
|
|
1404
|
+
category: "component"
|
|
1405
|
+
}
|
|
1406
|
+
},
|
|
1407
|
+
{
|
|
1408
|
+
type: "import",
|
|
1409
|
+
name: "h",
|
|
1410
|
+
package: "vue",
|
|
1411
|
+
meta: {
|
|
1412
|
+
docs: `${VUE_DOCS_BASE}/render-function.html#h`,
|
|
1413
|
+
category: "component"
|
|
1414
|
+
}
|
|
1415
|
+
},
|
|
1416
|
+
{
|
|
1417
|
+
type: "import",
|
|
1418
|
+
name: "inject",
|
|
1419
|
+
package: "vue",
|
|
1420
|
+
meta: {
|
|
1421
|
+
docs: `${VUE_DOCS_BASE}/composition-api-dependency-injection.html#inject`,
|
|
1422
|
+
category: "component"
|
|
1423
|
+
}
|
|
1424
|
+
},
|
|
1425
|
+
{
|
|
1426
|
+
type: "import",
|
|
1427
|
+
name: "nextTick",
|
|
1428
|
+
package: "vue",
|
|
1429
|
+
meta: {
|
|
1430
|
+
docs: `${VUE_DOCS_BASE}/general.html#nexttick`,
|
|
1431
|
+
category: "component"
|
|
1432
|
+
}
|
|
1433
|
+
},
|
|
1434
|
+
{
|
|
1435
|
+
type: "import",
|
|
1436
|
+
name: "provide",
|
|
1437
|
+
package: "vue",
|
|
1438
|
+
meta: {
|
|
1439
|
+
docs: `${VUE_DOCS_BASE}/composition-api-dependency-injection.html#provide`,
|
|
1440
|
+
category: "component"
|
|
1441
|
+
}
|
|
1442
|
+
},
|
|
1443
|
+
{
|
|
1444
|
+
type: "import",
|
|
1445
|
+
name: "useCssModule",
|
|
1446
|
+
package: "vue",
|
|
1447
|
+
meta: {
|
|
1448
|
+
docs: `${VUE_DOCS_BASE}/sfc-css-features.html#css-modules`,
|
|
1449
|
+
category: "component"
|
|
1450
|
+
}
|
|
1451
|
+
},
|
|
1452
|
+
{
|
|
1453
|
+
type: "import",
|
|
1454
|
+
name: "createApp",
|
|
1455
|
+
package: "vue",
|
|
1456
|
+
meta: {
|
|
1457
|
+
docs: `${VUE_DOCS_BASE}/application.html#createapp`,
|
|
1458
|
+
category: "component"
|
|
1459
|
+
}
|
|
1460
|
+
},
|
|
1461
|
+
{
|
|
1462
|
+
type: "import",
|
|
1463
|
+
name: "effectScope",
|
|
1464
|
+
package: "vue",
|
|
1465
|
+
meta: {
|
|
1466
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#effectscope`,
|
|
1467
|
+
category: "effect-scope"
|
|
1468
|
+
}
|
|
1469
|
+
},
|
|
1470
|
+
{
|
|
1471
|
+
type: "import",
|
|
1472
|
+
name: "EffectScope",
|
|
1473
|
+
package: "vue",
|
|
1474
|
+
meta: {
|
|
1475
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#effectscope`,
|
|
1476
|
+
category: "effect-scope"
|
|
1477
|
+
}
|
|
1478
|
+
},
|
|
1479
|
+
{
|
|
1480
|
+
type: "import",
|
|
1481
|
+
name: "getCurrentScope",
|
|
1482
|
+
package: "vue",
|
|
1483
|
+
meta: {
|
|
1484
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#getcurrentscope`,
|
|
1485
|
+
category: "effect-scope"
|
|
1486
|
+
}
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
type: "import",
|
|
1490
|
+
name: "onScopeDispose",
|
|
1491
|
+
package: "vue",
|
|
1492
|
+
meta: {
|
|
1493
|
+
docs: `${VUE_DOCS_BASE}/reactivity-advanced.html#onscopedispose`,
|
|
1494
|
+
category: "effect-scope"
|
|
1495
|
+
}
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
type: "import",
|
|
1499
|
+
name: "onRenderTracked",
|
|
1500
|
+
package: "vue",
|
|
1501
|
+
meta: {
|
|
1502
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onrendertracked`,
|
|
1503
|
+
category: "lifecycle"
|
|
1504
|
+
}
|
|
1505
|
+
},
|
|
1506
|
+
{
|
|
1507
|
+
type: "import",
|
|
1508
|
+
name: "onRenderTriggered",
|
|
1509
|
+
package: "vue",
|
|
1510
|
+
meta: {
|
|
1511
|
+
docs: `${VUE_DOCS_BASE}/composition-api-lifecycle.html#onrendertriggered`,
|
|
1512
|
+
category: "lifecycle"
|
|
1513
|
+
}
|
|
1514
|
+
},
|
|
1515
|
+
{
|
|
1516
|
+
type: "import",
|
|
1517
|
+
name: "resolveComponent",
|
|
1518
|
+
package: "vue",
|
|
1519
|
+
meta: {
|
|
1520
|
+
docs: `${VUE_DOCS_BASE}/render-function.html#resolvecomponent`,
|
|
1521
|
+
category: "component"
|
|
1522
|
+
}
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
type: "import",
|
|
1526
|
+
name: "useCssVars",
|
|
1527
|
+
package: "vue",
|
|
1528
|
+
meta: {
|
|
1529
|
+
docs: `${VUE_DOCS_BASE}/sfc-css-features.html#v-bind-in-css`,
|
|
1530
|
+
category: "component"
|
|
1531
|
+
}
|
|
1532
|
+
},
|
|
1533
|
+
{
|
|
1534
|
+
type: "import",
|
|
1535
|
+
name: "useModel",
|
|
1536
|
+
package: "vue",
|
|
1537
|
+
meta: {
|
|
1538
|
+
docs: `${VUE_DOCS_BASE}/composition-api-helpers.html#usemodel`,
|
|
1539
|
+
category: "helper"
|
|
1540
|
+
}
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
type: "import",
|
|
1544
|
+
name: "getCurrentWatcher",
|
|
1545
|
+
package: "vue",
|
|
1546
|
+
meta: { category: "reactivity" }
|
|
1547
|
+
},
|
|
1548
|
+
{
|
|
1549
|
+
type: "import",
|
|
1550
|
+
name: "onWatcherCleanup",
|
|
1551
|
+
package: "vue",
|
|
1552
|
+
meta: {
|
|
1553
|
+
docs: `${VUE_DOCS_BASE}/reactivity-core.html#onwatchercleanup`,
|
|
1554
|
+
category: "reactivity"
|
|
1555
|
+
}
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
type: "import",
|
|
1559
|
+
name: "useId",
|
|
1560
|
+
package: "vue",
|
|
1561
|
+
meta: {
|
|
1562
|
+
docs: `${VUE_DOCS_BASE}/composition-api-helpers.html#useid`,
|
|
1563
|
+
category: "helper"
|
|
1564
|
+
}
|
|
1565
|
+
},
|
|
1566
|
+
{
|
|
1567
|
+
type: "import",
|
|
1568
|
+
name: "useTemplateRef",
|
|
1569
|
+
package: "vue",
|
|
1570
|
+
meta: {
|
|
1571
|
+
docs: `${VUE_DOCS_BASE}/composition-api-helpers.html#usetemplateref`,
|
|
1572
|
+
category: "helper"
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
];
|
|
1576
|
+
const vueComponent = [
|
|
1577
|
+
{
|
|
1578
|
+
type: "component",
|
|
1579
|
+
name: "Transition",
|
|
1580
|
+
package: "vue",
|
|
1581
|
+
meta: { docs: `${VUE_DOCS_BASE}/built-in-components.html#transition` }
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
type: "component",
|
|
1585
|
+
name: "TransitionGroup",
|
|
1586
|
+
package: "vue",
|
|
1587
|
+
meta: { docs: `${VUE_DOCS_BASE}/built-in-components.html#transitiongroup` }
|
|
1588
|
+
},
|
|
1589
|
+
{
|
|
1590
|
+
type: "component",
|
|
1591
|
+
name: "KeepAlive",
|
|
1592
|
+
package: "vue",
|
|
1593
|
+
meta: { docs: `${VUE_DOCS_BASE}/built-in-components.html#keepalive` }
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
type: "component",
|
|
1597
|
+
name: "Teleport",
|
|
1598
|
+
package: "vue",
|
|
1599
|
+
meta: { docs: `${VUE_DOCS_BASE}/built-in-components.html#teleport` }
|
|
1600
|
+
},
|
|
1601
|
+
{
|
|
1602
|
+
type: "component",
|
|
1603
|
+
name: "Suspense",
|
|
1604
|
+
package: "vue",
|
|
1605
|
+
meta: { docs: `${VUE_DOCS_BASE}/built-in-components.html#suspense` }
|
|
1606
|
+
}
|
|
1607
|
+
];
|
|
1608
|
+
|
|
1609
|
+
//#endregion
|
|
1610
|
+
//#region src/@data/index.ts
|
|
1611
|
+
const functions = [...vueImport, ...pubinfoImport];
|
|
1612
|
+
const components = [...vueComponent, ...pubinfoComponent];
|
|
1613
|
+
|
|
1614
|
+
//#endregion
|
|
1615
|
+
//#region src/server/utils/ast-analyzer.ts
|
|
1616
|
+
/** 跳过的目录 */
|
|
1617
|
+
const IGNORED_DIRS = new Set([
|
|
1618
|
+
"node_modules",
|
|
1619
|
+
"dist",
|
|
1620
|
+
"build",
|
|
1621
|
+
".git",
|
|
1622
|
+
"coverage",
|
|
1623
|
+
".pubinfo"
|
|
1624
|
+
]);
|
|
1625
|
+
/** 默认源码扩展名 */
|
|
1626
|
+
const SOURCE_EXTENSIONS$2 = new Set([
|
|
1627
|
+
".ts",
|
|
1628
|
+
".tsx",
|
|
1629
|
+
".js",
|
|
1630
|
+
".jsx",
|
|
1631
|
+
".vue"
|
|
1632
|
+
]);
|
|
1633
|
+
/** 单次并发读取文件数(防止文件描述符爆满) */
|
|
1634
|
+
const FILE_READ_CONCURRENCY = 64;
|
|
1635
|
+
/**
|
|
1636
|
+
* 递归扫描目录获取所有源文件(同步版本,保留给 module-counter 使用)
|
|
1637
|
+
*/
|
|
1638
|
+
function getAllSourceFiles(dir, extensions = [
|
|
1639
|
+
".ts",
|
|
1640
|
+
".tsx",
|
|
1641
|
+
".js",
|
|
1642
|
+
".jsx",
|
|
1643
|
+
".vue"
|
|
1644
|
+
]) {
|
|
1645
|
+
const extSet = new Set(extensions);
|
|
1646
|
+
const files = [];
|
|
1647
|
+
function scan(currentDir) {
|
|
1648
|
+
try {
|
|
1649
|
+
const entries = readdirSync(currentDir);
|
|
1650
|
+
for (const entry of entries) {
|
|
1651
|
+
const fullPath = join(currentDir, entry);
|
|
1652
|
+
try {
|
|
1653
|
+
const s = statSync(fullPath);
|
|
1654
|
+
if (s.isDirectory()) {
|
|
1655
|
+
if (!IGNORED_DIRS.has(entry)) scan(fullPath);
|
|
1656
|
+
} else if (s.isFile() && extSet.has(extname(fullPath))) files.push(fullPath);
|
|
1657
|
+
} catch {}
|
|
1658
|
+
}
|
|
1659
|
+
} catch {}
|
|
1660
|
+
}
|
|
1661
|
+
scan(dir);
|
|
1662
|
+
return files;
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* 异步递归扫描目录获取所有源文件
|
|
1666
|
+
*/
|
|
1667
|
+
async function getAllSourceFilesAsync(dir) {
|
|
1668
|
+
const files = [];
|
|
1669
|
+
async function scan(currentDir) {
|
|
1670
|
+
let entries;
|
|
1671
|
+
try {
|
|
1672
|
+
entries = await readdir(currentDir);
|
|
1673
|
+
} catch {
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1676
|
+
const statJobs = entries.map(async (entry) => {
|
|
1677
|
+
const fullPath = join(currentDir, entry);
|
|
1678
|
+
try {
|
|
1679
|
+
const s = await stat(fullPath);
|
|
1680
|
+
if (s.isDirectory()) {
|
|
1681
|
+
if (!IGNORED_DIRS.has(entry)) await scan(fullPath);
|
|
1682
|
+
} else if (s.isFile() && SOURCE_EXTENSIONS$2.has(extname(fullPath))) files.push(fullPath);
|
|
1683
|
+
} catch {}
|
|
1684
|
+
});
|
|
1685
|
+
await Promise.all(statJobs);
|
|
1686
|
+
}
|
|
1687
|
+
await scan(dir);
|
|
1688
|
+
return files;
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* 根据文件扩展名选择语言类型
|
|
1692
|
+
*/
|
|
1693
|
+
function getLang(filePath) {
|
|
1694
|
+
switch (extname(filePath)) {
|
|
1695
|
+
case ".tsx":
|
|
1696
|
+
case ".jsx": return Lang.Tsx;
|
|
1697
|
+
case ".js": return Lang.JavaScript;
|
|
1698
|
+
default: return Lang.TypeScript;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
/**
|
|
1702
|
+
* 在文件内容上做一次快速字符串检查,返回内容中可能出现的追踪项名称集合。
|
|
1703
|
+
* 通过这一步可以跳过绝大部分不相关的文件,避免无效 AST 解析。
|
|
1704
|
+
*/
|
|
1705
|
+
function quickFilterNames(content, names) {
|
|
1706
|
+
const hits = /* @__PURE__ */ new Set();
|
|
1707
|
+
for (const name of names) if (content.includes(name)) hits.add(name);
|
|
1708
|
+
return hits;
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* 对单个文件做一次性全量分析,返回该文件中出现的所有追踪名的使用位置。
|
|
1712
|
+
*
|
|
1713
|
+
* 优化点:
|
|
1714
|
+
* 1. 先用字符串 includes 快速预过滤,跳过不相关名称。
|
|
1715
|
+
* 2. AST 只解析一次,遍历一次 rootNode,收集所有 call_expression / import_specifier / jsx。
|
|
1716
|
+
* 3. Vue template 部分改为一次性合并正则匹配(而非 N 次独立正则)。
|
|
1717
|
+
*/
|
|
1718
|
+
function analyzeFile(content, filePath, functionNames, componentNames) {
|
|
1719
|
+
const result = {
|
|
1720
|
+
functions: /* @__PURE__ */ new Map(),
|
|
1721
|
+
components: /* @__PURE__ */ new Map()
|
|
1722
|
+
};
|
|
1723
|
+
const hitFunctions = quickFilterNames(content, functionNames);
|
|
1724
|
+
const hitComponents = quickFilterNames(content, componentNames);
|
|
1725
|
+
if (hitFunctions.size === 0 && hitComponents.size === 0) return result;
|
|
1726
|
+
try {
|
|
1727
|
+
const rootNode = parse(getLang(filePath), content).root();
|
|
1728
|
+
if (hitFunctions.size > 0) {
|
|
1729
|
+
const callMatches = rootNode.findAll({ rule: { kind: "call_expression" } });
|
|
1730
|
+
for (const match of callMatches) {
|
|
1731
|
+
const calleeName = match.field("function")?.text();
|
|
1732
|
+
if (calleeName && hitFunctions.has(calleeName)) {
|
|
1733
|
+
const range = match.range();
|
|
1734
|
+
const loc = {
|
|
1735
|
+
file: filePath,
|
|
1736
|
+
line: range.start.line + 1,
|
|
1737
|
+
column: range.start.column + 1,
|
|
1738
|
+
code: match.text().slice(0, 100)
|
|
1739
|
+
};
|
|
1740
|
+
if (!result.functions.has(calleeName)) result.functions.set(calleeName, []);
|
|
1741
|
+
result.functions.get(calleeName).push(loc);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
if (hitComponents.size > 0) {
|
|
1746
|
+
const jsxMatches = rootNode.findAll({ rule: { kind: "jsx_opening_element" } });
|
|
1747
|
+
for (const match of jsxMatches) {
|
|
1748
|
+
const text = match.text();
|
|
1749
|
+
const nameMatch = text.match(/^<(\w+)/);
|
|
1750
|
+
if (nameMatch && hitComponents.has(nameMatch[1])) {
|
|
1751
|
+
const compName = nameMatch[1];
|
|
1752
|
+
const range = match.range();
|
|
1753
|
+
const loc = {
|
|
1754
|
+
file: filePath,
|
|
1755
|
+
line: range.start.line + 1,
|
|
1756
|
+
column: range.start.column + 1,
|
|
1757
|
+
code: text.slice(0, 100)
|
|
1758
|
+
};
|
|
1759
|
+
if (!result.components.has(compName)) result.components.set(compName, []);
|
|
1760
|
+
result.components.get(compName).push(loc);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
} catch {}
|
|
1765
|
+
if (filePath.endsWith(".vue") && hitComponents.size > 0) {
|
|
1766
|
+
const names = Array.from(hitComponents);
|
|
1767
|
+
const unionRegex = new RegExp(`<(${names.map(escapeRegex).join("|")})[\\s>/]`, "g");
|
|
1768
|
+
const lines = content.split("\n");
|
|
1769
|
+
const lineOffsets = buildLineOffsets(lines);
|
|
1770
|
+
let match = unionRegex.exec(content);
|
|
1771
|
+
while (match !== null) {
|
|
1772
|
+
const compName = match[1];
|
|
1773
|
+
const pos = match.index;
|
|
1774
|
+
const lineNum = findLineNumber(lineOffsets, pos);
|
|
1775
|
+
const loc = {
|
|
1776
|
+
file: filePath,
|
|
1777
|
+
line: lineNum,
|
|
1778
|
+
column: pos - lineOffsets[lineNum - 1] + 1,
|
|
1779
|
+
code: lines[lineNum - 1].trim().slice(0, 100)
|
|
1780
|
+
};
|
|
1781
|
+
if (!result.components.has(compName)) result.components.set(compName, []);
|
|
1782
|
+
result.components.get(compName).push(loc);
|
|
1783
|
+
match = unionRegex.exec(content);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
return result;
|
|
1787
|
+
}
|
|
1788
|
+
/** 转义正则特殊字符 */
|
|
1789
|
+
function escapeRegex(str) {
|
|
1790
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1791
|
+
}
|
|
1792
|
+
/** 构建每行起始偏移量数组,索引 0 = 第 1 行起始偏移 */
|
|
1793
|
+
function buildLineOffsets(lines) {
|
|
1794
|
+
const offsets = Array.from({ length: lines.length });
|
|
1795
|
+
offsets[0] = 0;
|
|
1796
|
+
for (let i = 1; i < lines.length; i++) offsets[i] = offsets[i - 1] + lines[i - 1].length + 1;
|
|
1797
|
+
return offsets;
|
|
1798
|
+
}
|
|
1799
|
+
/** 通过二分查找快速定位字符偏移所在的行号(1-based) */
|
|
1800
|
+
function findLineNumber(lineOffsets, pos) {
|
|
1801
|
+
let lo = 0;
|
|
1802
|
+
let hi = lineOffsets.length - 1;
|
|
1803
|
+
while (lo < hi) {
|
|
1804
|
+
const mid = lo + hi + 1 >> 1;
|
|
1805
|
+
if (lineOffsets[mid] <= pos) lo = mid;
|
|
1806
|
+
else hi = mid - 1;
|
|
1807
|
+
}
|
|
1808
|
+
return lo + 1;
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* 以指定并发度分批执行异步任务
|
|
1812
|
+
*/
|
|
1813
|
+
async function processBatched(items, concurrency, fn) {
|
|
1814
|
+
const results = [];
|
|
1815
|
+
for (let i = 0; i < items.length; i += concurrency) {
|
|
1816
|
+
const batch = items.slice(i, i + concurrency);
|
|
1817
|
+
const batchResults = await Promise.all(batch.map(fn));
|
|
1818
|
+
results.push(...batchResults);
|
|
1819
|
+
}
|
|
1820
|
+
return results;
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* 扫描单个文件的使用情况。用于增量更新场景。
|
|
1824
|
+
*/
|
|
1825
|
+
async function scanSingleFile(filePath, functionsToTrack, componentsToTrack) {
|
|
1826
|
+
const ext = extname(filePath);
|
|
1827
|
+
if (!SOURCE_EXTENSIONS$2.has(ext)) return null;
|
|
1828
|
+
try {
|
|
1829
|
+
return analyzeFile(await readFile(filePath, "utf-8"), filePath, new Set(functionsToTrack.map((f) => f.name)), new Set(componentsToTrack.map((c) => c.name)));
|
|
1830
|
+
} catch {
|
|
1831
|
+
return null;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* 扫描项目中的使用情况(优化版)
|
|
1836
|
+
*
|
|
1837
|
+
* 相比旧版的优化:
|
|
1838
|
+
* 1. 异步 I/O + 并发读取文件,不阻塞 Vite dev server。
|
|
1839
|
+
* 2. 每文件只做一次 AST 解析,遍历一次收集所有命中。
|
|
1840
|
+
* 3. 快速预过滤(string.includes)跳过不相关文件。
|
|
1841
|
+
* 4. Vue template 使用联合正则一次匹配,二分法定位行号。
|
|
1842
|
+
*
|
|
1843
|
+
* @param projectRoot 项目根目录
|
|
1844
|
+
* @param functionsToTrack 要追踪的函数列表
|
|
1845
|
+
* @param componentsToTrack 要追踪的组件列表
|
|
1846
|
+
*/
|
|
1847
|
+
async function scanUsageStats(projectRoot, functionsToTrack, componentsToTrack) {
|
|
1848
|
+
const result = {
|
|
1849
|
+
functions: [],
|
|
1850
|
+
components: [],
|
|
1851
|
+
totalFiles: 0,
|
|
1852
|
+
scannedFiles: 0
|
|
1853
|
+
};
|
|
1854
|
+
const files = await getAllSourceFilesAsync(projectRoot);
|
|
1855
|
+
result.totalFiles = files.length;
|
|
1856
|
+
const functionNames = new Set(functionsToTrack.map((f) => f.name));
|
|
1857
|
+
const componentNames = new Set(componentsToTrack.map((c) => c.name));
|
|
1858
|
+
const functionStatsMap = /* @__PURE__ */ new Map();
|
|
1859
|
+
const componentStatsMap = /* @__PURE__ */ new Map();
|
|
1860
|
+
for (const func of functionsToTrack) functionStatsMap.set(func.name, {
|
|
1861
|
+
name: func.name,
|
|
1862
|
+
type: "import",
|
|
1863
|
+
package: func.package,
|
|
1864
|
+
count: 0,
|
|
1865
|
+
locations: [],
|
|
1866
|
+
meta: func.meta
|
|
1867
|
+
});
|
|
1868
|
+
for (const comp of componentsToTrack) componentStatsMap.set(comp.name, {
|
|
1869
|
+
name: comp.name,
|
|
1870
|
+
type: "component",
|
|
1871
|
+
package: comp.package,
|
|
1872
|
+
count: 0,
|
|
1873
|
+
locations: [],
|
|
1874
|
+
meta: comp.meta
|
|
1875
|
+
});
|
|
1876
|
+
await processBatched(files, FILE_READ_CONCURRENCY, async (file) => {
|
|
1877
|
+
try {
|
|
1878
|
+
const content = await readFile(file, "utf-8");
|
|
1879
|
+
result.scannedFiles++;
|
|
1880
|
+
const analysis = analyzeFile(content, file, functionNames, componentNames);
|
|
1881
|
+
for (const [name, locations] of analysis.functions) {
|
|
1882
|
+
const stats = functionStatsMap.get(name);
|
|
1883
|
+
if (stats) {
|
|
1884
|
+
stats.locations.push(...locations);
|
|
1885
|
+
stats.count += locations.length;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
for (const [name, locations] of analysis.components) {
|
|
1889
|
+
const stats = componentStatsMap.get(name);
|
|
1890
|
+
if (stats) {
|
|
1891
|
+
stats.locations.push(...locations);
|
|
1892
|
+
stats.count += locations.length;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
} catch {}
|
|
1896
|
+
});
|
|
1897
|
+
result.functions = Array.from(functionStatsMap.values());
|
|
1898
|
+
result.components = Array.from(componentStatsMap.values());
|
|
1899
|
+
return result;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
//#endregion
|
|
1903
|
+
//#region src/server/utils/module-counter.ts
|
|
1904
|
+
/**
|
|
1905
|
+
* 统计项目中通过 createPubinfo 注册的模块数量。
|
|
1906
|
+
*
|
|
1907
|
+
* 解析项目里的所有源文件,查找 createPubinfo 调用,并分析其 options.modules
|
|
1908
|
+
* 字段(含本地变量引用与展开语法)来估算注册的模块个数。
|
|
1909
|
+
*/
|
|
1910
|
+
function countRegisteredModules(projectRoot) {
|
|
1911
|
+
const files = getAllSourceFiles(projectRoot);
|
|
1912
|
+
let totalModules = 0;
|
|
1913
|
+
for (const filePath of files) {
|
|
1914
|
+
let content;
|
|
1915
|
+
try {
|
|
1916
|
+
content = readFileSync(filePath, "utf-8");
|
|
1917
|
+
} catch {
|
|
1918
|
+
continue;
|
|
1919
|
+
}
|
|
1920
|
+
if (!content.includes("createPubinfo")) continue;
|
|
1921
|
+
try {
|
|
1922
|
+
const rootNode = parse(getLang(filePath), content).root();
|
|
1923
|
+
const context = {
|
|
1924
|
+
rootNode,
|
|
1925
|
+
initializerCache: /* @__PURE__ */ new Map()
|
|
1926
|
+
};
|
|
1927
|
+
const callExpressions = rootNode.findAll({ rule: { kind: "call_expression" } });
|
|
1928
|
+
for (const call of callExpressions) {
|
|
1929
|
+
if (!isCreatePubinfoCall(call)) continue;
|
|
1930
|
+
totalModules += extractModulesFromCall(call, context);
|
|
1931
|
+
}
|
|
1932
|
+
} catch {
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
return totalModules;
|
|
1937
|
+
}
|
|
1938
|
+
function isCreatePubinfoCall(callNode) {
|
|
1939
|
+
const name = callNode.field("function")?.text() ?? "";
|
|
1940
|
+
return name === "createPubinfo" || name.endsWith(".createPubinfo");
|
|
1941
|
+
}
|
|
1942
|
+
function extractModulesFromCall(callNode, context) {
|
|
1943
|
+
const optionsNode = resolveOptionsObject(callNode, context);
|
|
1944
|
+
if (!optionsNode) return 0;
|
|
1945
|
+
const modulesPair = findModulesPair(optionsNode);
|
|
1946
|
+
if (!modulesPair) return 0;
|
|
1947
|
+
const valueNode = modulesPair.field("value");
|
|
1948
|
+
if (!valueNode) return 0;
|
|
1949
|
+
return countModulesFromValue(valueNode, context, /* @__PURE__ */ new Set());
|
|
1950
|
+
}
|
|
1951
|
+
function resolveOptionsObject(callNode, context) {
|
|
1952
|
+
const argsNode = callNode.field("arguments");
|
|
1953
|
+
if (!argsNode) return null;
|
|
1954
|
+
const directObject = argsNode.children().find((child) => child.kind() === "object");
|
|
1955
|
+
if (directObject) return directObject;
|
|
1956
|
+
const firstArg = getFirstNamedChild(argsNode);
|
|
1957
|
+
if (firstArg?.kind() === "identifier") {
|
|
1958
|
+
const initializer = findInitializer(context, firstArg.text());
|
|
1959
|
+
if (initializer?.kind() === "object") return initializer;
|
|
1960
|
+
}
|
|
1961
|
+
return null;
|
|
1962
|
+
}
|
|
1963
|
+
function findModulesPair(objectNode) {
|
|
1964
|
+
const pairs = objectNode.children().filter((child) => child.kind() === "pair");
|
|
1965
|
+
for (const pair of pairs) {
|
|
1966
|
+
const key = pair.field("key");
|
|
1967
|
+
if ((key ? normalizeKeyText(key.text()) : null) === "modules") return pair;
|
|
1968
|
+
}
|
|
1969
|
+
return null;
|
|
1970
|
+
}
|
|
1971
|
+
function normalizeKeyText(raw) {
|
|
1972
|
+
if (raw.startsWith("'") || raw.startsWith("\"") || raw.startsWith("`")) return raw.slice(1, -1);
|
|
1973
|
+
return raw;
|
|
1974
|
+
}
|
|
1975
|
+
function countModulesFromValue(valueNode, context, visited) {
|
|
1976
|
+
switch (valueNode.kind()) {
|
|
1977
|
+
case "array": return countArrayEntries(valueNode, context, visited);
|
|
1978
|
+
case "identifier": return countIdentifierReference(valueNode.text(), context, visited);
|
|
1979
|
+
case "parenthesized_expression":
|
|
1980
|
+
case "as_expression":
|
|
1981
|
+
case "type_assertion":
|
|
1982
|
+
case "non_null_expression": {
|
|
1983
|
+
const inner = getFirstNamedChild(valueNode);
|
|
1984
|
+
return inner ? countModulesFromValue(inner, context, visited) : 0;
|
|
1985
|
+
}
|
|
1986
|
+
default: return 0;
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
function countArrayEntries(arrayNode, context, visited) {
|
|
1990
|
+
let count = 0;
|
|
1991
|
+
for (const child of arrayNode.children()) {
|
|
1992
|
+
if (!child.isNamed()) continue;
|
|
1993
|
+
if (child.kind() === "spread_element") {
|
|
1994
|
+
const spreadTarget = getFirstNamedChild(child);
|
|
1995
|
+
if (spreadTarget) count += countModulesFromValue(spreadTarget, context, visited);
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1998
|
+
count += 1;
|
|
1999
|
+
}
|
|
2000
|
+
return count;
|
|
2001
|
+
}
|
|
2002
|
+
function countIdentifierReference(identifierName, context, visited) {
|
|
2003
|
+
if (!identifierName || visited.has(identifierName)) return 0;
|
|
2004
|
+
visited.add(identifierName);
|
|
2005
|
+
const initializer = findInitializer(context, identifierName);
|
|
2006
|
+
let result = 0;
|
|
2007
|
+
if (initializer) result = countModulesFromValue(initializer, context, visited);
|
|
2008
|
+
visited.delete(identifierName);
|
|
2009
|
+
return result;
|
|
2010
|
+
}
|
|
2011
|
+
function findInitializer(context, identifierName) {
|
|
2012
|
+
if (context.initializerCache.has(identifierName)) return context.initializerCache.get(identifierName) ?? null;
|
|
2013
|
+
let found = null;
|
|
2014
|
+
const stack = [context.rootNode];
|
|
2015
|
+
while (stack.length > 0) {
|
|
2016
|
+
const node = stack.pop();
|
|
2017
|
+
if (node.kind() === "variable_declarator") {
|
|
2018
|
+
if (node.field("name")?.text() === identifierName) {
|
|
2019
|
+
const valueNode = node.field("value");
|
|
2020
|
+
if (valueNode) {
|
|
2021
|
+
found = valueNode;
|
|
2022
|
+
break;
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
stack.push(...node.children());
|
|
2027
|
+
}
|
|
2028
|
+
context.initializerCache.set(identifierName, found);
|
|
2029
|
+
return found;
|
|
2030
|
+
}
|
|
2031
|
+
function getFirstNamedChild(node) {
|
|
2032
|
+
for (const child of node.children()) if (child.isNamed()) return child;
|
|
2033
|
+
return null;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
//#endregion
|
|
2037
|
+
//#region src/server/controller/usage.ts
|
|
2038
|
+
/** 源码扩展名集合,用于判断文件变更是否需要触发增量更新 */
|
|
2039
|
+
const SOURCE_EXTENSIONS$1 = new Set([
|
|
2040
|
+
".ts",
|
|
2041
|
+
".tsx",
|
|
2042
|
+
".js",
|
|
2043
|
+
".jsx",
|
|
2044
|
+
".vue"
|
|
2045
|
+
]);
|
|
2046
|
+
let _cache = null;
|
|
2047
|
+
let _scanPromise = null;
|
|
2048
|
+
/**
|
|
2049
|
+
* 获取或初始化缓存。
|
|
2050
|
+
* 多个并发请求只会触发一次扫描(Promise 去重)。
|
|
2051
|
+
*/
|
|
2052
|
+
async function getOrCreateCache(projectRoot) {
|
|
2053
|
+
if (_cache) return _cache;
|
|
2054
|
+
if (_scanPromise) return _scanPromise;
|
|
2055
|
+
_scanPromise = (async () => {
|
|
2056
|
+
const stats = await scanUsageStats(projectRoot, functions, components);
|
|
2057
|
+
_cache = {
|
|
2058
|
+
stats,
|
|
2059
|
+
fileHits: buildFileHitsIndex(stats),
|
|
2060
|
+
timestamp: Date.now()
|
|
2061
|
+
};
|
|
2062
|
+
_scanPromise = null;
|
|
2063
|
+
return _cache;
|
|
2064
|
+
})();
|
|
2065
|
+
return _scanPromise;
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* 从扫描结果构建 "文件 → 命中" 索引,用于增量更新时快速清除旧数据。
|
|
2069
|
+
*/
|
|
2070
|
+
function buildFileHitsIndex(stats) {
|
|
2071
|
+
const map = /* @__PURE__ */ new Map();
|
|
2072
|
+
for (const item of stats.functions) for (const loc of item.locations) {
|
|
2073
|
+
if (!map.has(loc.file)) map.set(loc.file, {
|
|
2074
|
+
functions: /* @__PURE__ */ new Map(),
|
|
2075
|
+
components: /* @__PURE__ */ new Map()
|
|
2076
|
+
});
|
|
2077
|
+
const entry = map.get(loc.file);
|
|
2078
|
+
entry.functions.set(item.name, (entry.functions.get(item.name) ?? 0) + 1);
|
|
2079
|
+
}
|
|
2080
|
+
for (const item of stats.components) for (const loc of item.locations) {
|
|
2081
|
+
if (!map.has(loc.file)) map.set(loc.file, {
|
|
2082
|
+
functions: /* @__PURE__ */ new Map(),
|
|
2083
|
+
components: /* @__PURE__ */ new Map()
|
|
2084
|
+
});
|
|
2085
|
+
const entry = map.get(loc.file);
|
|
2086
|
+
entry.components.set(item.name, (entry.components.get(item.name) ?? 0) + 1);
|
|
2087
|
+
}
|
|
2088
|
+
return map;
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* 增量更新:当单个文件发生变更时,只重新扫描该文件。
|
|
2092
|
+
*
|
|
2093
|
+
* 流程:
|
|
2094
|
+
* 1. 从缓存中清除该文件的旧贡献值。
|
|
2095
|
+
* 2. 重新扫描该文件。
|
|
2096
|
+
* 3. 把新结果合并回缓存。
|
|
2097
|
+
*/
|
|
2098
|
+
async function incrementalUpdate(filePath) {
|
|
2099
|
+
if (!_cache) return;
|
|
2100
|
+
const ext = extname(filePath);
|
|
2101
|
+
if (!SOURCE_EXTENSIONS$1.has(ext)) return;
|
|
2102
|
+
const { stats, fileHits } = _cache;
|
|
2103
|
+
const oldEntry = fileHits.get(filePath);
|
|
2104
|
+
if (oldEntry) {
|
|
2105
|
+
for (const [name, count] of oldEntry.functions) {
|
|
2106
|
+
const item = stats.functions.find((f) => f.name === name);
|
|
2107
|
+
if (item) {
|
|
2108
|
+
item.count -= count;
|
|
2109
|
+
item.locations = item.locations.filter((loc) => loc.file !== filePath);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
for (const [name, count] of oldEntry.components) {
|
|
2113
|
+
const item = stats.components.find((c) => c.name === name);
|
|
2114
|
+
if (item) {
|
|
2115
|
+
item.count -= count;
|
|
2116
|
+
item.locations = item.locations.filter((loc) => loc.file !== filePath);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
fileHits.delete(filePath);
|
|
2120
|
+
}
|
|
2121
|
+
const result = await scanSingleFile(filePath, functions, components);
|
|
2122
|
+
if (!result) return;
|
|
2123
|
+
const newEntry = {
|
|
2124
|
+
functions: /* @__PURE__ */ new Map(),
|
|
2125
|
+
components: /* @__PURE__ */ new Map()
|
|
2126
|
+
};
|
|
2127
|
+
for (const [name, locations] of result.functions) {
|
|
2128
|
+
const item = stats.functions.find((f) => f.name === name);
|
|
2129
|
+
if (item) {
|
|
2130
|
+
item.locations.push(...locations);
|
|
2131
|
+
item.count += locations.length;
|
|
2132
|
+
}
|
|
2133
|
+
newEntry.functions.set(name, locations.length);
|
|
2134
|
+
}
|
|
2135
|
+
for (const [name, locations] of result.components) {
|
|
2136
|
+
const item = stats.components.find((c) => c.name === name);
|
|
2137
|
+
if (item) {
|
|
2138
|
+
item.locations.push(...locations);
|
|
2139
|
+
item.count += locations.length;
|
|
2140
|
+
}
|
|
2141
|
+
newEntry.components.set(name, locations.length);
|
|
2142
|
+
}
|
|
2143
|
+
if (newEntry.functions.size > 0 || newEntry.components.size > 0) fileHits.set(filePath, newEntry);
|
|
2144
|
+
_cache.timestamp = Date.now();
|
|
2145
|
+
}
|
|
2146
|
+
/**
|
|
2147
|
+
* 增量删除:当文件被删除时,清除其贡献。
|
|
2148
|
+
*/
|
|
2149
|
+
async function incrementalRemove(filePath) {
|
|
2150
|
+
if (!_cache) return;
|
|
2151
|
+
const { stats, fileHits } = _cache;
|
|
2152
|
+
const oldEntry = fileHits.get(filePath);
|
|
2153
|
+
if (!oldEntry) return;
|
|
2154
|
+
for (const [name, count] of oldEntry.functions) {
|
|
2155
|
+
const item = stats.functions.find((f) => f.name === name);
|
|
2156
|
+
if (item) {
|
|
2157
|
+
item.count -= count;
|
|
2158
|
+
item.locations = item.locations.filter((loc) => loc.file !== filePath);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
for (const [name, count] of oldEntry.components) {
|
|
2162
|
+
const item = stats.components.find((c) => c.name === name);
|
|
2163
|
+
if (item) {
|
|
2164
|
+
item.count -= count;
|
|
2165
|
+
item.locations = item.locations.filter((loc) => loc.file !== filePath);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
fileHits.delete(filePath);
|
|
2169
|
+
_cache.timestamp = Date.now();
|
|
2170
|
+
}
|
|
2171
|
+
/**
|
|
2172
|
+
* 将 PascalCase/CamelCase 名称转换为 kebab-case(用于搜索匹配)
|
|
2173
|
+
*/
|
|
2174
|
+
function toKebabCase(name) {
|
|
2175
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
2176
|
+
}
|
|
2177
|
+
/**
|
|
2178
|
+
* 获取 Overview 数据
|
|
2179
|
+
* 返回项目中实际使用的 functions 和 components(仅 count > 0)
|
|
2180
|
+
*/
|
|
2181
|
+
async function getOverview(projectRoot) {
|
|
2182
|
+
const stats = (await getOrCreateCache(projectRoot)).stats;
|
|
2183
|
+
const apiMethodCount = (await getRequestList(projectRoot)).modules.reduce((total, module) => {
|
|
2184
|
+
return total + module.functions.length;
|
|
2185
|
+
}, 0);
|
|
2186
|
+
const moduleCount = countRegisteredModules(projectRoot);
|
|
2187
|
+
return {
|
|
2188
|
+
functions: stats.functions.filter((item) => item.count > 0),
|
|
2189
|
+
components: stats.components.filter((item) => item.count > 0),
|
|
2190
|
+
totalFiles: stats.totalFiles,
|
|
2191
|
+
scannedFiles: stats.scannedFiles,
|
|
2192
|
+
apiMethodCount,
|
|
2193
|
+
moduleCount
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
/**
|
|
2197
|
+
* 获取完整的使用统计数据
|
|
2198
|
+
* 返回所有追踪项的使用情况(包括未使用的 count = 0)
|
|
2199
|
+
*/
|
|
2200
|
+
async function getUsageStats(projectRoot) {
|
|
2201
|
+
return (await getOrCreateCache(projectRoot)).stats;
|
|
2202
|
+
}
|
|
2203
|
+
/**
|
|
2204
|
+
* 获取组件使用详情,附带统计摘要
|
|
2205
|
+
*/
|
|
2206
|
+
async function getComponentUsage(projectRoot) {
|
|
2207
|
+
const componentsWithMeta = (await getOrCreateCache(projectRoot)).stats.components.map((component) => {
|
|
2208
|
+
const dependents = Array.from(new Set(component.locations.map((location) => location.file))).sort();
|
|
2209
|
+
const componentFilePath = component.locations[0]?.file;
|
|
2210
|
+
return {
|
|
2211
|
+
...component,
|
|
2212
|
+
pascalName: component.name,
|
|
2213
|
+
kebabName: toKebabCase(component.name),
|
|
2214
|
+
filePath: componentFilePath,
|
|
2215
|
+
dependents,
|
|
2216
|
+
dependencies: []
|
|
2217
|
+
};
|
|
2218
|
+
});
|
|
2219
|
+
const used = componentsWithMeta.filter((item) => item.count > 0).length;
|
|
2220
|
+
return {
|
|
2221
|
+
components: componentsWithMeta,
|
|
2222
|
+
stats: {
|
|
2223
|
+
total: componentsWithMeta.length,
|
|
2224
|
+
used,
|
|
2225
|
+
unused: componentsWithMeta.length - used
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* 获取 Import/Hook 使用详情,附带统计摘要
|
|
2231
|
+
*/
|
|
2232
|
+
async function getImportUsage(projectRoot) {
|
|
2233
|
+
const importsWithMeta = (await getOrCreateCache(projectRoot)).stats.functions.map((importItem) => {
|
|
2234
|
+
const dependents = Array.from(new Set(importItem.locations.map((location) => location.file))).sort();
|
|
2235
|
+
const importFilePath = importItem.locations[0]?.file;
|
|
2236
|
+
return {
|
|
2237
|
+
...importItem,
|
|
2238
|
+
camelName: importItem.name,
|
|
2239
|
+
filePath: importFilePath,
|
|
2240
|
+
dependents
|
|
2241
|
+
};
|
|
2242
|
+
});
|
|
2243
|
+
const used = importsWithMeta.filter((item) => item.count > 0).length;
|
|
2244
|
+
return {
|
|
2245
|
+
imports: importsWithMeta,
|
|
2246
|
+
stats: {
|
|
2247
|
+
total: importsWithMeta.length,
|
|
2248
|
+
used,
|
|
2249
|
+
unused: importsWithMeta.length - used
|
|
2250
|
+
}
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
//#endregion
|
|
2255
|
+
//#region src/server/controller/version.ts
|
|
2256
|
+
/**
|
|
2257
|
+
* 从 node_modules 中查找实际安装的包版本
|
|
2258
|
+
* 这比从 package.json 读取更准确,因为反映了实际安装的版本
|
|
2259
|
+
*/
|
|
2260
|
+
function findPackageVersion(root, packageName) {
|
|
2261
|
+
try {
|
|
2262
|
+
const nodeModulesPath = join(root, "node_modules", packageName, "package.json");
|
|
2263
|
+
if (!existsSync(nodeModulesPath)) {
|
|
2264
|
+
const parentNodeModulesPath = join(root, "..", "node_modules", packageName, "package.json");
|
|
2265
|
+
if (!existsSync(parentNodeModulesPath)) return null;
|
|
2266
|
+
return JSON.parse(readFileSync(parentNodeModulesPath, "utf-8")).version || null;
|
|
2267
|
+
}
|
|
2268
|
+
return JSON.parse(readFileSync(nodeModulesPath, "utf-8")).version || null;
|
|
2269
|
+
} catch (error) {
|
|
2270
|
+
console.error(`Error reading package version from node_modules: ${error}`);
|
|
2271
|
+
return null;
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
* 从 fast-npm-meta 服务获取包的最新版本
|
|
2276
|
+
*/
|
|
2277
|
+
async function fetchLatestVersion(packageName) {
|
|
2278
|
+
try {
|
|
2279
|
+
const response = await fetch(`https://npm.elonehoo.cn/${packageName}`);
|
|
2280
|
+
if (!response.ok) {
|
|
2281
|
+
console.error(`Failed to fetch package info: ${response.status} ${response.statusText}`);
|
|
2282
|
+
return null;
|
|
2283
|
+
}
|
|
2284
|
+
return (await response.json()).version || null;
|
|
2285
|
+
} catch (error) {
|
|
2286
|
+
console.error(`Error fetching latest version from npm meta service: ${error}`);
|
|
2287
|
+
return null;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* 比较两个语义化版本号
|
|
2292
|
+
* 返回 true 如果 current 版本低于 latest 版本
|
|
2293
|
+
*/
|
|
2294
|
+
function isOutdated(current, latest) {
|
|
2295
|
+
if (!current || !latest) return false;
|
|
2296
|
+
try {
|
|
2297
|
+
const currentParts = current.split(".").map(Number);
|
|
2298
|
+
const latestParts = latest.split(".").map(Number);
|
|
2299
|
+
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
|
|
2300
|
+
const currentPart = currentParts[i] || 0;
|
|
2301
|
+
const latestPart = latestParts[i] || 0;
|
|
2302
|
+
if (currentPart < latestPart) return true;
|
|
2303
|
+
if (currentPart > latestPart) return false;
|
|
2304
|
+
}
|
|
2305
|
+
return false;
|
|
2306
|
+
} catch (error) {
|
|
2307
|
+
console.error(`Error comparing versions: ${error}`);
|
|
2308
|
+
return false;
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
/**
|
|
2312
|
+
* 获取包的版本信息
|
|
2313
|
+
*/
|
|
2314
|
+
async function getPackageVersion(root, packageName) {
|
|
2315
|
+
try {
|
|
2316
|
+
if (!packageName || typeof packageName !== "string") return {
|
|
2317
|
+
packageName: packageName || "",
|
|
2318
|
+
currentVersion: null,
|
|
2319
|
+
latestVersion: null,
|
|
2320
|
+
isOutdated: false,
|
|
2321
|
+
error: "Package name is required"
|
|
2322
|
+
};
|
|
2323
|
+
const currentVersion = findPackageVersion(root, packageName);
|
|
2324
|
+
const latestVersion = await fetchLatestVersion(packageName);
|
|
2325
|
+
return {
|
|
2326
|
+
packageName,
|
|
2327
|
+
currentVersion,
|
|
2328
|
+
latestVersion,
|
|
2329
|
+
isOutdated: isOutdated(currentVersion, latestVersion)
|
|
2330
|
+
};
|
|
2331
|
+
} catch (error) {
|
|
2332
|
+
return {
|
|
2333
|
+
packageName,
|
|
2334
|
+
currentVersion: null,
|
|
2335
|
+
latestVersion: null,
|
|
2336
|
+
isOutdated: false,
|
|
2337
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
//#endregion
|
|
2343
|
+
//#region src/server/index.ts
|
|
2344
|
+
/**
|
|
2345
|
+
* Pubinfo Devtools 的 Vite 插件入口。
|
|
2346
|
+
*
|
|
2347
|
+
* 能力概览:
|
|
2348
|
+
* - 提供一个轻量 UI(iframe)静态资源服务:`/__pubinfo_devtools`。
|
|
2349
|
+
* - 提供 devtools 专用的 API:`/__pubinfo_devtools_api`。
|
|
2350
|
+
* - `/overview` → 返回项目中实际使用的方法和组件。
|
|
2351
|
+
* - `/usage-stats` → 返回完整使用统计。
|
|
2352
|
+
* - `/component-usage` → 返回组件使用详情。
|
|
2353
|
+
* - `/import-usage` → 返回 Import/Hook 使用详情。
|
|
2354
|
+
* - `/api-list` → 返回解析的 OpenAPI 配置与扫描到的 API 函数/类型清单。
|
|
2355
|
+
* - `/package-version` → 返回包版本信息。
|
|
2356
|
+
*
|
|
2357
|
+
* 性能优化:
|
|
2358
|
+
* - 首次请求时执行全量扫描并缓存结果。
|
|
2359
|
+
* - 借助 Vite watcher 监听文件变化,仅对变更文件做增量更新。
|
|
2360
|
+
* - 所有 API 共享同一份缓存,避免重复扫描。
|
|
2361
|
+
* - JSON 响应不做美化格式化(减少 ~30% 体积)。
|
|
2362
|
+
*
|
|
2363
|
+
* 注意:本插件默认以 `enforce: 'pre'` 注入,避免被其他中间件拦截。
|
|
2364
|
+
* 路由均在开发服务器中间件阶段生效,不影响生产构建输出。
|
|
2365
|
+
*
|
|
2366
|
+
* 安全说明:这些接口仅用于本地开发环境调试,不应暴露到公网。
|
|
2367
|
+
*/
|
|
2368
|
+
/** 源码文件扩展名,与 ast-analyzer 保持一致 */
|
|
2369
|
+
const SOURCE_EXTENSIONS = new Set([
|
|
2370
|
+
".ts",
|
|
2371
|
+
".tsx",
|
|
2372
|
+
".js",
|
|
2373
|
+
".jsx",
|
|
2374
|
+
".vue"
|
|
2375
|
+
]);
|
|
2376
|
+
function isSourceFile(path) {
|
|
2377
|
+
const dot = path.lastIndexOf(".");
|
|
2378
|
+
return dot !== -1 && SOURCE_EXTENSIONS.has(path.slice(dot));
|
|
2379
|
+
}
|
|
2380
|
+
function pubinfoDevtools() {
|
|
2381
|
+
return {
|
|
2382
|
+
name: "vite-plugin-pubinfo-devtools",
|
|
2383
|
+
enforce: "pre",
|
|
2384
|
+
configureServer(server) {
|
|
2385
|
+
const baseURL = "__pubinfo_devtools";
|
|
2386
|
+
server.watcher.on("change", (filePath) => {
|
|
2387
|
+
if (isSourceFile(filePath)) incrementalUpdate(filePath).catch(() => {});
|
|
2388
|
+
});
|
|
2389
|
+
server.watcher.on("add", (filePath) => {
|
|
2390
|
+
if (isSourceFile(filePath)) incrementalUpdate(filePath).catch(() => {});
|
|
2391
|
+
});
|
|
2392
|
+
server.watcher.on("unlink", (filePath) => {
|
|
2393
|
+
if (isSourceFile(filePath)) incrementalRemove(filePath).catch(() => {});
|
|
2394
|
+
});
|
|
2395
|
+
server.middlewares.use(`/${baseURL}`, sirv(DIR_CLIENT, {
|
|
2396
|
+
single: true,
|
|
2397
|
+
dev: true
|
|
2398
|
+
}));
|
|
2399
|
+
server.middlewares.use(`/${baseURL}_api`, async (req, res, next) => {
|
|
2400
|
+
if (!req.url) return next();
|
|
2401
|
+
if (req.url === "/overview") {
|
|
2402
|
+
try {
|
|
2403
|
+
sendJSON(res, await getOverview(server.config.root));
|
|
2404
|
+
} catch (error) {
|
|
2405
|
+
sendError(res, "Failed to get overview data", error);
|
|
2406
|
+
}
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
if (req.url === "/usage-stats") {
|
|
2410
|
+
try {
|
|
2411
|
+
sendJSON(res, await getUsageStats(server.config.root));
|
|
2412
|
+
} catch (error) {
|
|
2413
|
+
sendError(res, "Failed to get usage statistics", error);
|
|
2414
|
+
}
|
|
2415
|
+
return;
|
|
2416
|
+
}
|
|
2417
|
+
if (req.url === "/component-usage") {
|
|
2418
|
+
try {
|
|
2419
|
+
sendJSON(res, await getComponentUsage(server.config.root));
|
|
2420
|
+
} catch (error) {
|
|
2421
|
+
sendError(res, "Failed to get component usage", error);
|
|
2422
|
+
}
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
if (req.url === "/import-usage") {
|
|
2426
|
+
try {
|
|
2427
|
+
sendJSON(res, await getImportUsage(server.config.root));
|
|
2428
|
+
} catch (error) {
|
|
2429
|
+
sendError(res, "Failed to get import usage", error);
|
|
2430
|
+
}
|
|
2431
|
+
return;
|
|
2432
|
+
}
|
|
2433
|
+
if (req.url.startsWith("/api-list")) {
|
|
2434
|
+
try {
|
|
2435
|
+
sendJSON(res, await getRequestList(server.config.root));
|
|
2436
|
+
} catch (error) {
|
|
2437
|
+
sendError(res, "Failed to get API list", error);
|
|
2438
|
+
}
|
|
2439
|
+
return;
|
|
2440
|
+
}
|
|
2441
|
+
if (req.url.startsWith("/package-version")) {
|
|
2442
|
+
try {
|
|
2443
|
+
const packageName = new URLSearchParams(req.url.slice(16)).get("package") || "";
|
|
2444
|
+
if (!packageName) {
|
|
2445
|
+
res.statusCode = 400;
|
|
2446
|
+
sendJSON(res, {
|
|
2447
|
+
error: "Missing package parameter",
|
|
2448
|
+
message: "Please provide a package name using ?package=<package-name>"
|
|
2449
|
+
});
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
sendJSON(res, await getPackageVersion(server.config.root, packageName));
|
|
2453
|
+
} catch (error) {
|
|
2454
|
+
sendError(res, "Failed to get package version", error);
|
|
2455
|
+
}
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
if (req.url === "/environment-info") {
|
|
2459
|
+
try {
|
|
2460
|
+
sendJSON(res, getEnvironmentInfo(server.config.root));
|
|
2461
|
+
} catch (error) {
|
|
2462
|
+
sendError(res, "Failed to get environment info", error);
|
|
2463
|
+
}
|
|
2464
|
+
return;
|
|
2465
|
+
}
|
|
2466
|
+
if (req.url === "/issue" && req.method === "POST") {
|
|
2467
|
+
try {
|
|
2468
|
+
const chunks = [];
|
|
2469
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
2470
|
+
req.on("end", async () => {
|
|
2471
|
+
try {
|
|
2472
|
+
const body = JSON.parse(Buffer.concat(chunks).toString());
|
|
2473
|
+
sendJSON(res, await submitIssue(server.config.root, body));
|
|
2474
|
+
} catch (error) {
|
|
2475
|
+
sendError(res, "Failed to submit issue", error);
|
|
2476
|
+
}
|
|
2477
|
+
});
|
|
2478
|
+
} catch (error) {
|
|
2479
|
+
sendError(res, "Failed to submit issue", error);
|
|
2480
|
+
}
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2483
|
+
next();
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
/**
|
|
2489
|
+
* 发送 JSON 响应(不做美化,减小体积)
|
|
2490
|
+
*/
|
|
2491
|
+
function sendJSON(res, data) {
|
|
2492
|
+
res.setHeader("Content-Type", "application/json");
|
|
2493
|
+
res.end(JSON.stringify(data));
|
|
2494
|
+
}
|
|
2495
|
+
/**
|
|
2496
|
+
* 发送 500 错误响应
|
|
2497
|
+
*/
|
|
2498
|
+
function sendError(res, defaultMessage, error) {
|
|
2499
|
+
res.statusCode = 500;
|
|
2500
|
+
res.setHeader("Content-Type", "application/json");
|
|
2501
|
+
res.end(JSON.stringify({
|
|
2502
|
+
error: defaultMessage,
|
|
2503
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2504
|
+
}));
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
//#endregion
|
|
2508
|
+
export { pubinfoDevtools };
|