@oj-module/cli 0.2.64 → 0.2.66

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 (193) hide show
  1. package/devkit/README.md +9 -3
  2. package/devkit/SKILL.md +5 -2
  3. package/devkit/framework-dev.md +12 -7
  4. package/devkit/manual.md +123 -56
  5. package/devkit/menu-rules.md +4 -1
  6. package/package.json +3 -2
  7. package/shell-dist/assets/ahooks.2l850ymmh0.js +18 -0
  8. package/shell-dist/assets/antd-es-locale-zh_CN.1gjjsvxbf0.js +1 -0
  9. package/shell-dist/assets/antd-img-crop.196rkty480.js +99 -0
  10. package/shell-dist/assets/antd-locale-en_US.561tozx860.js +1 -0
  11. package/shell-dist/assets/antd-locale-zh_CN.1cmpmgzh70.js +1 -0
  12. package/shell-dist/assets/antd.uw1coq15l0.js +348 -0
  13. package/shell-dist/assets/clsx.jotilrt2e0.js +1 -0
  14. package/shell-dist/assets/contract-errors.2o17vz9u50.js +1 -0
  15. package/shell-dist/assets/contract.3h327o3mj0.js +57 -0
  16. package/shell-dist/assets/cssinjs.atgmhan7m0.js +3 -0
  17. package/shell-dist/assets/dayjs-plugin-advancedFormat.3vsyd2eis0.js +1 -0
  18. package/shell-dist/assets/dayjs-plugin-customParseFormat.13xhghv1d0.js +1 -0
  19. package/shell-dist/assets/dayjs-plugin-isoWeek.1fn1tw82p0.js +1 -0
  20. package/shell-dist/assets/dayjs-plugin-localeData.2rwbjp9nk0.js +1 -0
  21. package/shell-dist/assets/dayjs-plugin-quarterOfYear.2oqo1lcx80.js +1 -0
  22. package/shell-dist/assets/dayjs-plugin-relativeTime.2pqs3et4y0.js +1 -0
  23. package/shell-dist/assets/dayjs-plugin-weekOfYear.gvn9iph080.js +1 -0
  24. package/shell-dist/assets/dayjs-plugin-weekYear.2u78l3ex10.js +1 -0
  25. package/shell-dist/assets/dayjs-plugin-weekday.2d56a4tml0.js +1 -0
  26. package/shell-dist/assets/dayjs.pa60l6wjo0.js +1 -0
  27. package/shell-dist/assets/dnd-kit-core.1xyreig6r0.js +5 -0
  28. package/shell-dist/assets/dnd-kit-sortable.s94qciuft0.js +1 -0
  29. package/shell-dist/assets/dnd-kit-utilities.1umw4xphb0.js +1 -0
  30. package/shell-dist/assets/i18next.33zqofapj0.js +1 -0
  31. package/shell-dist/assets/icons-es-components-Context.365qmflwg0.js +1 -0
  32. package/shell-dist/assets/icons.159wjwals0.js +57 -0
  33. package/shell-dist/assets/jsx-dev-runtime.3uibuc82f0.js +14 -0
  34. package/shell-dist/assets/jsx-runtime.h40xu4o5b0.js +14 -0
  35. package/shell-dist/assets/keepalive-for-react.xenu5vam70.js +1 -0
  36. package/shell-dist/assets/kit.37kr8ui390.js +1 -0
  37. package/shell-dist/assets/ky.1cgch7gu60.js +6 -0
  38. package/shell-dist/assets/motion-react.pjsxytvjs0.js +12 -0
  39. package/shell-dist/assets/motion.21tciqutg0.js +4 -0
  40. package/shell-dist/assets/nprogress.2nz76n9ny0.js +7 -0
  41. package/shell-dist/assets/ojm-dayjs-locale-zh-cn.3vr3beiyp0.js +9 -0
  42. package/shell-dist/assets/pinyin-pro.3v0mnxczn0.js +1 -0
  43. package/shell-dist/assets/pro-components.2kyot1vcl0.js +83 -0
  44. package/shell-dist/assets/react-countup.2x8idnr8y0.js +10 -0
  45. package/shell-dist/assets/react-dom-client.ick0v05ob0.js +43 -0
  46. package/shell-dist/assets/react-dom.2so9uxg3f0.js +22 -0
  47. package/shell-dist/assets/react-error-boundary.2dncdfj3b0.js +1 -0
  48. package/shell-dist/assets/react-i18next.auhvjhqv00.js +1 -0
  49. package/shell-dist/assets/react-jss.1ei6dr9e00.js +29 -0
  50. package/shell-dist/assets/react-query.3rln45ow00.js +1 -0
  51. package/shell-dist/assets/react-router-dom.3c6zld8h40.js +44 -0
  52. package/shell-dist/assets/react-router.2h9rebcv50.js +56 -0
  53. package/shell-dist/assets/react.1ew63mwts0.js +14 -0
  54. package/shell-dist/assets/runtime.1xfsxcajj0.js +21702 -0
  55. package/shell-dist/assets/simplebar-react.2g739uvai0.js +1 -0
  56. package/shell-dist/assets/spin-delay.yjeyj6iwp0.js +9 -0
  57. package/shell-dist/assets/tailwind-merge.125ehiqjw0.js +1 -0
  58. package/shell-dist/assets/use-sync-external-store-shim.1nz1pi9bb0.js +22 -0
  59. package/shell-dist/assets/zustand-middleware.1xaxjueme0.js +6 -0
  60. package/shell-dist/assets/zustand-react-shallow.pybayv1hx0.js +1 -0
  61. package/shell-dist/assets/zustand-react.9fsl9brxy0.js +1 -0
  62. package/shell-dist/assets/zustand-shallow.6cz0yyv9x0.js +1 -0
  63. package/shell-dist/assets/zustand-vanilla-shallow.3424monv50.js +1 -0
  64. package/shell-dist/assets/zustand-vanilla.1mo3ka3g30.js +1 -0
  65. package/shell-dist/assets/zustand.3gjgzlnqo0.js +1 -0
  66. package/shell-dist/index.html +67 -118
  67. package/shell-dist/versions.json +3 -3
  68. package/src/esm-exports.ts +152 -70
  69. package/src/shared-deps.ts +69 -19
  70. package/src/site-base.ts +4 -0
  71. package/templates/src/app/web/home/pages/components/bar-chart.tsx +5 -3
  72. package/templates/src/app/web/home/pages/components/echarts-setup.ts +30 -0
  73. package/templates/src/app/web/home/pages/components/line-chart.tsx +5 -3
  74. package/templates/src/app/web/home/pages/components/pie-chart.tsx +6 -3
  75. package/templates/src/app/web/home/pages/index.tsx +3 -2
  76. package/templates/src/app/web/home/routes.ts +3 -2
  77. package/vendor/host-versions.json +3 -3
  78. package/shell-dist/assets/ahooks.js +0 -6909
  79. package/shell-dist/assets/antd-es-locale-zh_CN.js +0 -257
  80. package/shell-dist/assets/antd-img-crop.js +0 -1495
  81. package/shell-dist/assets/antd-locale-en_US.js +0 -374
  82. package/shell-dist/assets/antd-locale-zh_CN.js +0 -379
  83. package/shell-dist/assets/antd.js +0 -106874
  84. package/shell-dist/assets/clsx.js +0 -33
  85. package/shell-dist/assets/contract-errors.js +0 -30
  86. package/shell-dist/assets/contract.js +0 -18900
  87. package/shell-dist/assets/cssinjs.js +0 -2595
  88. package/shell-dist/assets/dayjs-plugin-advancedFormat.js +0 -98
  89. package/shell-dist/assets/dayjs-plugin-customParseFormat.js +0 -173
  90. package/shell-dist/assets/dayjs-plugin-isoWeek.js +0 -77
  91. package/shell-dist/assets/dayjs-plugin-localeData.js +0 -126
  92. package/shell-dist/assets/dayjs-plugin-quarterOfYear.js +0 -77
  93. package/shell-dist/assets/dayjs-plugin-relativeTime.js +0 -92
  94. package/shell-dist/assets/dayjs-plugin-weekOfYear.js +0 -73
  95. package/shell-dist/assets/dayjs-plugin-weekYear.js +0 -63
  96. package/shell-dist/assets/dayjs-plugin-weekday.js +0 -63
  97. package/shell-dist/assets/dayjs.js +0 -326
  98. package/shell-dist/assets/dnd-kit-core.js +0 -3601
  99. package/shell-dist/assets/dnd-kit-sortable.js +0 -717
  100. package/shell-dist/assets/dnd-kit-utilities.js +0 -340
  101. package/shell-dist/assets/i18next.js +0 -2321
  102. package/shell-dist/assets/icons-es-components-Context.js +0 -20
  103. package/shell-dist/assets/icons.js +0 -34961
  104. package/shell-dist/assets/jsx-dev-runtime.js +0 -75
  105. package/shell-dist/assets/jsx-runtime.js +0 -97
  106. package/shell-dist/assets/keepalive-for-react.js +0 -283
  107. package/shell-dist/assets/kit.js +0 -151
  108. package/shell-dist/assets/ky.js +0 -1427
  109. package/shell-dist/assets/motion-react.js +0 -13589
  110. package/shell-dist/assets/motion.js +0 -9959
  111. package/shell-dist/assets/nprogress.js +0 -314
  112. package/shell-dist/assets/ojm--ant-design-icons-es-icons-BarsOutlined.js +0 -7
  113. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CalendarOutlined.js +0 -7
  114. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretDownFilled.js +0 -7
  115. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretDownOutlined.js +0 -7
  116. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretUpOutlined.js +0 -7
  117. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CheckCircleFilled.js +0 -7
  118. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CheckOutlined.js +0 -7
  119. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ClockCircleOutlined.js +0 -7
  120. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CloseCircleFilled.js +0 -7
  121. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CloseOutlined.js +0 -7
  122. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CopyOutlined.js +0 -7
  123. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DeleteOutlined.js +0 -7
  124. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DoubleLeftOutlined.js +0 -7
  125. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DoubleRightOutlined.js +0 -7
  126. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DownOutlined.js +0 -7
  127. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DownloadOutlined.js +0 -7
  128. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EditOutlined.js +0 -7
  129. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EllipsisOutlined.js +0 -7
  130. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EnterOutlined.js +0 -7
  131. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ExclamationCircleFilled.js +0 -7
  132. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EyeInvisibleOutlined.js +0 -7
  133. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EyeOutlined.js +0 -7
  134. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FileOutlined.js +0 -7
  135. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FileTextOutlined.js +0 -7
  136. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FilterFilled.js +0 -7
  137. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FolderOpenOutlined.js +0 -7
  138. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FolderOutlined.js +0 -7
  139. package/shell-dist/assets/ojm--ant-design-icons-es-icons-HolderOutlined.js +0 -7
  140. package/shell-dist/assets/ojm--ant-design-icons-es-icons-InfoCircleFilled.js +0 -7
  141. package/shell-dist/assets/ojm--ant-design-icons-es-icons-LeftOutlined.js +0 -7
  142. package/shell-dist/assets/ojm--ant-design-icons-es-icons-LoadingOutlined.js +0 -7
  143. package/shell-dist/assets/ojm--ant-design-icons-es-icons-MinusOutlined.js +0 -7
  144. package/shell-dist/assets/ojm--ant-design-icons-es-icons-MinusSquareOutlined.js +0 -7
  145. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PaperClipOutlined.js +0 -7
  146. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PictureOutlined.js +0 -7
  147. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PlusOutlined.js +0 -7
  148. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PlusSquareOutlined.js +0 -7
  149. package/shell-dist/assets/ojm--ant-design-icons-es-icons-QuestionCircleOutlined.js +0 -7
  150. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ReloadOutlined.js +0 -7
  151. package/shell-dist/assets/ojm--ant-design-icons-es-icons-RightOutlined.js +0 -7
  152. package/shell-dist/assets/ojm--ant-design-icons-es-icons-RotateLeftOutlined.js +0 -7
  153. package/shell-dist/assets/ojm--ant-design-icons-es-icons-RotateRightOutlined.js +0 -7
  154. package/shell-dist/assets/ojm--ant-design-icons-es-icons-SearchOutlined.js +0 -7
  155. package/shell-dist/assets/ojm--ant-design-icons-es-icons-StarFilled.js +0 -7
  156. package/shell-dist/assets/ojm--ant-design-icons-es-icons-SwapOutlined.js +0 -7
  157. package/shell-dist/assets/ojm--ant-design-icons-es-icons-SwapRightOutlined.js +0 -7
  158. package/shell-dist/assets/ojm--ant-design-icons-es-icons-UpOutlined.js +0 -7
  159. package/shell-dist/assets/ojm--ant-design-icons-es-icons-VerticalAlignTopOutlined.js +0 -7
  160. package/shell-dist/assets/ojm--ant-design-icons-es-icons-WarningFilled.js +0 -7
  161. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ZoomInOutlined.js +0 -7
  162. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ZoomOutOutlined.js +0 -7
  163. package/shell-dist/assets/ojm-dayjs-locale-zh-cn.js +0 -81
  164. package/shell-dist/assets/pinyin-pro.js +0 -25175
  165. package/shell-dist/assets/pro-components.js +0 -44627
  166. package/shell-dist/assets/react-countup.js +0 -525
  167. package/shell-dist/assets/react-dom-client.js +0 -12134
  168. package/shell-dist/assets/react-dom.js +0 -266
  169. package/shell-dist/assets/react-error-boundary.js +0 -141
  170. package/shell-dist/assets/react-i18next.js +0 -1773
  171. package/shell-dist/assets/react-jss.js +0 -4089
  172. package/shell-dist/assets/react-query.js +0 -3072
  173. package/shell-dist/assets/react-router-dom.js +0 -9927
  174. package/shell-dist/assets/react-router.js +0 -14584
  175. package/shell-dist/assets/react.js +0 -590
  176. package/shell-dist/assets/runtime.js +0 -28311
  177. package/shell-dist/assets/simplebar-react.js +0 -1119
  178. package/shell-dist/assets/spin-delay.js +0 -111
  179. package/shell-dist/assets/tailwind-merge.js +0 -3364
  180. package/shell-dist/assets/use-sync-external-store-shim.js +0 -130
  181. package/shell-dist/assets/zustand-middleware.js +0 -513
  182. package/shell-dist/assets/zustand-react-shallow.js +0 -27
  183. package/shell-dist/assets/zustand-react.js +0 -39
  184. package/shell-dist/assets/zustand-shallow.js +0 -22
  185. package/shell-dist/assets/zustand-vanilla-shallow.js +0 -71
  186. package/shell-dist/assets/zustand-vanilla.js +0 -40
  187. package/shell-dist/assets/zustand.js +0 -32
  188. /package/shell-dist/assets/{index-xZhmujjM.js → index.i0kqbxp050.js} +0 -0
  189. /package/shell-dist/assets/{nprogress-css.js → nprogress-css.1cq2pzkre0.js} +0 -0
  190. /package/shell-dist/assets/{ojm-antd-es-button.js → ojm-antd-es-button.2uriphio50.js} +0 -0
  191. /package/shell-dist/assets/{ojm-antd-es-modal.js → ojm-antd-es-modal.bf3tgdh8v0.js} +0 -0
  192. /package/shell-dist/assets/{ojm-antd-es-slider.js → ojm-antd-es-slider.xyvptuhid0.js} +0 -0
  193. /package/shell-dist/assets/{ojm-antd-es-upload.js → ojm-antd-es-upload.13gx45e0g0.js} +0 -0
@@ -14,14 +14,19 @@
14
14
  * 本模块只做「扫描产物文本、回答谁导出/导入了什么」,不含修复逻辑,
15
15
  * 供 shell 构建(门禁,失败即中断)与 tests/ 共用,避免两套正则漂移。
16
16
  *
17
- * 解析口径:只认**行首**的 `import` / `export`。esbuild 与 vite 在
18
- * `minify: false` 下顶层语句一律顶格输出,依赖内部的代码有缩进,
19
- * 借此把「字符串字面量里恰好写了 import」这类误报压到最低。
17
+ * 解析口径:`es-module-lexer` 真词法扫描,而非行首正则。
18
+ * 早先的实现假设「esbuild/vite 在 `minify: false` 下顶层语句一律顶格」,一旦
19
+ * 共享资产开启压缩(`minifyWhitespace` 会把 `export{a as b}` / `}from"react"`
20
+ * 挤成一行且行内任意位置),行首锚定的正则全部失配 → 门禁集体误报「零具名导出」
21
+ * 和「裸说明符未登记」。词法扫描只依赖语法、与压缩/换行无关,故换掉。
22
+ * 顺带消灭了「字符串字面量里恰好写了 import/from」的误报(原先靠行首锚定压制)。
20
23
  */
21
24
 
22
25
  import { existsSync, readdirSync, readFileSync } from "node:fs";
23
26
  import { resolve } from "node:path";
24
27
 
28
+ import { ImportType, initSync, parse as lexModule } from "es-module-lexer";
29
+
25
30
  /** 一个 ESM 文件的导出面 */
26
31
  export interface EsmExports {
27
32
  /** 静态具名导出(不含 `default`、不含 `export *` 透传的部分) */
@@ -56,22 +61,6 @@ export interface EsmImport {
56
61
  needsDefault: boolean
57
62
  }
58
63
 
59
- /**
60
- * `export { a, b as c }`(本地导出块)与 `export { a, b as c } from "x"`(再导出)
61
- * 的公共部分:取出**对外暴露**的名字(`b as c` 取 `c`)。
62
- */
63
- function exportedNamesFromClause(clause: string): string[] {
64
- return clause
65
- .split(",")
66
- .map(part => part.trim())
67
- .filter(Boolean)
68
- .map((part) => {
69
- const as = part.split(/\s+as\s+/);
70
- return (as[as.length - 1] ?? part).trim();
71
- })
72
- .filter(name => name !== "default");
73
- }
74
-
75
64
  /**
76
65
  * `import { a, b as c }` 中被**请求**的名字。
77
66
  *
@@ -88,51 +77,75 @@ function importedNamesFromClause(clause: string): string[] {
88
77
  .filter(name => name !== "default");
89
78
  }
90
79
 
80
+ /** wasm 实例化只需一次;按需初始化,避免「只是 import 一下」的消费方付启动开销 */
81
+ let lexReady = false;
82
+ function lex(source: string): ReturnType<typeof lexModule> {
83
+ if (!lexReady) {
84
+ initSync();
85
+ lexReady = true;
86
+ }
87
+ return lexModule(source);
88
+ }
89
+
90
+ /** 语句文本;`ss`/`se` 是 lexer 给出的整条语句区间(含分号) */
91
+ function statementAt(source: string, ss: number, se: number): string {
92
+ return source.slice(ss, se);
93
+ }
94
+
95
+ /**
96
+ * 从一条 `import … from "x"` / `export … from "x"` 语句里分出关键字与子句。
97
+ *
98
+ * 语句边界已由 lexer 确定,这里只处理语句内部的切片,因此不再需要行首锚定,
99
+ * 压缩后(`import{a as b}from"react"`)同样成立。
100
+ */
101
+ function clauseOf(statement: string): { keyword: "import" | "export", clause: string } | undefined {
102
+ const head = /^\s*(import|export)\b/.exec(statement);
103
+ if (!head)
104
+ return undefined;
105
+ const rest = statement.slice(head[0].length);
106
+ // `from` 后必须紧跟说明符字符串,才不会误取子句里的同名标识符
107
+ const fromIdx = rest.search(/\bfrom\s*["']/);
108
+ if (fromIdx < 0)
109
+ return undefined;
110
+ return { keyword: head[1] as "import" | "export", clause: rest.slice(0, fromIdx) };
111
+ }
112
+
91
113
  /** 解析产物的静态导出面 */
92
114
  export function parseEsmExports(source: string): EsmExports {
115
+ const [imports, exports] = lex(source);
93
116
  const names = new Set<string>();
117
+ let hasDefault = false;
94
118
 
95
- // 收集全部 export 子句(本地导出块 + 再导出),具名与 default 两种判定共用
96
- const clauses: string[] = [];
97
- for (const m of source.matchAll(/^export\s*\{([^}]*)\}(?:\s*from\s*["'][^"']+["'])?\s*(?:;\s*)?$/gm)) {
98
- clauses.push(m[1] ?? "");
99
- }
100
-
101
- for (const clause of clauses) {
102
- for (const name of exportedNamesFromClause(clause))
103
- names.add(name);
119
+ for (const exp of exports) {
120
+ if (exp.n === "default")
121
+ hasDefault = true;
122
+ else
123
+ names.add(exp.n);
104
124
  }
105
125
 
106
- // 声明式导出:export const/let/var/function/class/async function
107
- for (const m of source.matchAll(/^export\s+(?:declare\s+)?(?:async\s+)?(?:const|let|var|function\*?|class)\s+([A-Za-z_$][\w$]*)/gm)) {
108
- names.add(m[1]!);
109
- }
126
+ // `export * from "x"` 不产生具名导出(静态不可展开),lexer 也不把它列进 exports,
127
+ // 只能从「语句以 export 开头、星号之后不带 as」反推。
128
+ // `export * as ns from "x"` 是有名字的导出(ns),导出面仍可静态确定,不算在内。
129
+ const hasStarReexport = imports.some((imp) => {
130
+ if (imp.t !== ImportType.Static || imp.ss < 0)
131
+ return false;
132
+ return /^export\s*\*(?!\s*as\b)/.test(statementAt(source, imp.ss, imp.se));
133
+ });
110
134
 
111
- // default 有两副面孔:声明式 `export default …`,或子句里的 `x as default`
112
- // (esbuild 产出的正是后者,形如 `rad_shim_xxx_default as default`)
113
- const hasDefaultClause = clauses.some(clause => clause
114
- .split(",")
115
- .map(part => part.trim())
116
- .some(part => /\bas\s+default$/.test(part)));
117
-
118
- return {
119
- names,
120
- hasDefault: /^export\s+default\b/m.test(source) || hasDefaultClause,
121
- hasStarReexport: /^export\s*\*/m.test(source),
122
- };
135
+ return { names, hasDefault, hasStarReexport };
123
136
  }
124
137
 
125
138
  /**
126
139
  * 解析产物消费了哪些说明符的哪些具名导出。
127
140
  *
128
- * 覆盖三种形态(均为 esbuild / vite 非压缩产物的常见写法):
129
- * `import { a, b as c } from "x"`、`import { a } from "x"`(多行)、
130
- * `export { a } from "x"`(再导出同样是消费)。
141
+ * 覆盖:`import { a, b as c } from "x"`、`import X, { a } from "x"`、
142
+ * `export { a } from "x"`(再导出同样是消费)。
131
143
  *
132
144
  * 不覆盖:`import * as ns from "x"`、动态 `import("x")` 后取属性——
133
145
  * 这些都拿不到具名绑定,无从校验。
134
146
  */
135
147
  export function parseEsmImports(source: string): EsmImport[] {
148
+ const [imports] = lex(source);
136
149
  const found = new Map<string, { names: Set<string>, needsDefault: boolean }>();
137
150
 
138
151
  const slot = (specifier: string) => {
@@ -144,25 +157,30 @@ export function parseEsmImports(source: string): EsmImport[] {
144
157
  return entry;
145
158
  };
146
159
 
147
- // 具名导入(允许跨行);用 [^;]*? 兜住语句边界,避免跨越到下一条语句
148
- for (const m of source.matchAll(/^import\s*\{([^}]*)\}\s*from\s*["']([^"']+)["']/gm)) {
149
- const entry = slot(m[2]!);
150
- for (const name of importedNamesFromClause(m[1] ?? ""))
151
- entry.names.add(name);
152
- }
160
+ for (const imp of imports) {
161
+ // 动态 import / import.meta 不带静态具名绑定
162
+ if (imp.t !== ImportType.Static || imp.ss < 0 || !imp.n)
163
+ continue;
164
+ const parsed = clauseOf(statementAt(source, imp.ss, imp.se));
165
+ if (!parsed)
166
+ continue;
153
167
 
154
- // 具名再导出
155
- for (const m of source.matchAll(/^export\s*\{([^}]*)\}\s*from\s*["']([^"']+)["']/gm)) {
156
- const entry = slot(m[2]!);
157
- for (const name of importedNamesFromClause(m[1] ?? ""))
158
- entry.names.add(name);
159
- }
168
+ const entry = slot(imp.n);
169
+ const brace = /\{([^}]*)\}/.exec(parsed.clause);
170
+ if (brace) {
171
+ for (const name of importedNamesFromClause(brace[1] ?? ""))
172
+ entry.names.add(name);
173
+ }
160
174
 
161
- // default 导入:`import X from "x"`(X 非 { 、非 *)
162
- // 不能假定它一定可用——只有 default 的包若 shim 没兜住,浏览器同样是
163
- // "does not provide an export named 'default'"
164
- for (const m of source.matchAll(/^import\s+([A-Za-z_$][\w$]*)\s+from\s*["']([^"']+)["']/gm)) {
165
- slot(m[2]!).needsDefault = true;
175
+ // default 导入:子句去掉 `{…}` 后仍剩标识符(`X` / `X,`)。
176
+ // `* as ns` 是命名空间导入,不消费 default。
177
+ // 不能假定它一定可用——只有 default 的包若 shim 没兜住,浏览器同样是
178
+ // "does not provide an export named 'default'"。
179
+ if (parsed.keyword === "import") {
180
+ const head = parsed.clause.replace(/\{[^}]*\}/g, "").trim();
181
+ if (head && !head.startsWith("*"))
182
+ entry.needsDefault = true;
183
+ }
166
184
  }
167
185
 
168
186
  return [...found].map(([specifier, entry]) => ({
@@ -233,24 +251,88 @@ export function collectDynamicRequires(distDir: string): DynamicRequireHit[] {
233
251
  }
234
252
 
235
253
  /**
236
- * 产物引用的全部裸说明符(三种形态:具名/副作用 import、`import("x")`、再导出)。
254
+ * 产物引用的全部裸说明符(静态/副作用 import、`import("x")`、再导出)。
237
255
  *
238
256
  * 浏览器只能解析 importmap 里登记过的裸说明符,漏一个就是
239
257
  * `Failed to resolve module specifier` 白屏。
240
258
  */
241
259
  export function parseBareSpecifiers(source: string): string[] {
260
+ const [imports] = lex(source);
242
261
  const found = new Set<string>();
243
- // 行首锚定,规避字符串字面量里的假阳性(P3.5 教训)
244
- const pattern = /^import\s*\(\s*["']([^"']+)["']\s*\)|^(?:import|export)\s[^;]*?from\s*["']([^"']+)["']|^import\s+["']([^"']+)["']/gm;
245
- for (const m of source.matchAll(pattern)) {
246
- const spec = m[1] ?? m[2] ?? m[3];
247
- if (!spec || spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("data:") || spec.startsWith("http"))
262
+ // lexer 已给出每条语句的说明符原文;非字符串字面量(`import(x)`)为 undefined,跳过
263
+ for (const imp of imports) {
264
+ const spec = imp.n;
265
+ if (!spec)
266
+ continue;
267
+ if (spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("data:") || spec.startsWith("http"))
248
268
  continue;
249
269
  found.add(spec);
250
270
  }
251
271
  return [...found];
252
272
  }
253
273
 
274
+ /**
275
+ * 产物的**静态**依赖说明符:`import … from "x"`、副作用 `import "x"`、再导出。
276
+ *
277
+ * 与 {@link parseBareSpecifiers} 的差别有两点,都是为了算「首屏静态闭包」:
278
+ * - **排除动态 `import("x")`**:那是懒加载,不在首屏关键路径上,预取它会白拉;
279
+ * - **保留相对/绝对路径**:闭包要顺着 `/assets/x.hash.js` 继续走,不能只看裸名。
280
+ *
281
+ * 静态图的切分口径与浏览器一致(缓存模块记录前先取完整静态图),所以这里能算出
282
+ * 「浏览器一定会请求的最小集合」。
283
+ */
284
+ export function parseStaticSpecifiers(source: string): string[] {
285
+ const [imports] = lex(source);
286
+ const found = new Set<string>();
287
+ for (const imp of imports) {
288
+ // d === -1 才是静态导入;`import("x")` 的 d 是 `(` 的偏移
289
+ if (imp.d !== -1 || !imp.n)
290
+ continue;
291
+ found.add(imp.n);
292
+ }
293
+ return [...found];
294
+ }
295
+
296
+ /**
297
+ * 首屏静态依赖闭包:从入口出发,顺着**静态** import 一直走到没得走。
298
+ *
299
+ * 这就是浏览器一定会请求的集合(ESM 语义:先取完整静态图再执行),所以可以放心
300
+ * 全量预取;动态 `import()` 不在其中。裸说明符经 importmap 解析,相对路径按当前
301
+ * 文件所在目录拼。
302
+ *
303
+ * `readAsset` 返回 undefined 表示读不到(跨源 CDN 资产、或缺失文件):该 URL 仍算
304
+ * 闭包成员(预取它对浏览器有效),但不再往下展开。
305
+ *
306
+ * 与构建脚本共用同一份实现,是为了让测试能重算一遍对撞产物——两套实现必然漂移。
307
+ */
308
+ export function collectStaticClosure(
309
+ entryUrl: string,
310
+ importmap: Record<string, string>,
311
+ readAsset: (url: string) => string | undefined,
312
+ ): string[] {
313
+ const seen = new Set<string>();
314
+ const queue = [entryUrl];
315
+ while (queue.length) {
316
+ const url = queue.shift()!;
317
+ if (seen.has(url))
318
+ continue;
319
+ seen.add(url);
320
+ const source = readAsset(url);
321
+ if (source === undefined)
322
+ continue;
323
+ for (const spec of parseStaticSpecifiers(source)) {
324
+ const target = spec.startsWith("/")
325
+ ? spec
326
+ : spec.startsWith(".")
327
+ ? resolve("/", url.slice(0, url.lastIndexOf("/") + 1), spec)
328
+ : importmap[spec];
329
+ if (target)
330
+ queue.push(target);
331
+ }
332
+ }
333
+ return [...seen];
334
+ }
335
+
254
336
  /** 一处「资产引用了 importmap 未登记的裸说明符」 */
255
337
  export interface UnresolvedSpecifier {
256
338
  file: string
@@ -1,3 +1,5 @@
1
+ import { createHash } from "node:crypto";
2
+
1
3
  /**
2
4
  * 共享依赖表 —— 单一常量源(设计文档 §4.3,解决 B11 / B12)。
3
5
  *
@@ -21,15 +23,18 @@ export interface SharedDepEntry {
21
23
  /** 硬共享:模块不得自带 */
22
24
  hard: boolean
23
25
  /**
24
- * 软共享但「不进宿主首屏」:宿主 shell 不预构建该资产、importmap 不列该键,
25
- * 模块若 import 它则自行打包进模块产物(设计文档 §4.3 的「软共享缺席→模块自带
26
- * 副本兜底」语义)。用于**仅模块使用、宿主与共享 chunk 永不 import**的重型依赖
27
- * (编辑器 / 图表 / Markdown / 拖拽 …),避免把它们 5~6MB 的体量无谓铺进
28
- * shell-dist 并要求公开链路多传一堆文件。宿主侧有真实 import 的共享依赖
29
- * (antd / pro-components / icons 内部依赖的 motion / react-jss / dayjs /
30
- * zustand 等)绝不能标 bundle —— 否则会被 antd 等共享 chunk 经 importmap 解析
31
- * 不到而 404。判定依据:宿主 host.tsx + runtime.js 的传递 import 图,以及
32
- * antd/pro-components/icons 的传递依赖图,都不含这些说明符。
26
+ * 模块自打包:宿主 shell 不预构建该资产、importmap 不列该键,模块若 import 它
27
+ * 则自行打包进模块产物(设计文档 §4.3 的「软共享缺席→模块自带副本兜底」语义)。
28
+ * 用于**仅模块使用、宿主与共享 chunk 永不 import**的重型依赖(编辑器 / 图表 /
29
+ * Markdown),避免把它们 5~6MB 的体量无谓铺进 shell-dist 并要求公开链路多传
30
+ * 一堆文件。宿主侧有真实 import 的共享依赖(antd / pro-components / icons 内部
31
+ * 依赖的 motion / react-jss / dayjs / zustand / @dnd-kit 等)绝不能标 bundle ——
32
+ * 否则会被 antd 等共享 chunk 经 importmap 解析不到而 404。判定依据:宿主
33
+ * host.tsx + runtime.js 的传递 import 图,以及 antd/pro-components/icons 的
34
+ * 传递依赖图,都不含这些说明符。
35
+ *
36
+ * ⚠️ 本档位**不计入宿主版本一致性保障**:模块把副本打进产物,多副本不会报错,
37
+ * 只会重复体积。体积怎么控,见 docs/devkit/manual.md §2.6。
33
38
  */
34
39
  bundle?: boolean
35
40
  }
@@ -66,9 +71,10 @@ export const SHARED_DEPS: SharedDepEntry[] = [
66
71
  * - 无单例语义。kit 必须唯一是因为多副本 = 两份 antd 上下文树;编辑器资产
67
72
  * 把 @tiptap/*、@plane/ui 等**全部内联**在自身里,跨边界穿过的只有 React
68
73
  * 家族(那是独立的硬共享条目),多一份编辑器副本不会拆散任何 context。
69
- * - 不是每个模块都用编辑器。软共享的语义正是「默认由宿主提供,允许 importmap
70
- * scopes 多版本共存兜底(C7)」——缺席时模块可自带副本而不硬失败。
71
- * 代价:多副本会重复约 5.4 MB 资产,故 scopes 只作兜底、默认仍走宿主。
74
+ * - 不是每个模块都用编辑器。软共享的语义是「允许缺席」——宿主**不预构建**
75
+ * 这份资产(bundle: true,不进 shell-dist、不进 importmap),模块 import 它
76
+ * 就自己打进产物。代价是每个用编辑器的模块各带一份约 5.4 MB,换来宿主
77
+ * 分发体积不被没人用的重型依赖撑大。
72
78
  */
73
79
  { specifier: "@plane/editor", asset: "editor", hard: false, bundle: true },
74
80
  // 注:@rc-component/form(antd 6 Form 底层)曾尝试单例化以修 my-profile
@@ -120,6 +126,9 @@ export const SHARED_DEPS: SharedDepEntry[] = [
120
126
  { specifier: "dayjs/plugin/weekday", asset: "dayjs-plugin-weekday", hard: false },
121
127
  { specifier: "dayjs/plugin/weekOfYear", asset: "dayjs-plugin-weekOfYear", hard: false },
122
128
  { specifier: "dayjs/plugin/weekYear", asset: "dayjs-plugin-weekYear", hard: false },
129
+ // echarts 家族:模块自打包。整包 index(`import * as echarts from "echarts"`)会让
130
+ // 单个模块产物从 ~180KB 涨到 1.7MB(实测 echarts 918KB + zrender 221KB 占 87%),
131
+ // 故脚手架改用 `echarts/core` 按需注册;接入方写法见 docs/devkit/manual.md §2.6。
123
132
  { specifier: "echarts", asset: "echarts", hard: false, bundle: true },
124
133
  { specifier: "echarts/charts", asset: "echarts-charts", hard: false, bundle: true },
125
134
  { specifier: "echarts/features.js", asset: "echarts-features", hard: false, bundle: true },
@@ -147,18 +156,21 @@ export const SHARED_DEPS: SharedDepEntry[] = [
147
156
  { specifier: "tailwind-merge", asset: "tailwind-merge", hard: false },
148
157
  /**
149
158
  * Markdown 编辑器(Vditor v4,M5 回升自下游 oj-wiki):npm 包是 webpack **UMD**
150
- * 单产物(main=dist/index.js,无 ESM 出口、仅 default 导出),走 starShim + esbuild
151
- * 的既有 CJS/UMD interop 链路打包成 ESM 资产(与 react 家族同路,无需新机制)。
159
+ * 单产物(main=dist/index.js,无 ESM 出口、仅 default 导出)。bundle: true 表示
160
+ * 宿主不预构建 —— CJS/UMD interop 交给模块自己的 rolldown 打包链路处理。
152
161
  *
153
- * 软共享:无单例语义(多副本只是重复体积,不拆 context),且不是每个模块都用。
154
- * 体积大(gzip ≈700 KiB),故**惰性生效**:importmap 只做映射,宿主 shell 自身
155
- * 不 import vditor ⇒ /assets/vditor.js 仅在模块产物真正 import 它时才被拉取,
156
- * 不进宿主首屏。
162
+ * 为什么不预构建:体积大(gzip ≈700 KiB)且不是每个模块都用。宿主为它留一份
163
+ * /assets/vditor.js,等于给所有站点无条件加 700 KiB 分发体积,而只有编辑器页
164
+ * 才用得上。改由模块自带后,代价转给「真正 import 它的那个模块」。
165
+ *
166
+ * 副作用(写文档时容易说错):模块产物是单文件(codeSplitting: false),
167
+ * 所以 `import("vditor")` 会被内联回去,**动态导入并不能把它从首屏摘掉**;
168
+ * 要省这笔体积只能把编辑器页拆成独立模块/方案。
157
169
  *
158
170
  * 深路径只登记 CSS 主样式(nprogress 同款样式垫片)。vditor 其余 dist 深路径
159
171
  * (lute / i18n / icons / 主题 CSS…)是**运行期经 cdn 选项按需拉取的资源**而非
160
172
  * import 说明符,由模块侧自行处理(自注入或指向宿主可服务的路径);若模块代码
161
- * 直接 import 它们,会命中前缀 external 但无 importmap 键 → 构建期深路径报错,
173
+ * 直接 import 它们,会命中前缀 external 但无共享表键 → 构建期深路径报错,
162
174
  * 属预期拦截(与 dayjs/plugin/* 同规则)。
163
175
  */
164
176
  { specifier: "vditor", asset: "vditor", hard: false, bundle: true },
@@ -182,6 +194,10 @@ export function generateShellEntries(): { name: string, pkg: string }[] {
182
194
  /**
183
195
  * 由共享表生成 importmap:说明符 → /assets/<asset>.js。
184
196
  * baseUrl 供宿主部署在子路径时使用。
197
+ *
198
+ * 注意:这里给出的是**逻辑名**。shell 构建落盘时会把每个资产改名成
199
+ * `<asset>.<hash>.js`(见 `hashedAssetFileName`),importmap 由构建脚本同步改写,
200
+ * 运行时拿到的仍是 `/assets/<asset>.<hash>.js`。
185
201
  */
186
202
  export function generateImportmap(baseUrl = ""): Record<string, string> {
187
203
  const map: Record<string, string> = {};
@@ -196,3 +212,37 @@ export function generateImportmap(baseUrl = ""): Record<string, string> {
196
212
  export function isPrebuiltDep(id: string): boolean {
197
213
  return SHARED_DEPS.some(dep => !dep.bundle && (id === dep.specifier || id.startsWith(`${dep.specifier}/`)));
198
214
  }
215
+
216
+ /**
217
+ * 共享资产的内容哈希段:10 位 base36,且**保证**同时含数字与字母。
218
+ *
219
+ * 为什么必须保证:oj serve ≥ 0.1.116 的默认缓存策略按**文件名**判定
220
+ * (`serve/src/static_files.rs::default_cache_control`)——文件名 stem 按 `-` `_` `.`
221
+ * 切段,末段 ≥8 位且同时含数字与字母才发 `public, max-age=31536000, immutable`,
222
+ * 其余一律 `no-cache`。纯 base36 有十万分之一量级会全数字(vite 的 base64url 更糟:
223
+ * 8 位全字母约 19%),漏判就白丢一年缓存。这里按「缺哪类补哪类」定长补齐,
224
+ * 让每个资产 100% 命中长缓存,而不是赌概率。
225
+ *
226
+ * 9 位 base36 ≈ 46.5 bit,65 个资产量级碰撞概率可忽略。
227
+ */
228
+ export function assetHash(content: string | Uint8Array): string {
229
+ const digest = createHash("sha256").update(content).digest();
230
+ const body = digest.readBigUInt64BE(0).toString(36).slice(0, 9).padStart(9, "0");
231
+ // 已有字母 → 补数字;全数字 → 补字母。两类必在其一,补齐另一类即可。
232
+ return /[a-z]/.test(body) ? `${body}0` : `${body}z`;
233
+ }
234
+
235
+ /** 内容寻址文件名:`<base>.<hash>.js`(js 与 map 同为 .js 后缀,map 保持原名不动) */
236
+ export function hashedAssetFileName(base: string, content: string | Uint8Array): string {
237
+ return `${base}.${assetHash(content)}.js`;
238
+ }
239
+
240
+ /**
241
+ * 与 oj serve 的 `default_cache_control` 同口径:末段 ≥8 位且数字字母都有 → 可发 immutable。
242
+ * 构建期用它断言产物名,避免哪天哈希算法换掉后长缓存静默失效。
243
+ */
244
+ export function isImmutableAssetFileName(fileName: string): boolean {
245
+ const stem = fileName.replace(/\.[^.]*$/, "");
246
+ const seg = stem.split(/[-_.]/).pop() ?? "";
247
+ return seg.length >= 8 && /\d/.test(seg) && /[a-z]/i.test(seg);
248
+ }
package/src/site-base.ts CHANGED
@@ -102,6 +102,10 @@ export function rewriteIndexHtml(html: string, siteBase: string | undefined, api
102
102
  // 上一步已处理,二者不重叠。
103
103
  out = out.replace(/(\b(?:href|src|data-ojm-entry)=")(\/[^/"][^"]*|\/")/g, (_m, attr: string, url: string) =>
104
104
  `${attr}${withSiteBase(url, base)}`);
105
+ // data-ojm-preload 是**逗号分隔的 URL 列表**(构建期算好的首屏静态闭包),
106
+ // 上面那条单值正则匹配不到列表,漏改会让子路径部署下预取全 404(退化回瀑布,不白屏但慢)
107
+ out = out.replace(/(\bdata-ojm-preload=")([^"]*)"/g, (_m, attr: string, list: string) =>
108
+ `${attr}${list.split(",").map(url => url.startsWith("/") ? withSiteBase(url, base) : url).join(",")}"`);
105
109
  globals.push(`window.__OJ_BASE__=${JSON.stringify(base)};`);
106
110
  }
107
111
  if (api) {
@@ -1,9 +1,11 @@
1
1
  import type { EChartsOption } from "echarts";
2
2
  import { Card } from "antd";
3
- import ReactECharts from "echarts-for-react";
3
+ import ReactEChartsCore from "echarts-for-react/esm/core";
4
4
  import { useTranslation } from "react-i18next";
5
5
 
6
- /** 柱状图:演示 `echarts` + `echarts-for-react`(共享依赖,宿主 importmap 提供)。 */
6
+ import { echarts } from "./echarts-setup";
7
+
8
+ /** 柱状图:按需注册的 `echarts` + `echarts-for-react/esm/core`(见 echarts-setup.ts)。 */
7
9
  export default function BarChart() {
8
10
  const { t } = useTranslation();
9
11
  const categories = [
@@ -25,7 +27,7 @@ export default function BarChart() {
25
27
  };
26
28
  return (
27
29
  <Card title={t("home:views")}>
28
- <ReactECharts opts={{ height: 320, width: "auto" }} option={option} />
30
+ <ReactEChartsCore echarts={echarts} opts={{ height: 320, width: "auto" }} option={option} />
29
31
  </Card>
30
32
  );
31
33
  }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 按需注册的 echarts 实例(体积治理)。
3
+ *
4
+ * 为什么不用 `import ReactECharts from "echarts-for-react"`:
5
+ * 那条路径会 `import * as echarts from "echarts"`(整包 index),把 **全部图表类型 +
6
+ * 全部组件 + 全部渲染器** 打进模块产物。实测 `app` 模块 entry.js 1,697,666 B 中
7
+ * echarts 918 KB + zrender 221 KB,占 **87%**;而首页只用到折线/柱状/饼图三种。
8
+ *
9
+ * 这里改用 `echarts/core` 按需注册:只留 3 种图表 + 3 个组件 + Canvas 渲染器,
10
+ * 同一份代码实测 1,125 KB → 572 KB(−49%)。
11
+ *
12
+ * 新图表类型/组件时,务必在这里 `use()` 补上——否则运行期渲染为空且**不报错**,
13
+ * 是最常见的 echarts 按需踩坑。
14
+ */
15
+ import { BarChart, LineChart, PieChart } from "echarts/charts";
16
+ import { GridComponent, LegendComponent, TooltipComponent } from "echarts/components";
17
+ import * as echarts from "echarts/core";
18
+ import { CanvasRenderer } from "echarts/renderers";
19
+
20
+ echarts.use([
21
+ BarChart,
22
+ LineChart,
23
+ PieChart,
24
+ GridComponent,
25
+ LegendComponent,
26
+ TooltipComponent,
27
+ CanvasRenderer,
28
+ ]);
29
+
30
+ export { echarts };
@@ -1,12 +1,14 @@
1
1
  import type { EChartsOption } from "echarts";
2
2
  import { Card, Radio } from "antd";
3
3
  import dayjs from "dayjs";
4
- import ReactECharts from "echarts-for-react";
4
+ import ReactEChartsCore from "echarts-for-react/esm/core";
5
5
  import { useMemo, useState } from "react";
6
6
  import { useTranslation } from "react-i18next";
7
7
 
8
+ import { echarts } from "./echarts-setup";
9
+
8
10
  /**
9
- * 折线图:演示 `echarts` + `echarts-for-react` + `dayjs`(共享依赖)。
11
+ * 折线图:按需注册的 `echarts` + `echarts-for-react/esm/core`(见 echarts-setup.ts)。
10
12
  * 数据为静态演示值——真实业务请走模块契约 + `ojm api` 生成的 client
11
13
  * (参考 `demo` 模块的裸 fetch 与教程里 books 模块的契约式接法)。
12
14
  */
@@ -69,7 +71,7 @@ export default function LineChart() {
69
71
  </Radio.Group>
70
72
  )}
71
73
  >
72
- <ReactECharts opts={{ height: 320, width: "auto" }} option={option} />
74
+ <ReactEChartsCore echarts={echarts} opts={{ height: 320, width: "auto" }} option={option} />
73
75
  </Card>
74
76
  );
75
77
  }
@@ -1,11 +1,14 @@
1
1
  import type { EChartsOption } from "echarts";
2
2
  import { Card, Segmented } from "antd";
3
- import ReactECharts from "echarts-for-react";
3
+ import ReactEChartsCore from "echarts-for-react/esm/core";
4
4
  import { useMemo, useState } from "react";
5
5
  import { useTranslation } from "react-i18next";
6
6
 
7
+ import { echarts } from "./echarts-setup";
8
+
7
9
  /**
8
- * 饼图:演示 `echarts` + `echarts-for-react` + antd `Segmented` 联动。
10
+ * 饼图:按需注册的 `echarts` + `echarts-for-react/esm/core` + antd `Segmented` 联动
11
+ * (注册清单见 echarts-setup.ts)。
9
12
  * 数据为静态演示值(各渠道 × 品类)。
10
13
  */
11
14
  const VALUES: Record<string, number[]> = {
@@ -59,7 +62,7 @@ export default function PieChart() {
59
62
  />
60
63
  )}
61
64
  >
62
- <ReactECharts opts={{ height: 320, width: "auto" }} option={option} />
65
+ <ReactEChartsCore echarts={echarts} opts={{ height: 320, width: "auto" }} option={option} />
63
66
  </Card>
64
67
  );
65
68
  }
@@ -19,8 +19,9 @@ const wrapperCol: ColProps = {
19
19
 
20
20
  /**
21
21
  * 首页:结构对齐 playground home,用自带的统计卡片与三类图表
22
- * 演示宿主共享依赖矩阵(echarts / echarts-for-react / react-countup / dayjs /
23
- * antd / @ant-design/icons / react-i18next 均由宿主 importmap 单例提供)。
22
+ * 演示依赖矩阵(react-countup / dayjs / antd / @ant-design/icons / react-i18next
23
+ * 由宿主 importmap 单例提供;echarts 由模块自打包并按需注册,
24
+ * 见 `./components/echarts-setup.ts`)。
24
25
  */
25
26
  export default function HomePage() {
26
27
  return (
@@ -14,8 +14,9 @@ const Home = lazy(() => import("./pages/index"));
14
14
  * 工程缺 `/home` 路由时这些跳转会落到错误边界。
15
15
  *
16
16
  * 结构对齐 playground-oj/web/src/home;页面用统计卡片 + 折线/柱/饼图
17
- * 演示宿主共享依赖矩阵(echarts / echarts-for-react / react-countup / dayjs /
18
- * antd / @ant-design/icons / react-i18next,均由宿主 importmap 提供单例)。
17
+ * 演示依赖矩阵(react-countup / dayjs / antd / @ant-design/icons / react-i18next
18
+ * 由宿主 importmap 提供单例;echarts 属模块自打包的重型依赖,走按需注册,
19
+ * 见 `pages/components/echarts-setup.ts`)。
19
20
  * 图表数据为静态演示值——真实业务走模块契约 + `ojm api` 生成的 client。
20
21
  */
21
22
  export const routes: AppRouteRecordRaw[] = [
@@ -8,9 +8,9 @@
8
8
  "react-router": "7.18.2",
9
9
  "react-router/dom": "7.18.2",
10
10
  "@tanstack/react-query": "5.102.8",
11
- "@oj-module/runtime": "0.2.64",
12
- "@oj-module/runtime/contract": "0.2.64",
13
- "@oj-module/runtime/contract/errors": "0.2.64",
11
+ "@oj-module/runtime": "0.2.66",
12
+ "@oj-module/runtime/contract": "0.2.66",
13
+ "@oj-module/runtime/contract/errors": "0.2.66",
14
14
  "@plane/kit": "0.1.20",
15
15
  "@plane/editor": "0.1.20",
16
16
  "antd": "6.6.1",