king-design-analyzer 2.1.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/components/actions.json +101 -0
  2. package/components/advancedset.json +127 -0
  3. package/components/affix.json +7 -0
  4. package/components/aksk.json +172 -0
  5. package/components/az.json +15 -2
  6. package/components/badge.json +7 -0
  7. package/components/billtypes.json +14 -2
  8. package/components/breadcrumb.json +7 -0
  9. package/components/button.json +191 -0
  10. package/components/buttonlink.json +73 -0
  11. package/components/card.json +8 -1
  12. package/components/cardcol.json +142 -0
  13. package/components/cardcols.json +121 -0
  14. package/components/cardcontent.json +15 -3
  15. package/components/cardtabs.json +172 -0
  16. package/components/carousel.json +7 -0
  17. package/components/cascader.json +520 -368
  18. package/components/checkbox.json +51 -4
  19. package/components/cidrinput.json +140 -0
  20. package/components/code.json +8 -1
  21. package/components/collapse.json +7 -0
  22. package/components/colorpicker.json +217 -0
  23. package/components/copy.json +7 -0
  24. package/components/copyhover.json +111 -0
  25. package/components/copyrow.json +124 -0
  26. package/components/datepicker.json +656 -524
  27. package/components/description.json +98 -0
  28. package/components/dialog.json +522 -455
  29. package/components/divider.json +8 -1
  30. package/components/drawer.json +571 -558
  31. package/components/dropdown.json +464 -384
  32. package/components/duration.json +125 -0
  33. package/components/editable.json +66 -0
  34. package/components/ellipsis.json +34 -1
  35. package/components/filtertablefieldsdialog.json +128 -0
  36. package/components/flex.json +151 -0
  37. package/components/form.json +131 -0
  38. package/components/formiteminput.json +214 -0
  39. package/components/formitemspinner.json +213 -0
  40. package/components/formitemswitch.json +185 -0
  41. package/components/formitemtableconfigs.json +210 -0
  42. package/components/grid.json +7 -0
  43. package/components/header.json +161 -0
  44. package/components/icon.json +13 -1
  45. package/components/icontooltip.json +19 -1
  46. package/components/input.json +188 -4
  47. package/components/instancelist.json +194 -0
  48. package/components/instancenum.json +196 -0
  49. package/components/ipinput.json +117 -0
  50. package/components/kspstatus.json +205 -0
  51. package/components/kvcode.json +120 -0
  52. package/components/layoutcontent.json +15 -3
  53. package/components/layoutlistcontent.json +148 -0
  54. package/components/layoutpermissioncontent.json +155 -0
  55. package/components/layoutstandardlist.json +275 -0
  56. package/components/layouttabs.json +168 -0
  57. package/components/lazymount.json +86 -0
  58. package/components/lazyteleport.json +118 -0
  59. package/components/menu.json +7 -0
  60. package/components/pagination.json +12 -0
  61. package/components/paginationplus.json +30 -3
  62. package/components/password.json +121 -0
  63. package/components/popover.json +457 -437
  64. package/components/projects.json +129 -0
  65. package/components/protable.json +97 -8
  66. package/components/queuevisualrange.json +110 -0
  67. package/components/radio.json +82 -2
  68. package/components/rate.json +199 -0
  69. package/components/region.json +14 -2
  70. package/components/search.json +221 -0
  71. package/components/searchinput.json +132 -0
  72. package/components/searchitems.json +215 -0
  73. package/components/searchselect.json +195 -0
  74. package/components/select.json +686 -666
  75. package/components/sidebar.json +198 -0
  76. package/components/skeleton.json +233 -0
  77. package/components/spin.json +8 -1
  78. package/components/split.json +260 -0
  79. package/components/sshkeys.json +138 -0
  80. package/components/status.json +60 -3
  81. package/components/steps.json +7 -0
  82. package/components/switch.json +8 -1
  83. package/components/table.json +18 -1
  84. package/components/tablecolumnid.json +14 -2
  85. package/components/tabs.json +12 -0
  86. package/components/timeline.json +213 -0
  87. package/components/timepicker.json +147 -3
  88. package/components/timerange.json +262 -0
  89. package/components/tip.json +7 -0
  90. package/components/tooltip.json +484 -395
  91. package/components/tour.json +418 -372
  92. package/components/transfer.json +27 -3
  93. package/components/tree.json +6 -4
  94. package/components/treeselect.json +556 -475
  95. package/components/upload.json +474 -473
  96. package/components/vdialog.json +308 -0
  97. package/components/vdrawer.json +331 -0
  98. package/components/virtuallist.json +7 -0
  99. package/dist/ast/index.d.mts +35 -1
  100. package/dist/ast/index.d.ts +35 -1
  101. package/dist/ast/index.js +5 -3
  102. package/dist/ast/index.mjs +3 -1
  103. package/dist/{chunk-DSWKLUIX.mjs → chunk-3LYQ5XFM.mjs} +1 -1
  104. package/dist/chunk-BI5TIQID.mjs +330 -0
  105. package/dist/{chunk-6QS5IGS6.js → chunk-CJGGFVQJ.js} +8 -8
  106. package/dist/chunk-CR3GC4H3.js +353 -0
  107. package/dist/{chunk-NZ6TLWMD.mjs → chunk-D2SXGGTX.mjs} +28 -17
  108. package/dist/{chunk-D3Y6FGWA.js → chunk-EYKZY2F3.js} +29 -18
  109. package/dist/chunk-JSBRDJBE.js +30 -0
  110. package/dist/chunk-KF5YBEM5.js +143 -0
  111. package/dist/chunk-KMIDURUR.mjs +33324 -0
  112. package/dist/chunk-QC6VTSA3.mjs +117 -0
  113. package/dist/chunk-SZYVGYKK.js +33350 -0
  114. package/dist/{chunk-WYSRJVX4.js → chunk-TA3SV4SP.js} +2 -2
  115. package/dist/chunk-UJCSKKID.mjs +27 -0
  116. package/dist/{chunk-FVSQ6BU6.mjs → chunk-XGPHQHLR.mjs} +4 -4
  117. package/dist/docs/index.js +1 -1
  118. package/dist/docs/index.mjs +1 -1
  119. package/dist/full/index.js +8 -6
  120. package/dist/full/index.mjs +6 -4
  121. package/dist/index.d.mts +1 -0
  122. package/dist/index.d.ts +1 -0
  123. package/dist/index.js +15 -13
  124. package/dist/index.mjs +7 -5
  125. package/dist/metadata/index.d.mts +22 -2
  126. package/dist/metadata/index.d.ts +22 -2
  127. package/dist/metadata/index.js +27 -17
  128. package/dist/metadata/index.mjs +27 -16
  129. package/dist/resolve-BsLBxlBi.d.mts +24 -0
  130. package/dist/resolve-BsLBxlBi.d.ts +24 -0
  131. package/dist/runtime/index.js +5 -4
  132. package/dist/runtime/index.mjs +3 -2
  133. package/dist/shared/index.d.mts +10 -0
  134. package/dist/shared/index.d.ts +10 -0
  135. package/dist/shared/index.js +23 -0
  136. package/dist/shared/index.mjs +2 -0
  137. package/dist/static/index.js +6 -5
  138. package/dist/static/index.mjs +3 -2
  139. package/docs_for_llm/actions.doc.md +77 -0
  140. package/docs_for_llm/advancedset.doc.md +89 -0
  141. package/docs_for_llm/affix.doc.md +15 -7
  142. package/docs_for_llm/aksk.doc.md +111 -0
  143. package/docs_for_llm/az.doc.md +17 -11
  144. package/docs_for_llm/badge.doc.md +6 -5
  145. package/docs_for_llm/billtypes.doc.md +17 -11
  146. package/docs_for_llm/breadcrumb.doc.md +6 -3
  147. package/docs_for_llm/button.doc.md +34 -18
  148. package/docs_for_llm/buttonlink.doc.md +55 -0
  149. package/docs_for_llm/card.doc.md +8 -5
  150. package/docs_for_llm/cardcol.doc.md +78 -0
  151. package/docs_for_llm/cardcols.doc.md +84 -0
  152. package/docs_for_llm/cardcontent.doc.md +10 -10
  153. package/docs_for_llm/cardtabs.doc.md +106 -0
  154. package/docs_for_llm/carousel.doc.md +8 -7
  155. package/docs_for_llm/cascader.doc.md +46 -25
  156. package/docs_for_llm/checkbox.doc.md +25 -13
  157. package/docs_for_llm/cidrinput.doc.md +83 -0
  158. package/docs_for_llm/code.doc.md +22 -11
  159. package/docs_for_llm/collapse.doc.md +9 -6
  160. package/docs_for_llm/colorpicker.doc.md +104 -0
  161. package/docs_for_llm/copy.doc.md +15 -13
  162. package/docs_for_llm/copyhover.doc.md +59 -0
  163. package/docs_for_llm/copyrow.doc.md +71 -0
  164. package/docs_for_llm/datepicker.doc.md +51 -39
  165. package/docs_for_llm/description.doc.md +75 -0
  166. package/docs_for_llm/descriptions.doc.md +9 -6
  167. package/docs_for_llm/dialog.doc.md +48 -77
  168. package/docs_for_llm/divider.doc.md +9 -7
  169. package/docs_for_llm/drawer.doc.md +43 -84
  170. package/docs_for_llm/dropdown.doc.md +52 -21
  171. package/docs_for_llm/duration.doc.md +76 -0
  172. package/docs_for_llm/editable.doc.md +36 -14
  173. package/docs_for_llm/ellipsis.doc.md +10 -5
  174. package/docs_for_llm/filtertablefieldsdialog.doc.md +99 -0
  175. package/docs_for_llm/flex.doc.md +74 -0
  176. package/docs_for_llm/form.doc.md +33 -14
  177. package/docs_for_llm/formiteminput.doc.md +128 -0
  178. package/docs_for_llm/formitemspinner.doc.md +105 -0
  179. package/docs_for_llm/formitemswitch.doc.md +113 -0
  180. package/docs_for_llm/formitemtableconfigs.doc.md +95 -0
  181. package/docs_for_llm/grid.doc.md +7 -5
  182. package/docs_for_llm/header.doc.md +85 -0
  183. package/docs_for_llm/icon.doc.md +12 -8
  184. package/docs_for_llm/icontooltip.doc.md +121 -0
  185. package/docs_for_llm/input.doc.md +90 -39
  186. package/docs_for_llm/instancelist.doc.md +113 -0
  187. package/docs_for_llm/instancenum.doc.md +95 -0
  188. package/docs_for_llm/ipinput.doc.md +77 -0
  189. package/docs_for_llm/kspstatus.doc.md +82 -0
  190. package/docs_for_llm/kvcode.doc.md +76 -0
  191. package/docs_for_llm/layoutcontent.doc.md +17 -17
  192. package/docs_for_llm/layoutlistcontent.doc.md +95 -0
  193. package/docs_for_llm/layoutpermissioncontent.doc.md +84 -0
  194. package/docs_for_llm/layoutstandardlist.doc.md +120 -0
  195. package/docs_for_llm/layouttabs.doc.md +101 -0
  196. package/docs_for_llm/lazymount.doc.md +73 -0
  197. package/docs_for_llm/lazyteleport.doc.md +76 -0
  198. package/docs_for_llm/menu.doc.md +14 -11
  199. package/docs_for_llm/message.doc.md +48 -10
  200. package/docs_for_llm/pagination.doc.md +30 -17
  201. package/docs_for_llm/paginationplus.doc.md +31 -10
  202. package/docs_for_llm/password.doc.md +86 -0
  203. package/docs_for_llm/popover.doc.md +24 -55
  204. package/docs_for_llm/progress.doc.md +13 -10
  205. package/docs_for_llm/projects.doc.md +91 -0
  206. package/docs_for_llm/protable.doc.md +89 -15
  207. package/docs_for_llm/queuevisualrange.doc.md +78 -0
  208. package/docs_for_llm/radio.doc.md +23 -8
  209. package/docs_for_llm/rate.doc.md +122 -0
  210. package/docs_for_llm/region.doc.md +25 -13
  211. package/docs_for_llm/search.doc.md +120 -0
  212. package/docs_for_llm/searchinput.doc.md +111 -0
  213. package/docs_for_llm/searchitems.doc.md +116 -0
  214. package/docs_for_llm/searchselect.doc.md +126 -0
  215. package/docs_for_llm/select.doc.md +40 -55
  216. package/docs_for_llm/sidebar.doc.md +133 -0
  217. package/docs_for_llm/skeleton.doc.md +117 -0
  218. package/docs_for_llm/slider.doc.md +31 -21
  219. package/docs_for_llm/spin.doc.md +8 -5
  220. package/docs_for_llm/spinner.doc.md +28 -18
  221. package/docs_for_llm/split.doc.md +157 -0
  222. package/docs_for_llm/sshkeys.doc.md +88 -0
  223. package/docs_for_llm/status.doc.md +14 -9
  224. package/docs_for_llm/steps.doc.md +10 -7
  225. package/docs_for_llm/switch.doc.md +17 -14
  226. package/docs_for_llm/table.doc.md +118 -51
  227. package/docs_for_llm/tablecolumnid.doc.md +36 -16
  228. package/docs_for_llm/tabs.doc.md +19 -10
  229. package/docs_for_llm/tag.doc.md +19 -10
  230. package/docs_for_llm/timeline.doc.md +109 -0
  231. package/docs_for_llm/timepicker.doc.md +57 -38
  232. package/docs_for_llm/timerange.doc.md +157 -0
  233. package/docs_for_llm/tip.doc.md +20 -11
  234. package/docs_for_llm/tooltip.doc.md +55 -29
  235. package/docs_for_llm/tour.doc.md +48 -29
  236. package/docs_for_llm/transfer.doc.md +40 -29
  237. package/docs_for_llm/tree.doc.md +65 -23
  238. package/docs_for_llm/treeselect.doc.md +37 -48
  239. package/docs_for_llm/upload.doc.md +56 -32
  240. package/docs_for_llm/useCRUD.doc.md +49 -0
  241. package/docs_for_llm/useCountdown.doc.md +43 -0
  242. package/docs_for_llm/useDetail.doc.md +48 -0
  243. package/docs_for_llm/useDialog.doc.md +46 -0
  244. package/docs_for_llm/useEntity.doc.md +45 -0
  245. package/docs_for_llm/useEventListener.doc.md +44 -0
  246. package/docs_for_llm/useFalseUntilTruthy.doc.md +41 -0
  247. package/docs_for_llm/useGetCopyAuthText.doc.md +35 -0
  248. package/docs_for_llm/useGetUserPermission.doc.md +43 -0
  249. package/docs_for_llm/useGroup.doc.md +48 -0
  250. package/docs_for_llm/useIdEntities.doc.md +48 -0
  251. package/docs_for_llm/useIdEntity.doc.md +103 -0
  252. package/docs_for_llm/useInterval.doc.md +43 -0
  253. package/docs_for_llm/useLayoutStandardList.doc.md +38 -0
  254. package/docs_for_llm/useMessage.doc.md +46 -0
  255. package/docs_for_llm/useModifyCache.doc.md +37 -0
  256. package/docs_for_llm/useOpenDialog.doc.md +41 -0
  257. package/docs_for_llm/usePagination.doc.md +60 -0
  258. package/docs_for_llm/usePersist.doc.md +54 -0
  259. package/docs_for_llm/usePoll.doc.md +41 -0
  260. package/docs_for_llm/useRoutePlus.doc.md +34 -0
  261. package/docs_for_llm/useRouterPlus.doc.md +34 -0
  262. package/docs_for_llm/useSearch.doc.md +68 -0
  263. package/docs_for_llm/useShell.doc.md +43 -0
  264. package/docs_for_llm/useSkip.doc.md +41 -0
  265. package/docs_for_llm/useStorageIdEntity.doc.md +46 -0
  266. package/docs_for_llm/useStore.doc.md +34 -0
  267. package/docs_for_llm/useTable.doc.md +57 -0
  268. package/docs_for_llm/useTableGroup.doc.md +47 -0
  269. package/docs_for_llm/useTableSort.doc.md +40 -0
  270. package/docs_for_llm/useToState.doc.md +112 -0
  271. package/docs_for_llm/useUser.doc.md +38 -0
  272. package/docs_for_llm/useValidForm.doc.md +41 -0
  273. package/docs_for_llm/vdialog.doc.md +129 -0
  274. package/docs_for_llm/vdrawer.doc.md +119 -0
  275. package/docs_for_llm/virtuallist.doc.md +6 -3
  276. package/hooks/useCRUD.json +61 -0
  277. package/hooks/useCountdown.json +49 -0
  278. package/hooks/useDetail.json +67 -0
  279. package/hooks/useDialog.json +49 -0
  280. package/hooks/useEntity.json +61 -0
  281. package/hooks/useEventListener.json +61 -0
  282. package/hooks/useFalseUntilTruthy.json +43 -0
  283. package/hooks/useGetCopyAuthText.json +36 -0
  284. package/hooks/useGetUserPermission.json +55 -0
  285. package/hooks/useGroup.json +49 -0
  286. package/hooks/useIdEntities.json +61 -0
  287. package/hooks/useInterval.json +55 -0
  288. package/hooks/useLayoutStandardList.json +36 -0
  289. package/hooks/useMessage.json +73 -0
  290. package/hooks/useModifyCache.json +36 -0
  291. package/hooks/useOpenDialog.json +43 -0
  292. package/hooks/usePagination.json +49 -0
  293. package/hooks/usePersist.json +61 -0
  294. package/hooks/usePoll.json +43 -0
  295. package/hooks/useRoutePlus.json +36 -0
  296. package/hooks/useRouterPlus.json +36 -0
  297. package/hooks/useSearch.json +56 -0
  298. package/hooks/useShell.json +49 -0
  299. package/hooks/useSkip.json +43 -0
  300. package/hooks/useStorageIdEntity.json +67 -0
  301. package/hooks/useStore.json +36 -0
  302. package/hooks/useTable.json +49 -0
  303. package/hooks/useTableGroup.json +43 -0
  304. package/hooks/useTableSort.json +43 -0
  305. package/hooks/useUser.json +36 -0
  306. package/hooks/useValidForm.json +43 -0
  307. package/package.json +12 -6
  308. package/dist/chunk-HDV7ETXE.mjs +0 -1055
  309. package/dist/chunk-HPAUCD5I.js +0 -156
  310. package/dist/chunk-L4DS3EXI.mjs +0 -133
  311. package/dist/chunk-YWY3D4J7.js +0 -1082
@@ -1,1055 +0,0 @@
1
- import * as fs from 'fs/promises';
2
- import * as fsSync from 'fs';
3
- import * as path from 'path';
4
- import { fileURLToPath } from 'url';
5
- import { parse } from '@vue/compiler-sfc';
6
- import * as ts from 'typescript';
7
-
8
- // src/analysis/componentRegistry.ts
9
- var __filename$1 = fileURLToPath(import.meta.url);
10
- var __dirname$1 = path.dirname(__filename$1);
11
- function resolveComponentsPath() {
12
- const prodPath = path.join(__dirname$1, "../components");
13
- if (fsSync.existsSync(prodPath)) return prodPath;
14
- const devPath = path.join(__dirname$1, "../../components");
15
- if (fsSync.existsSync(devPath)) return devPath;
16
- return prodPath;
17
- }
18
- var ComponentRegistry = class {
19
- constructor() {
20
- this.components = /* @__PURE__ */ new Map();
21
- this.loaded = false;
22
- this.watcher = null;
23
- // Path to components directory (resolved dynamically for dev/prod compatibility)
24
- this.metadataPath = resolveComponentsPath();
25
- }
26
- async load() {
27
- if (this.loaded) return;
28
- await this.reload();
29
- this.loaded = true;
30
- this.startWatching();
31
- }
32
- /**
33
- * 重新加载所有组件元数据
34
- */
35
- async reload() {
36
- const newComponents = /* @__PURE__ */ new Map();
37
- try {
38
- const files = await fs.readdir(this.metadataPath);
39
- for (const file of files) {
40
- if (!file.endsWith(".json")) continue;
41
- const content = await fs.readFile(path.join(this.metadataPath, file), "utf-8");
42
- const data = JSON.parse(content);
43
- newComponents.set(data.name, data);
44
- if (data.subComponents) {
45
- for (const sub of data.subComponents) {
46
- newComponents.set(sub.name, sub);
47
- }
48
- }
49
- }
50
- this.components = newComponents;
51
- console.log(`[ComponentRegistry] ${this.loaded ? "Reloaded" : "Loaded"} ${this.components.size} components.`);
52
- } catch (error) {
53
- console.error("[ComponentRegistry] Failed to load metadata:", error);
54
- }
55
- }
56
- /**
57
- * 开始监听文件变化
58
- */
59
- startWatching() {
60
- if (this.watcher) return;
61
- try {
62
- let reloadTimeout = null;
63
- this.watcher = fsSync.watch(this.metadataPath, (eventType, filename) => {
64
- if (!filename?.endsWith(".json")) return;
65
- console.log(`[ComponentRegistry] Detected change: ${filename} (${eventType})`);
66
- if (reloadTimeout) {
67
- clearTimeout(reloadTimeout);
68
- }
69
- reloadTimeout = setTimeout(() => {
70
- this.reload();
71
- }, 500);
72
- });
73
- console.log(`[ComponentRegistry] Hot reload enabled for: ${this.metadataPath}`);
74
- } catch (error) {
75
- console.warn("[ComponentRegistry] Could not enable hot reload:", error);
76
- }
77
- }
78
- /**
79
- * 停止监听
80
- */
81
- stopWatching() {
82
- if (this.watcher) {
83
- this.watcher.close();
84
- this.watcher = null;
85
- console.log("[ComponentRegistry] Hot reload disabled.");
86
- }
87
- }
88
- getComponent(name) {
89
- return this.components.get(name);
90
- }
91
- getAllComponentNames() {
92
- return Array.from(this.components.keys());
93
- }
94
- isKnownComponent(name) {
95
- return this.components.has(name);
96
- }
97
- };
98
- var componentRegistry = new ComponentRegistry();
99
- var __filename2 = fileURLToPath(import.meta.url);
100
- var __dirname2 = path.dirname(__filename2);
101
- function resolveHooksPath() {
102
- const prodPath = path.join(__dirname2, "../hooks");
103
- if (fsSync.existsSync(prodPath)) return prodPath;
104
- const devPath = path.join(__dirname2, "../../hooks");
105
- if (fsSync.existsSync(devPath)) return devPath;
106
- return prodPath;
107
- }
108
- var HooksRegistry = class {
109
- constructor() {
110
- this.hooks = /* @__PURE__ */ new Map();
111
- this.loaded = false;
112
- this.watcher = null;
113
- this.metadataPath = resolveHooksPath();
114
- }
115
- async load() {
116
- if (this.loaded) return;
117
- await this.reload();
118
- this.loaded = true;
119
- this.startWatching();
120
- }
121
- /**
122
- * 重新加载所有 hooks 元数据
123
- */
124
- async reload() {
125
- const newHooks = /* @__PURE__ */ new Map();
126
- try {
127
- const files = await fs.readdir(this.metadataPath);
128
- for (const file of files) {
129
- if (!file.endsWith(".json")) continue;
130
- const content = await fs.readFile(path.join(this.metadataPath, file), "utf-8");
131
- const data = JSON.parse(content);
132
- newHooks.set(data.name, data);
133
- }
134
- this.hooks = newHooks;
135
- if (this.hooks.size > 0) {
136
- console.log(`[HooksRegistry] ${this.loaded ? "Reloaded" : "Loaded"} ${this.hooks.size} hooks.`);
137
- }
138
- } catch (error) {
139
- if (error.code !== "ENOENT") {
140
- console.error("[HooksRegistry] Failed to load metadata:", error);
141
- }
142
- }
143
- }
144
- /**
145
- * 开始监听文件变化
146
- */
147
- startWatching() {
148
- if (this.watcher) return;
149
- try {
150
- let reloadTimeout = null;
151
- this.watcher = fsSync.watch(this.metadataPath, (eventType, filename) => {
152
- if (!filename?.endsWith(".json")) return;
153
- console.log(`[HooksRegistry] Detected change: ${filename} (${eventType})`);
154
- if (reloadTimeout) {
155
- clearTimeout(reloadTimeout);
156
- }
157
- reloadTimeout = setTimeout(() => {
158
- this.reload();
159
- }, 500);
160
- });
161
- } catch (error) {
162
- }
163
- }
164
- /**
165
- * 停止监听
166
- */
167
- stopWatching() {
168
- if (this.watcher) {
169
- this.watcher.close();
170
- this.watcher = null;
171
- }
172
- }
173
- getHook(name) {
174
- return this.hooks.get(name);
175
- }
176
- getAllHookNames() {
177
- return Array.from(this.hooks.keys());
178
- }
179
- isKnownHook(name) {
180
- return this.hooks.has(name);
181
- }
182
- /**
183
- * 获取 hook 的必填参数数量
184
- */
185
- getRequiredParamCount(name) {
186
- const hook = this.hooks.get(name);
187
- if (!hook) return 0;
188
- return hook.params.filter((p) => p.required).length;
189
- }
190
- /**
191
- * 获取 hook 的总参数数量
192
- */
193
- getTotalParamCount(name) {
194
- const hook = this.hooks.get(name);
195
- if (!hook) return 0;
196
- return hook.params.length;
197
- }
198
- };
199
- var hooksRegistry = new HooksRegistry();
200
-
201
- // src/analysis/astReviewer.ts
202
- var VUE_BUILTINS = /* @__PURE__ */ new Set([
203
- // Reactivity
204
- "ref",
205
- "reactive",
206
- "computed",
207
- "watch",
208
- "watchEffect",
209
- "watchPostEffect",
210
- "watchSyncEffect",
211
- "toRef",
212
- "toRefs",
213
- "toRaw",
214
- "markRaw",
215
- "isRef",
216
- "isReactive",
217
- "isReadonly",
218
- "isProxy",
219
- "shallowRef",
220
- "shallowReactive",
221
- "shallowReadonly",
222
- "triggerRef",
223
- "customRef",
224
- "readonly",
225
- "effectScope",
226
- "getCurrentScope",
227
- "onScopeDispose",
228
- // Lifecycle
229
- "onMounted",
230
- "onUnmounted",
231
- "onBeforeMount",
232
- "onBeforeUnmount",
233
- "onUpdated",
234
- "onBeforeUpdate",
235
- "onActivated",
236
- "onDeactivated",
237
- "onErrorCaptured",
238
- "onRenderTracked",
239
- "onRenderTriggered",
240
- "onServerPrefetch",
241
- // Composition API
242
- "defineProps",
243
- "defineEmits",
244
- "defineExpose",
245
- "defineOptions",
246
- "defineSlots",
247
- "defineModel",
248
- "withDefaults",
249
- "useSlots",
250
- "useAttrs",
251
- // Others
252
- "nextTick",
253
- "inject",
254
- "provide",
255
- "getCurrentInstance",
256
- "h",
257
- "createVNode",
258
- "resolveComponent",
259
- "resolveDirective",
260
- "withDirectives"
261
- ]);
262
- var JS_GLOBALS = /* @__PURE__ */ new Set([
263
- "console",
264
- "window",
265
- "document",
266
- "globalThis",
267
- "self",
268
- "Math",
269
- "JSON",
270
- "Date",
271
- "Array",
272
- "Object",
273
- "String",
274
- "Number",
275
- "Boolean",
276
- "Promise",
277
- "Error",
278
- "TypeError",
279
- "RangeError",
280
- "SyntaxError",
281
- "RegExp",
282
- "Map",
283
- "Set",
284
- "WeakMap",
285
- "WeakSet",
286
- "Symbol",
287
- "Proxy",
288
- "Reflect",
289
- "parseInt",
290
- "parseFloat",
291
- "isNaN",
292
- "isFinite",
293
- "encodeURI",
294
- "decodeURI",
295
- "encodeURIComponent",
296
- "decodeURIComponent",
297
- "atob",
298
- "btoa",
299
- "setTimeout",
300
- "setInterval",
301
- "clearTimeout",
302
- "clearInterval",
303
- "requestAnimationFrame",
304
- "cancelAnimationFrame",
305
- "fetch",
306
- "URL",
307
- "URLSearchParams",
308
- "FormData",
309
- "Blob",
310
- "File",
311
- "FileReader",
312
- "undefined",
313
- "null",
314
- "NaN",
315
- "Infinity",
316
- "true",
317
- "false"
318
- ]);
319
- var JS_KEYWORDS = /* @__PURE__ */ new Set([
320
- "if",
321
- "else",
322
- "for",
323
- "while",
324
- "do",
325
- "switch",
326
- "case",
327
- "break",
328
- "continue",
329
- "return",
330
- "try",
331
- "catch",
332
- "finally",
333
- "throw",
334
- "new",
335
- "delete",
336
- "typeof",
337
- "instanceof",
338
- "void",
339
- "in",
340
- "of",
341
- "this",
342
- "class",
343
- "extends",
344
- "super",
345
- "import",
346
- "export",
347
- "default",
348
- "const",
349
- "let",
350
- "var",
351
- "function",
352
- "async",
353
- "await",
354
- "yield",
355
- "true",
356
- "false",
357
- "null",
358
- "undefined",
359
- "NaN",
360
- "Infinity"
361
- ]);
362
- var TEMPLATE_BUILTINS = /* @__PURE__ */ new Set([
363
- "$event",
364
- "$refs",
365
- "$slots",
366
- "$attrs",
367
- "$emit",
368
- "$el",
369
- "$parent",
370
- "$root",
371
- "$data",
372
- "$options",
373
- "$route",
374
- "$router",
375
- "$t",
376
- "$i18n",
377
- "$n",
378
- "$d",
379
- "$te"
380
- // Vue Router & i18n
381
- ]);
382
- var NATIVE_EVENTS = /* @__PURE__ */ new Set([
383
- // Mouse
384
- "click",
385
- "dblclick",
386
- "mousedown",
387
- "mouseup",
388
- "mousemove",
389
- "mouseenter",
390
- "mouseleave",
391
- "mouseover",
392
- "mouseout",
393
- // Keyboard
394
- "keydown",
395
- "keyup",
396
- "keypress",
397
- // Focus
398
- "focus",
399
- "blur",
400
- "focusin",
401
- "focusout",
402
- // Form
403
- "input",
404
- "change",
405
- "submit",
406
- "reset",
407
- "invalid",
408
- // Drag
409
- "drag",
410
- "dragstart",
411
- "dragend",
412
- "dragenter",
413
- "dragleave",
414
- "dragover",
415
- "drop",
416
- // Clipboard
417
- "copy",
418
- "cut",
419
- "paste",
420
- // Touch
421
- "touchstart",
422
- "touchend",
423
- "touchmove",
424
- "touchcancel",
425
- // Scroll & Resize
426
- "scroll",
427
- "resize",
428
- "wheel",
429
- // Other
430
- "contextmenu",
431
- "pointerdown",
432
- "pointerup",
433
- "pointermove",
434
- "pointerenter",
435
- "pointerleave"
436
- ]);
437
- var NATIVE_HTML_ATTRIBUTES = /* @__PURE__ */ new Set([
438
- // Form attributes
439
- "maxlength",
440
- "minlength",
441
- "max",
442
- "min",
443
- "step",
444
- "pattern",
445
- "autocomplete",
446
- "autofocus",
447
- "required",
448
- "readonly",
449
- "disabled",
450
- "checked",
451
- "selected",
452
- "multiple",
453
- "accept",
454
- "name",
455
- "value",
456
- "placeholder",
457
- "form",
458
- "formaction",
459
- "formmethod",
460
- "formtarget",
461
- // Link/Navigation attributes
462
- "href",
463
- "target",
464
- "rel",
465
- "download",
466
- "hreflang",
467
- "ping",
468
- "referrerpolicy",
469
- // Media attributes
470
- "src",
471
- "alt",
472
- "width",
473
- "height",
474
- "loading",
475
- "decoding",
476
- "crossorigin",
477
- "controls",
478
- "autoplay",
479
- "loop",
480
- "muted",
481
- "poster",
482
- "preload",
483
- // Table attributes
484
- "colspan",
485
- "rowspan",
486
- "headers",
487
- "scope",
488
- // Global attributes
489
- "id",
490
- "title",
491
- "lang",
492
- "dir",
493
- "hidden",
494
- "tabindex",
495
- "accesskey",
496
- "draggable",
497
- "contenteditable",
498
- "spellcheck",
499
- "translate",
500
- "role",
501
- // ARIA attributes (common ones)
502
- "aria-label",
503
- "aria-labelledby",
504
- "aria-describedby",
505
- "aria-hidden",
506
- "aria-expanded",
507
- "aria-selected",
508
- "aria-checked",
509
- "aria-disabled",
510
- "aria-controls",
511
- "aria-haspopup"
512
- ]);
513
- var STRICT_CHECK_ATTRIBUTES = /* @__PURE__ */ new Set([
514
- "required",
515
- "disabled",
516
- "readonly",
517
- "value",
518
- "name",
519
- "checked",
520
- "selected"
521
- ]);
522
- var VERSATILE_COMPONENTS = /* @__PURE__ */ new Set([
523
- "BillTypes",
524
- "Region",
525
- "AZ",
526
- "LayoutContent",
527
- "CardContent",
528
- "ProTable",
529
- "PaginationPlus",
530
- "Status",
531
- "TableColumnId",
532
- "IconTooltip",
533
- "ButtonLink"
534
- ]);
535
- var DUAL_EXPORT_COMPONENTS = /* @__PURE__ */ new Set([
536
- "TableColumn"
537
- // 可与 ProTable 配合从 versatile 导入,也可与 Table 配合从 king-design 导入
538
- ]);
539
- var NESTING_RULES = {
540
- "DropdownItem": ["DropdownMenu"],
541
- "DropdownMenu": ["Dropdown"],
542
- "TableColumn": ["Table", "ProTable"],
543
- "FormItem": ["Form"],
544
- "Tab": ["Tabs"],
545
- "Step": ["Steps"],
546
- "MenuItem": ["Menu", "SubMenu"],
547
- "SubMenu": ["Menu"],
548
- "BreadcrumbItem": ["Breadcrumb"],
549
- "CollapseItem": ["Collapse"],
550
- "CarouselItem": ["Carousel"],
551
- "DescriptionItem": ["Descriptions"]
552
- };
553
- async function analyzeCodeWithAST(code) {
554
- await componentRegistry.load();
555
- await hooksRegistry.load();
556
- const violations = [];
557
- const { descriptor, errors } = parse(code);
558
- if (errors.length > 0) {
559
- return violations;
560
- }
561
- const scriptContent = descriptor.scriptSetup?.content || descriptor.script?.content || "";
562
- if (scriptContent) {
563
- const sourceFile = ts.createSourceFile(
564
- "temp.ts",
565
- scriptContent,
566
- ts.ScriptTarget.Latest,
567
- true
568
- );
569
- ts.forEachChild(sourceFile, (node) => {
570
- if (ts.isImportDeclaration(node)) {
571
- checkImport(node, violations, sourceFile);
572
- }
573
- });
574
- }
575
- if (descriptor.template?.ast) {
576
- checkTemplate(descriptor.template.ast, violations, []);
577
- }
578
- if (scriptContent && descriptor.template) {
579
- const scriptBindings = extractScriptBindings(scriptContent);
580
- checkTemplateVariables(descriptor.template, scriptBindings, violations);
581
- }
582
- if (scriptContent) {
583
- const scriptBindings = extractScriptBindings(scriptContent);
584
- checkScriptFunctionCalls(scriptContent, scriptBindings, violations);
585
- }
586
- return violations;
587
- }
588
- function extractScriptBindings(scriptContent) {
589
- const bindings = /* @__PURE__ */ new Set();
590
- VUE_BUILTINS.forEach((b) => bindings.add(b));
591
- JS_GLOBALS.forEach((b) => bindings.add(b));
592
- try {
593
- let extractNames2 = function(name) {
594
- if (ts.isIdentifier(name)) {
595
- bindings.add(name.text);
596
- } else if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
597
- name.elements.forEach((element) => {
598
- if (ts.isBindingElement(element)) {
599
- extractNames2(element.name);
600
- }
601
- });
602
- }
603
- }, visit2 = function(node) {
604
- if (ts.isVariableStatement(node)) {
605
- node.declarationList.declarations.forEach((decl) => extractNames2(decl.name));
606
- } else if (ts.isFunctionDeclaration(node) && node.name) {
607
- bindings.add(node.name.text);
608
- } else if (ts.isClassDeclaration(node) && node.name) {
609
- bindings.add(node.name.text);
610
- } else if (ts.isImportDeclaration(node)) {
611
- const namedBindings = node.importClause?.namedBindings;
612
- if (namedBindings && ts.isNamedImports(namedBindings)) {
613
- namedBindings.elements.forEach((element) => {
614
- bindings.add(element.name.text);
615
- });
616
- }
617
- if (node.importClause?.name) {
618
- bindings.add(node.importClause.name.text);
619
- }
620
- }
621
- };
622
- var extractNames = extractNames2, visit = visit2;
623
- const sourceFile = ts.createSourceFile(
624
- "temp.ts",
625
- scriptContent,
626
- ts.ScriptTarget.Latest,
627
- true
628
- );
629
- sourceFile.statements.forEach(visit2);
630
- } catch (err) {
631
- return bindings;
632
- }
633
- return bindings;
634
- }
635
- function checkScriptFunctionCalls(scriptContent, bindings, violations) {
636
- try {
637
- let visit2 = function(node) {
638
- if (ts.isCallExpression(node)) {
639
- const callee = node.expression;
640
- if (ts.isIdentifier(callee)) {
641
- const funcName = callee.text;
642
- const isHookCall = funcName.startsWith("use") && funcName.length > 3 && funcName[3] === funcName[3].toUpperCase();
643
- if (isHookCall) {
644
- if (!bindings.has(funcName)) {
645
- violations.push({
646
- rule: "\u4F7F\u7528\u4E86\u672A\u5BFC\u5165\u7684 Hook",
647
- match: `${funcName}()`,
648
- suggestion: `\u8BF7\u5148\u5BFC\u5165 ${funcName}: import { ${funcName} } from '@ksyun-internal/versatile'`
649
- });
650
- }
651
- }
652
- }
653
- }
654
- ts.forEachChild(node, visit2);
655
- };
656
- var visit = visit2;
657
- const sourceFile = ts.createSourceFile(
658
- "temp.ts",
659
- scriptContent,
660
- ts.ScriptTarget.Latest,
661
- true
662
- );
663
- ts.forEachChild(sourceFile, visit2);
664
- } catch (err) {
665
- }
666
- }
667
- function checkTemplateVariables(template, bindings, violations) {
668
- const templateContent = template.content;
669
- const templateAst = template.ast;
670
- const localVariables = /* @__PURE__ */ new Set();
671
- if (templateAst) {
672
- let walk2 = function(node) {
673
- if (node.type === 0 || node.type === 1) {
674
- const vFor = node.props?.find((p) => p.type === 7 && p.name === "for");
675
- if (vFor && vFor.exp) {
676
- if (vFor.parseResult) {
677
- const { value, key, index } = vFor.parseResult;
678
- if (value) extractIdentifiersFromTemplateAST(value, localVariables);
679
- if (key) extractIdentifiersFromTemplateAST(key, localVariables);
680
- if (index) extractIdentifiersFromTemplateAST(index, localVariables);
681
- } else if (vFor.exp.content) {
682
- extractIdentifiersFromTemplateAST(vFor.exp, localVariables);
683
- }
684
- }
685
- const vSlot = node.props?.find((p) => p.type === 7 && p.name === "slot");
686
- if (vSlot && vSlot.exp) {
687
- extractIdentifiersFromTemplateAST(vSlot.exp, localVariables);
688
- }
689
- if (node.children) {
690
- node.children.forEach(walk2);
691
- }
692
- }
693
- };
694
- walk2(templateAst);
695
- }
696
- const allBindings = /* @__PURE__ */ new Set([...bindings, ...localVariables]);
697
- const interpolationRegex = /\{\{\s*([^}]+)\s*\}\}/g;
698
- let match;
699
- while ((match = interpolationRegex.exec(templateContent)) !== null) {
700
- const expression = match[1].trim();
701
- const { identifiers, locals } = extractIdentifiersFromExpression(expression);
702
- const combinedBindings = /* @__PURE__ */ new Set([...allBindings, ...locals]);
703
- for (const id of identifiers) {
704
- if (!combinedBindings.has(id) && !isTemplateBuiltin(id)) {
705
- violations.push({
706
- rule: `\u6A21\u677F\u5F15\u7528\u4E86\u672A\u5B9A\u4E49\u7684\u53D8\u91CF: ${id}`,
707
- match: `{{ ${expression} }}`,
708
- suggestion: `\u8BF7\u786E\u4FDD\u5728 <script setup> \u4E2D\u5B9A\u4E49\u53D8\u91CF ${id}\uFF0C\u6216\u68C0\u67E5\u62FC\u5199\u662F\u5426\u6B63\u786E`
709
- });
710
- }
711
- }
712
- }
713
- const bindRegex = /(?::|v-bind:)[\w.-]+="([^"]+)"/g;
714
- while ((match = bindRegex.exec(templateContent)) !== null) {
715
- const expression = match[1].trim();
716
- const { identifiers, locals } = extractIdentifiersFromExpression(expression);
717
- const combinedBindings = /* @__PURE__ */ new Set([...allBindings, ...locals]);
718
- for (const id of identifiers) {
719
- if (!combinedBindings.has(id) && !isTemplateBuiltin(id)) {
720
- violations.push({
721
- rule: `\u7ED1\u5B9A\u5C5E\u6027\u5F15\u7528\u4E86\u672A\u5B9A\u4E49\u7684\u53D8\u91CF: ${id}`,
722
- match: match[0],
723
- suggestion: `\u8BF7\u786E\u4FDD\u5728 <script setup> \u4E2D\u5B9A\u4E49\u53D8\u91CF ${id}`
724
- });
725
- }
726
- }
727
- }
728
- const eventRegex = /(?:@|v-on:)[\w.-]+="([^"]+)"/g;
729
- while ((match = eventRegex.exec(templateContent)) !== null) {
730
- const expression = match[1].trim();
731
- const { identifiers, locals } = extractIdentifiersFromExpression(expression);
732
- const combinedBindings = /* @__PURE__ */ new Set([...allBindings, ...locals]);
733
- for (const id of identifiers) {
734
- if (!combinedBindings.has(id) && !isTemplateBuiltin(id)) {
735
- violations.push({
736
- rule: `\u4E8B\u4EF6\u5904\u7406\u5668\u5F15\u7528\u4E86\u672A\u5B9A\u4E49\u7684\u51FD\u6570/\u53D8\u91CF: ${id}`,
737
- match: match[0],
738
- suggestion: `\u8BF7\u786E\u4FDD\u5728 <script setup> \u4E2D\u5B9A\u4E49 ${id}`
739
- });
740
- }
741
- }
742
- }
743
- }
744
- function extractIdentifiersFromTemplateAST(node, set) {
745
- if (!node || typeof node !== "object") return;
746
- const n = node;
747
- if (n.type === 4 && n.content) {
748
- const matches = n.content.match(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g);
749
- if (matches) {
750
- matches.forEach((m) => {
751
- if (!isJsKeyword(m)) set.add(m);
752
- });
753
- }
754
- } else if (n.type === 8 && n.children) {
755
- n.children.forEach((c) => extractIdentifiersFromTemplateAST(c, set));
756
- }
757
- }
758
- function extractIdentifiersFromExpression(expression) {
759
- const identifiers = [];
760
- const locals = [];
761
- const arrowFuncRegex = /(?:(?:\(([^)]+)\))|([a-zA-Z_$][\w$]*))\s*=>/g;
762
- let arrowMatch;
763
- while ((arrowMatch = arrowFuncRegex.exec(expression)) !== null) {
764
- const params = arrowMatch[1] || arrowMatch[2];
765
- if (params) {
766
- const paramNames = params.split(",").map((p) => p.trim().split(":")[0].trim());
767
- paramNames.forEach((p) => {
768
- const subMatches = p.match(/\b([a-zA-Z_$][\w$]*)\b/g);
769
- if (subMatches) subMatches.forEach((sm) => {
770
- if (!isJsKeyword(sm)) locals.push(sm);
771
- });
772
- });
773
- }
774
- }
775
- const cleanExpr = expression.replace(/'[^']*'/g, "").replace(/"[^"]*"/g, "").replace(/`[^`]*`/g, "");
776
- const idRegex = /(?:^|[^.\w$])([a-zA-Z_$][\w$]*)\b(?!\s*:)/g;
777
- let match;
778
- while ((match = idRegex.exec(cleanExpr)) !== null) {
779
- const id = match[1];
780
- if (!isJsKeyword(id) && !locals.includes(id)) {
781
- identifiers.push(id);
782
- }
783
- }
784
- return { identifiers: [...new Set(identifiers)], locals: [...new Set(locals)] };
785
- }
786
- function isTemplateBuiltin(id) {
787
- return TEMPLATE_BUILTINS.has(id);
788
- }
789
- function isJsKeyword(id) {
790
- return JS_KEYWORDS.has(id);
791
- }
792
- function checkImport(node, violations, sourceFile) {
793
- const moduleSpecifier = node.moduleSpecifier.getText(sourceFile).replace(/['\"]/g, "");
794
- if (moduleSpecifier === "@king-design/vue" || moduleSpecifier === "@ksyun-internal/versatile") {
795
- const namedBindings = node.importClause?.namedBindings;
796
- if (namedBindings && ts.isNamedImports(namedBindings)) {
797
- namedBindings.elements.forEach((element) => {
798
- const originalName = element.propertyName ? element.propertyName.text : element.name.text;
799
- const localName = element.name.text;
800
- const isAlias = !!element.propertyName;
801
- const isHook = originalName.startsWith("use") && originalName.length > 3 && originalName[3] === originalName[3].toUpperCase();
802
- if (isHook) {
803
- if (moduleSpecifier !== "@ksyun-internal/versatile") {
804
- violations.push({
805
- rule: `Hook ${originalName} \u5BFC\u5165\u6E90\u9519\u8BEF`,
806
- match: `import { ${originalName} } from '${moduleSpecifier}'`,
807
- suggestion: `\u5E94\u4ECE '@ksyun-internal/versatile' \u5BFC\u5165`
808
- });
809
- return;
810
- }
811
- if (!hooksRegistry.isKnownHook(originalName)) {
812
- violations.push({
813
- rule: "\u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684 Hook",
814
- match: originalName,
815
- suggestion: `Hook ${originalName} \u4E0D\u5B58\u5728\u4E8E @ksyun-internal/versatile\u3002\u53EF\u7528\u7684 Hooks: ${hooksRegistry.getAllHookNames().join(", ") || "\u6682\u65E0"}`
816
- });
817
- return;
818
- }
819
- if (isAlias) {
820
- violations.push({
821
- rule: `\u7981\u6B62\u5BF9 Hook ${originalName} \u4F7F\u7528\u522B\u540D\u5BFC\u5165`,
822
- match: `${originalName} as ${localName}`,
823
- suggestion: `\u76F4\u63A5\u4F7F\u7528\u539F\u540D: import { ${originalName} } from '${moduleSpecifier}'`
824
- });
825
- }
826
- return;
827
- }
828
- if (isAlias) {
829
- violations.push({
830
- rule: `\u7981\u6B62\u5BF9\u7EC4\u4EF6 ${originalName} \u4F7F\u7528\u522B\u540D\u5BFC\u5165`,
831
- match: `${originalName} as ${localName}`,
832
- suggestion: `\u76F4\u63A5\u4F7F\u7528\u539F\u540D: import { ${originalName} } from '${moduleSpecifier}'`
833
- });
834
- }
835
- if (componentRegistry.isKnownComponent(originalName)) {
836
- if (DUAL_EXPORT_COMPONENTS.has(originalName)) {
837
- return;
838
- }
839
- const isVersatile = VERSATILE_COMPONENTS.has(originalName);
840
- const expectedPackage = isVersatile ? "@ksyun-internal/versatile" : "@king-design/vue";
841
- if (expectedPackage !== moduleSpecifier) {
842
- violations.push({
843
- rule: `\u7EC4\u4EF6 ${originalName} \u5BFC\u5165\u6E90\u9519\u8BEF`,
844
- match: `import { ... } from '${moduleSpecifier}'`,
845
- suggestion: `\u5E94\u4ECE '${expectedPackage}' \u5BFC\u5165`
846
- });
847
- }
848
- } else {
849
- violations.push({
850
- rule: "\u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684\u7EC4\u4EF6/\u5BFC\u51FA",
851
- match: originalName,
852
- suggestion: `\u8BF7\u786E\u8BA4 ${originalName} \u662F\u5426\u5B58\u5728\u4E8E ${moduleSpecifier}\u3002\u5EFA\u8BAE\u67E5\u770B\u6587\u6863\u3002`
853
- });
854
- }
855
- });
856
- }
857
- }
858
- }
859
- function checkNestingRules(tagName, metadata, ancestors, violations) {
860
- const requiredParents = metadata?.requiredParent ? [metadata.requiredParent] : NESTING_RULES[tagName];
861
- if (!requiredParents || requiredParents.length === 0) return;
862
- const hasValidAncestor = ancestors.some(
863
- (ancestor) => requiredParents.includes(ancestor) || ancestor === "template"
864
- );
865
- if (!hasValidAncestor && ancestors.length > 0) {
866
- violations.push({
867
- rule: `${tagName} \u5FC5\u987B\u653E\u5728 ${requiredParents.join(" \u6216 ")} \u7EC4\u4EF6\u5185`,
868
- match: tagName,
869
- suggestion: `\u8BF7\u5C06 ${tagName} \u79FB\u52A8\u5230 ${requiredParents[0]} \u7EC4\u4EF6\u4E2D`
870
- });
871
- }
872
- }
873
- function checkComponentSpecificRules(tagName, node, violations) {
874
- if (tagName === "Dropdown") {
875
- node.children?.forEach((child) => {
876
- if (child.type === 1 && child.tag === "template") {
877
- const slotProp = child.props?.find(
878
- (p) => p.type === 7 && p.name === "slot" && p.arg?.content === "menu"
879
- );
880
- if (slotProp) {
881
- violations.push({
882
- rule: "DropdownMenu \u4E0D\u80FD\u653E\u5728 #menu \u63D2\u69FD\u4E2D",
883
- match: "<template #menu>",
884
- suggestion: "\u8BF7\u79FB\u9664 <template #menu>\uFF0C\u5C06 DropdownMenu \u76F4\u63A5\u4F5C\u4E3A Dropdown \u7684\u7B2C\u4E8C\u4E2A\u5B50\u5143\u7D20"
885
- });
886
- }
887
- }
888
- });
889
- }
890
- if (tagName === "Table") {
891
- node.props.forEach((prop) => {
892
- if (prop.type === 6 && (prop.name === "rowKey" || prop.name === "row-key")) {
893
- violations.push({
894
- rule: "Table\u7EC4\u4EF6 rowKey \u5C5E\u6027 usage \u9519\u8BEF",
895
- match: `rowKey="${prop.value?.content || ""}"`,
896
- suggestion: 'rowKey \u5FC5\u987B\u662F\u4E00\u4E2A\u8FD4\u56DE\u552F\u4E00\u503C\u7684\u51FD\u6570\u3002\u8BF7\u4F7F\u7528\u7ED1\u5B9A\u8BED\u6CD5\uFF0C\u4F8B\u5982 :rowKey="(row) => row.id"'
897
- });
898
- }
899
- });
900
- }
901
- if (tagName === "Icon") {
902
- node.props.forEach((prop) => {
903
- if (prop.type === 6 && prop.name === "name") {
904
- violations.push({
905
- rule: "Icon \u7EC4\u4EF6\u7981\u6B62\u4F7F\u7528 name \u5C5E\u6027",
906
- match: `name="${prop.value?.content || ""}"`,
907
- suggestion: '\u8BF7\u4F7F\u7528 class \u5C5E\u6027\u6307\u5B9A\u56FE\u6807\uFF0C\u4F8B\u5982: class="k-icon-search"'
908
- });
909
- }
910
- });
911
- }
912
- if (tagName === "TableColumn") {
913
- node.children?.forEach((child) => {
914
- if (child.type === 1 && child.tag === "template") {
915
- const slotProp = child.props?.find((p) => p.type === 7 && p.name === "slot");
916
- if (slotProp && slotProp.exp && slotProp.exp.content) {
917
- const exp = slotProp.exp.content.trim();
918
- if (exp.startsWith("{") && exp.endsWith("}")) {
919
- violations.push({
920
- rule: "TableColumn \u63D2\u69FD\u53C2\u6570\u5E94\u4E3A\u6570\u7EC4\u7ED3\u6784",
921
- match: exp,
922
- suggestion: `King Design \u8868\u683C\u63D2\u69FD\u53C2\u6570\u4E3A\u6570\u7EC4\u987A\u5E8F\u4F20\u9012\uFF0C\u8BF7\u5C06 ${exp} \u6539\u4E3A [row]`
923
- });
924
- }
925
- }
926
- }
927
- });
928
- }
929
- }
930
- function checkProps(tagName, node, metadata, violations) {
931
- let customTagName = null;
932
- const tagNameProp = node.props.find(
933
- (p) => p.type === 6 && p.name === "tagName" || p.type === 7 && p.name === "bind" && p.arg?.content === "tagName"
934
- );
935
- if (tagNameProp?.type === 6 && tagNameProp.value?.content) {
936
- customTagName = tagNameProp.value.content;
937
- }
938
- const dynamicAllowedProps = [];
939
- if (customTagName === "a") {
940
- dynamicAllowedProps.push("href", "target", "rel", "download");
941
- }
942
- node.props.forEach((prop) => {
943
- if (prop.type !== 6) return;
944
- const propName = prop.name;
945
- const camelCaseProp = propName.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
946
- const isPropValid = metadata.props?.some(
947
- (p) => p.name === propName || p.name === camelCaseProp || p.name.toLowerCase() === propName.toLowerCase()
948
- );
949
- const isDynamicallyAllowed = dynamicAllowedProps.includes(propName);
950
- const isBuiltinProp = ["class", "style", "key", "ref"].includes(propName) || propName.startsWith("data-");
951
- const isNativeHtmlAttr = NATIVE_HTML_ATTRIBUTES.has(propName) || propName.startsWith("aria-");
952
- const isStrictAttr = STRICT_CHECK_ATTRIBUTES.has(propName);
953
- const isNativeIgnored = isNativeHtmlAttr && isStrictAttr && metadata.props;
954
- if (!isPropValid && !isDynamicallyAllowed && !isBuiltinProp && (!isNativeHtmlAttr || isNativeIgnored)) {
955
- if (metadata.props && metadata.props.length > 0) {
956
- violations.push({
957
- rule: `\u5C5E\u6027 ${propName} \u4E0D\u5B58\u5728\u4E8E ${tagName}${isNativeIgnored ? " (\u8BE5\u7EC4\u4EF6\u4E0D\u652F\u6301\u6B64\u539F\u751F\u5C5E\u6027)" : ""}`,
958
- match: propName,
959
- suggestion: `\u53EF\u7528\u5C5E\u6027: ${metadata.props.map((p) => p.name).join(", ")}`
960
- });
961
- }
962
- }
963
- if (isPropValid && metadata.props) {
964
- const targetProp = metadata.props.find(
965
- (p) => p.name === propName || p.name === camelCaseProp || p.name.toLowerCase() === propName.toLowerCase()
966
- );
967
- if (targetProp) {
968
- const attrValue = prop.value?.content;
969
- if (attrValue) {
970
- let cleanAllowedValues = [];
971
- if (targetProp.allowedValues && targetProp.allowedValues.length > 0) {
972
- cleanAllowedValues = targetProp.allowedValues.map((av) => String(av.value).replace(/['"]/g, ""));
973
- } else if (targetProp.type?.kind === "union" && targetProp.type.unionTypes) {
974
- cleanAllowedValues = targetProp.type.unionTypes.map((v) => String(v).replace(/['"]/g, ""));
975
- }
976
- if (cleanAllowedValues.length > 0 && !cleanAllowedValues.includes(attrValue)) {
977
- violations.push({
978
- rule: `${tagName} \u7684\u5C5E\u6027 ${propName} \u7684\u503C "${attrValue}" \u65E0\u6548`,
979
- match: `${propName}="${attrValue}"`,
980
- suggestion: `\u5141\u8BB8\u7684\u503C: ${cleanAllowedValues.join(" | ")}`
981
- });
982
- }
983
- }
984
- }
985
- }
986
- });
987
- }
988
- function checkEvents(tagName, node, metadata, violations) {
989
- node.props.forEach((prop) => {
990
- if (prop.type !== 7) return;
991
- if (prop.name !== "on") return;
992
- const arg = prop.arg;
993
- const eventName = arg && "content" in arg ? arg.content : null;
994
- if (!eventName) return;
995
- if (eventName.startsWith("$change:") || eventName.startsWith("update:")) {
996
- return;
997
- }
998
- const camelCaseEvent = eventName.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
999
- const isEventValid = metadata.events?.some(
1000
- (e) => e.name === eventName || e.name === camelCaseEvent || e.name.toLowerCase() === eventName.toLowerCase()
1001
- );
1002
- const isNativeEvent = NATIVE_EVENTS.has(eventName);
1003
- if (!isEventValid && !isNativeEvent && metadata.events && metadata.events.length > 0) {
1004
- violations.push({
1005
- rule: `\u4E8B\u4EF6 @${eventName} \u4E0D\u5B58\u5728\u4E8E ${tagName}`,
1006
- match: `@${eventName}`,
1007
- suggestion: `\u53EF\u7528\u4E8B\u4EF6: ${metadata.events.map((e) => e.name).join(", ") || "\u65E0\u81EA\u5B9A\u4E49\u4E8B\u4EF6"}`
1008
- });
1009
- }
1010
- });
1011
- }
1012
- function checkSlots(tagName, node, metadata, violations) {
1013
- if (!node.children || !metadata.slots?.length) return;
1014
- node.children.forEach((child) => {
1015
- if (child.type !== 1 || child.tag !== "template") return;
1016
- child.props.forEach((prop) => {
1017
- if (prop.type !== 7 || prop.name !== "slot") return;
1018
- const slotName = prop.arg?.content || "default";
1019
- const isSlotValid = metadata.slots?.some((s) => s.name === slotName);
1020
- if (!isSlotValid) {
1021
- violations.push({
1022
- rule: `\u63D2\u69FD #${slotName} \u4E0D\u5B58\u5728\u4E8E ${tagName}`,
1023
- match: `#${slotName}`,
1024
- suggestion: `\u53EF\u7528\u63D2\u69FD: ${metadata.slots.map((s) => s.name).join(", ")}`
1025
- });
1026
- }
1027
- });
1028
- });
1029
- }
1030
- function checkTemplate(node, violations, ancestors) {
1031
- if (node.type === 1) {
1032
- const elementNode = node;
1033
- const tagName = elementNode.tag;
1034
- const metadata = componentRegistry.isKnownComponent(tagName) ? componentRegistry.getComponent(tagName) : null;
1035
- if (metadata) {
1036
- checkNestingRules(tagName, metadata, ancestors, violations);
1037
- }
1038
- checkComponentSpecificRules(tagName, elementNode, violations);
1039
- if (metadata) {
1040
- checkProps(tagName, elementNode, metadata, violations);
1041
- checkEvents(tagName, elementNode, metadata, violations);
1042
- checkSlots(tagName, elementNode, metadata, violations);
1043
- }
1044
- }
1045
- if ("children" in node && Array.isArray(node.children)) {
1046
- const newAncestors = node.type === 1 && "tag" in node ? [...ancestors, node.tag] : ancestors;
1047
- node.children.forEach((child) => {
1048
- if (typeof child === "object" && child !== null && "type" in child) {
1049
- checkTemplate(child, violations, newAncestors);
1050
- }
1051
- });
1052
- }
1053
- }
1054
-
1055
- export { analyzeCodeWithAST, componentRegistry };