@pubinfo-pr/devtools 0.220.2 → 0.220.4
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/{fetch-DxuBXEk3.js → fetch-CwAFBmMG.js} +121 -4
- package/dist/client/assets/import-DV9l8S2T.js +375 -0
- package/dist/client/assets/{index-DcEJJh1_.js → index-ByOJyids.js} +34 -11
- 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/pages-D4LiKgxN.js +285 -0
- package/dist/client/assets/pages-DQ8FtB9Y.css +17 -0
- package/dist/client/assets/{server-router-8PynUpe5.css → server-router-B1AB70as.css} +0 -18
- package/dist/client/assets/{server-router-Bu5965f8.js → server-router-DuEpkdvh.js} +14 -225
- 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/index.d.mts +0 -14
- package/dist/index.mjs +1966 -50
- package/dist/panel/index.d.mts +1 -0
- package/dist/panel/index.mjs +97 -1
- package/package.json +7 -2
- package/dist/client/assets/index-D7UL6pHD.css +0 -443
- package/dist/client/assets/pages-CfWIiNIx.js +0 -20
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { DIR_CLIENT } from "./dirs.mjs";
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
2
3
|
import sirv from "sirv";
|
|
3
4
|
import { basename, dirname, extname, join, relative } from "node:path";
|
|
4
5
|
import { pathToFileURL } from "node:url";
|
|
5
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
6
|
-
import { readdir } from "node:fs/promises";
|
|
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";
|
|
7
12
|
|
|
8
13
|
//#region src/server/controller/api.ts
|
|
9
14
|
const REQUEST_MODULE_EXTENSIONS = [
|
|
@@ -486,49 +491,1852 @@ async function getRequestList(root) {
|
|
|
486
491
|
}
|
|
487
492
|
|
|
488
493
|
//#endregion
|
|
489
|
-
//#region src/server/
|
|
494
|
+
//#region src/server/controller/issue.ts
|
|
490
495
|
/**
|
|
491
|
-
*
|
|
492
|
-
* 这些目录通常体积较大或为构建产物,对健康检查意义不大。
|
|
496
|
+
* 安全执行 git 命令,失败时返回 null
|
|
493
497
|
*/
|
|
494
|
-
|
|
495
|
-
|
|
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",
|
|
496
1619
|
"dist",
|
|
497
|
-
"
|
|
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"
|
|
498
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
|
+
}
|
|
499
1710
|
/**
|
|
500
|
-
*
|
|
1711
|
+
* 对单个文件做一次性全量分析,返回该文件中出现的所有追踪名的使用位置。
|
|
501
1712
|
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
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
|
+
* 扫描项目中的使用情况(优化版)
|
|
505
1836
|
*
|
|
506
|
-
*
|
|
507
|
-
*
|
|
1837
|
+
* 相比旧版的优化:
|
|
1838
|
+
* 1. 异步 I/O + 并发读取文件,不阻塞 Vite dev server。
|
|
1839
|
+
* 2. 每文件只做一次 AST 解析,遍历一次收集所有命中。
|
|
1840
|
+
* 3. 快速预过滤(string.includes)跳过不相关文件。
|
|
1841
|
+
* 4. Vue template 使用联合正则一次匹配,二分法定位行号。
|
|
508
1842
|
*
|
|
509
|
-
* @param
|
|
510
|
-
* @
|
|
1843
|
+
* @param projectRoot 项目根目录
|
|
1844
|
+
* @param functionsToTrack 要追踪的函数列表
|
|
1845
|
+
* @param componentsToTrack 要追踪的组件列表
|
|
511
1846
|
*/
|
|
512
|
-
async function
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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()
|
|
519
2076
|
});
|
|
520
|
-
|
|
521
|
-
|
|
2077
|
+
const entry = map.get(loc.file);
|
|
2078
|
+
entry.functions.set(item.name, (entry.functions.get(item.name) ?? 0) + 1);
|
|
522
2079
|
}
|
|
523
|
-
for (const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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
|
+
};
|
|
530
2339
|
}
|
|
531
|
-
return total;
|
|
532
2340
|
}
|
|
533
2341
|
|
|
534
2342
|
//#endregion
|
|
@@ -539,47 +2347,137 @@ async function countFiles(root) {
|
|
|
539
2347
|
* 能力概览:
|
|
540
2348
|
* - 提供一个轻量 UI(iframe)静态资源服务:`/__pubinfo_devtools`。
|
|
541
2349
|
* - 提供 devtools 专用的 API:`/__pubinfo_devtools_api`。
|
|
542
|
-
* -
|
|
2350
|
+
* - `/overview` → 返回项目中实际使用的方法和组件。
|
|
2351
|
+
* - `/usage-stats` → 返回完整使用统计。
|
|
2352
|
+
* - `/component-usage` → 返回组件使用详情。
|
|
2353
|
+
* - `/import-usage` → 返回 Import/Hook 使用详情。
|
|
543
2354
|
* - `/api-list` → 返回解析的 OpenAPI 配置与扫描到的 API 函数/类型清单。
|
|
2355
|
+
* - `/package-version` → 返回包版本信息。
|
|
2356
|
+
*
|
|
2357
|
+
* 性能优化:
|
|
2358
|
+
* - 首次请求时执行全量扫描并缓存结果。
|
|
2359
|
+
* - 借助 Vite watcher 监听文件变化,仅对变更文件做增量更新。
|
|
2360
|
+
* - 所有 API 共享同一份缓存,避免重复扫描。
|
|
2361
|
+
* - JSON 响应不做美化格式化(减少 ~30% 体积)。
|
|
544
2362
|
*
|
|
545
2363
|
* 注意:本插件默认以 `enforce: 'pre'` 注入,避免被其他中间件拦截。
|
|
546
2364
|
* 路由均在开发服务器中间件阶段生效,不影响生产构建输出。
|
|
547
2365
|
*
|
|
548
2366
|
* 安全说明:这些接口仅用于本地开发环境调试,不应暴露到公网。
|
|
549
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
|
+
}
|
|
550
2380
|
function pubinfoDevtools() {
|
|
551
2381
|
return {
|
|
552
|
-
name: "vite-plugin-
|
|
2382
|
+
name: "vite-plugin-pubinfo-devtools",
|
|
553
2383
|
enforce: "pre",
|
|
554
2384
|
configureServer(server) {
|
|
555
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
|
+
});
|
|
556
2395
|
server.middlewares.use(`/${baseURL}`, sirv(DIR_CLIENT, {
|
|
557
2396
|
single: true,
|
|
558
2397
|
dev: true
|
|
559
2398
|
}));
|
|
560
2399
|
server.middlewares.use(`/${baseURL}_api`, async (req, res, next) => {
|
|
561
2400
|
if (!req.url) return next();
|
|
562
|
-
if (req.url === "/") {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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
|
+
}
|
|
567
2431
|
return;
|
|
568
2432
|
}
|
|
569
2433
|
if (req.url.startsWith("/api-list")) {
|
|
570
2434
|
try {
|
|
571
|
-
|
|
572
|
-
res.setHeader("Content-Type", "application/json");
|
|
573
|
-
res.write(JSON.stringify(apiList, null, 2));
|
|
2435
|
+
sendJSON(res, await getRequestList(server.config.root));
|
|
574
2436
|
} catch (error) {
|
|
575
|
-
res
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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);
|
|
581
2480
|
}
|
|
582
|
-
res.end();
|
|
583
2481
|
return;
|
|
584
2482
|
}
|
|
585
2483
|
next();
|
|
@@ -587,6 +2485,24 @@ function pubinfoDevtools() {
|
|
|
587
2485
|
}
|
|
588
2486
|
};
|
|
589
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
|
+
}
|
|
590
2506
|
|
|
591
2507
|
//#endregion
|
|
592
2508
|
export { pubinfoDevtools };
|