@maiyunnet/kebab 9.0.2 → 9.1.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 (166) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +0 -0
  3. package/bin/kebab.js +0 -0
  4. package/doc/kebab-rag.md +604 -604
  5. package/index.d.ts +1 -1
  6. package/index.js +1 -1
  7. package/lib/ai.d.ts +0 -0
  8. package/lib/ai.js +0 -0
  9. package/lib/buffer.d.ts +0 -0
  10. package/lib/buffer.js +0 -0
  11. package/lib/captcha/zcool-addict-italic.ttf +0 -0
  12. package/lib/captcha.d.ts +0 -0
  13. package/lib/captcha.js +0 -0
  14. package/lib/consistent.d.ts +0 -0
  15. package/lib/consistent.js +0 -0
  16. package/lib/core.d.ts +0 -0
  17. package/lib/core.js +0 -0
  18. package/lib/cron.d.ts +0 -0
  19. package/lib/cron.js +0 -0
  20. package/lib/crypto.d.ts +0 -0
  21. package/lib/crypto.js +0 -0
  22. package/lib/db/conn.d.ts +0 -0
  23. package/lib/db/conn.js +0 -0
  24. package/lib/db/pool.d.ts +0 -0
  25. package/lib/db/pool.js +0 -0
  26. package/lib/db/tran.d.ts +0 -0
  27. package/lib/db/tran.js +8 -8
  28. package/lib/db.d.ts +0 -0
  29. package/lib/db.js +0 -0
  30. package/lib/dns.d.ts +0 -0
  31. package/lib/dns.js +0 -0
  32. package/lib/fs.d.ts +0 -0
  33. package/lib/fs.js +0 -0
  34. package/lib/kv.d.ts +0 -0
  35. package/lib/kv.js +0 -0
  36. package/lib/lan.d.ts +0 -0
  37. package/lib/lan.js +0 -0
  38. package/lib/lang.d.ts +0 -0
  39. package/lib/lang.js +0 -0
  40. package/lib/net/cacert.pem +0 -0
  41. package/lib/net/formdata.d.ts +0 -0
  42. package/lib/net/formdata.js +0 -0
  43. package/lib/net/request.d.ts +0 -0
  44. package/lib/net/request.js +0 -0
  45. package/lib/net/response.d.ts +0 -0
  46. package/lib/net/response.js +0 -0
  47. package/lib/net.d.ts +0 -0
  48. package/lib/net.js +0 -0
  49. package/lib/ratelimit.d.ts +0 -0
  50. package/lib/ratelimit.js +0 -0
  51. package/lib/s3.d.ts +0 -0
  52. package/lib/s3.js +0 -0
  53. package/lib/scan.d.ts +0 -0
  54. package/lib/scan.js +0 -0
  55. package/lib/session.d.ts +0 -0
  56. package/lib/session.js +0 -0
  57. package/lib/socket.d.ts +0 -0
  58. package/lib/socket.js +0 -0
  59. package/lib/sql.d.ts +0 -0
  60. package/lib/sql.js +0 -0
  61. package/lib/ssh/sftp.d.ts +0 -0
  62. package/lib/ssh/sftp.js +0 -0
  63. package/lib/ssh/shell.d.ts +0 -0
  64. package/lib/ssh/shell.js +0 -0
  65. package/lib/ssh.d.ts +0 -0
  66. package/lib/ssh.js +0 -0
  67. package/lib/text/tld.json +0 -0
  68. package/lib/text.d.ts +0 -0
  69. package/lib/text.js +0 -0
  70. package/lib/time.d.ts +0 -0
  71. package/lib/time.js +0 -0
  72. package/lib/turnstile.d.ts +0 -0
  73. package/lib/turnstile.js +0 -0
  74. package/lib/vector.d.ts +0 -0
  75. package/lib/vector.js +0 -0
  76. package/lib/ws.d.ts +0 -0
  77. package/lib/ws.js +0 -0
  78. package/lib/zip.d.ts +0 -0
  79. package/lib/zip.js +0 -0
  80. package/lib/zlib.d.ts +0 -0
  81. package/lib/zlib.js +0 -0
  82. package/main.d.ts +0 -0
  83. package/main.js +0 -0
  84. package/package.json +9 -2
  85. package/sys/child.d.ts +0 -0
  86. package/sys/child.js +0 -0
  87. package/sys/cmd.d.ts +0 -0
  88. package/sys/cmd.js +69 -42
  89. package/sys/ctr.d.ts +8 -0
  90. package/sys/ctr.js +86 -36
  91. package/sys/master.d.ts +0 -0
  92. package/sys/master.js +0 -0
  93. package/sys/mod.d.ts +0 -0
  94. package/sys/mod.js +0 -0
  95. package/sys/monitor/watchdog.d.ts +0 -0
  96. package/sys/monitor/watchdog.js +0 -0
  97. package/sys/monitor.d.ts +0 -0
  98. package/sys/monitor.js +1 -1
  99. package/sys/route.d.ts +0 -0
  100. package/sys/route.js +0 -0
  101. package/www/example/ctr/main.d.ts +0 -0
  102. package/www/example/ctr/main.js +0 -0
  103. package/www/example/ctr/middle.d.ts +0 -0
  104. package/www/example/ctr/middle.js +0 -0
  105. package/www/example/ctr/test.d.ts +4 -0
  106. package/www/example/ctr/test.js +12 -3
  107. package/www/example/data/locale/en.test.json +0 -0
  108. package/www/example/data/locale/index.html +0 -0
  109. package/www/example/data/locale/ja.test.json +0 -0
  110. package/www/example/data/locale/sc.test.json +0 -0
  111. package/www/example/data/locale/tc.test.json +0 -0
  112. package/www/example/data/test.zip +0 -0
  113. package/www/example/kebab.json +0 -0
  114. package/www/example/mod/test.d.ts +0 -0
  115. package/www/example/mod/test.js +0 -0
  116. package/www/example/mod/testdata.d.ts +0 -0
  117. package/www/example/mod/testdata.js +0 -0
  118. package/www/example/route.json +0 -0
  119. package/www/example/stc/chunk-YJ3GYATF.js +81 -0
  120. package/www/example/stc/lib/ui/checkbox.d.ts +9 -0
  121. package/www/example/stc/lib/ui/checkbox.js +11 -0
  122. package/www/example/stc/lib/ui/checkbox.tsx +30 -0
  123. package/www/example/stc/lib/ui/input.d.ts +3 -0
  124. package/www/example/stc/lib/ui/input.js +10 -0
  125. package/www/example/stc/lib/ui/input.tsx +24 -0
  126. package/www/example/stc/lib/ui/label.d.ts +11 -0
  127. package/www/example/stc/lib/ui/label.js +13 -0
  128. package/www/example/stc/lib/ui/label.tsx +24 -0
  129. package/www/example/stc/lib/ui/switch.d.ts +9 -0
  130. package/www/example/stc/lib/ui/switch.js +11 -0
  131. package/www/example/stc/lib/ui/switch.tsx +31 -0
  132. package/www/example/stc/lib/utils.d.ts +7 -0
  133. package/www/example/stc/lib/utils.js +10 -0
  134. package/www/example/stc/view/hello.page.bundle.js +1 -0
  135. package/www/example/stc/view/hello.page.css +2 -0
  136. package/www/example/stc/view/hello.page.d.ts +17 -0
  137. package/www/example/stc/view/hello.page.js +15 -0
  138. package/www/example/stc/view/hello.page.tsx +49 -0
  139. package/www/example/stc/view/react-router.page.bundle.js +1 -0
  140. package/www/example/stc/view/react-router.page.css +2 -0
  141. package/www/example/stc/view/{react-router-page.d.ts → react-router.page.d.ts} +1 -1
  142. package/www/example/stc/view/{react-router-page.js → react-router.page.js} +1 -1
  143. package/www/example/stc/view/{react-router-page.tsx → react-router.page.tsx} +1 -1
  144. package/www/example/stc/view/react.page.bundle.js +26 -0
  145. package/www/example/stc/view/react.page.css +2 -0
  146. package/www/example/stc/view/{react-page.d.ts → react.page.d.ts} +16 -18
  147. package/www/example/stc/view/react.page.js +181 -0
  148. package/www/example/stc/view/{react-page.tsx → react.page.tsx} +259 -111
  149. package/www/example/view/test.ejs +0 -0
  150. package/www/example/ws/handler.d.ts +0 -0
  151. package/www/example/ws/handler.js +0 -0
  152. package/www/example/ws/main.d.ts +0 -0
  153. package/www/example/ws/main.js +0 -0
  154. package/www/example/ws/mproxy.d.ts +0 -0
  155. package/www/example/ws/mproxy.js +0 -0
  156. package/www/example/ws/rproxy.d.ts +0 -0
  157. package/www/example/ws/rproxy.js +0 -0
  158. package/www/example/ws/rsocket.d.ts +0 -0
  159. package/www/example/ws/rsocket.js +0 -0
  160. package/www/example/ws/test.d.ts +0 -0
  161. package/www/example/ws/test.js +0 -0
  162. package/www/example/stc/view/react-page.bundle.js +0 -97
  163. package/www/example/stc/view/react-page.css +0 -2
  164. package/www/example/stc/view/react-page.js +0 -136
  165. package/www/example/stc/view/react-router-page.bundle.js +0 -81
  166. package/www/example/stc/view/react-router-page.css +0 -2
package/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * --- 本文件用来定义每个目录实体地址的常量 ---
6
6
  */
7
7
  /** --- 当前系统版本号 --- */
8
- export declare const VER = "9.0.2";
8
+ export declare const VER = "9.1.0";
9
9
  /** --- 框架根目录,以 / 结尾 --- */
10
10
  export declare const ROOT_PATH: string;
11
11
  /** --- 框架的 LIB,以 / 结尾 --- */
package/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * --- 本文件用来定义每个目录实体地址的常量 ---
7
7
  */
8
8
  /** --- 当前系统版本号 --- */
9
- export const VER = '9.0.2';
9
+ export const VER = '9.1.0';
10
10
  // --- 服务端用的路径 ---
11
11
  const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
12
12
  /** --- /xxx/xxx --- */
package/lib/ai.d.ts CHANGED
File without changes
package/lib/ai.js CHANGED
File without changes
package/lib/buffer.d.ts CHANGED
File without changes
package/lib/buffer.js CHANGED
File without changes
File without changes
package/lib/captcha.d.ts CHANGED
File without changes
package/lib/captcha.js CHANGED
File without changes
File without changes
package/lib/consistent.js CHANGED
File without changes
package/lib/core.d.ts CHANGED
File without changes
package/lib/core.js CHANGED
File without changes
package/lib/cron.d.ts CHANGED
File without changes
package/lib/cron.js CHANGED
File without changes
package/lib/crypto.d.ts CHANGED
File without changes
package/lib/crypto.js CHANGED
File without changes
package/lib/db/conn.d.ts CHANGED
File without changes
package/lib/db/conn.js CHANGED
File without changes
package/lib/db/pool.d.ts CHANGED
File without changes
package/lib/db/pool.js CHANGED
File without changes
package/lib/db/tran.d.ts CHANGED
File without changes
package/lib/db/tran.js CHANGED
@@ -42,8 +42,8 @@ export class Transaction {
42
42
  async query(sql, values) {
43
43
  if (!this._conn) {
44
44
  // --- 当前连接已不可用 ---
45
- lCore.display('[DB][Transaction][query] has been closed ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr') + ': ' + sql);
46
- lCore.log({}, '[DB][Transaction][query] has been closed ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr') + ': ' + sql, '-error');
45
+ lCore.display('[DB][Transaction][query] has been closed, so you cannot execute SQL: ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr') + ': ' + sql);
46
+ lCore.log({}, '[DB][Transaction][query] has been closed, so you cannot execute SQL: ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr') + ': ' + sql, '-error');
47
47
  return {
48
48
  'rows': null,
49
49
  'fields': [],
@@ -65,8 +65,8 @@ export class Transaction {
65
65
  async execute(sql, values) {
66
66
  if (!this._conn) {
67
67
  // --- 当前连接已不可用 ---
68
- lCore.display('[DB][Transaction][execute] has been closed ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr') + ': ' + sql);
69
- lCore.log({}, '(db.Transaction.execute) has been closed ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr') + ': ' + sql, '-error');
68
+ lCore.display('[DB][Transaction][execute] has been closed, so you cannot execute SQL: ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr') + ': ' + sql);
69
+ lCore.log({}, '[DB][Transaction][execute] has been closed, so you cannot execute SQL: ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr') + ': ' + sql, '-error');
70
70
  return {
71
71
  'packet': null,
72
72
  'fields': [],
@@ -83,8 +83,8 @@ export class Transaction {
83
83
  async commit() {
84
84
  if (!this._conn) {
85
85
  // --- 当前连接已不可用 ---
86
- lCore.display('[DB][Transaction][commit] has been closed ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr'));
87
- lCore.log({}, '[DB][Transaction][commit] has been closed ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr'), '-error');
86
+ lCore.display('[DB][Transaction][commit] has been closed, so you cannot commit: ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr'));
87
+ lCore.log({}, '[DB][Transaction][commit] has been closed, so you cannot commit: ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr'), '-error');
88
88
  return false;
89
89
  }
90
90
  const r = await this._conn.commit();
@@ -104,8 +104,8 @@ export class Transaction {
104
104
  async rollback() {
105
105
  if (!this._conn) {
106
106
  // --- 当前连接已不可用 ---
107
- lCore.display('[DB][Transaction][rollback] has been closed: ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr'));
108
- lCore.log({}, '[DB][Transaction][rollback] has been closed: ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr'), '-error');
107
+ lCore.display('[DB][Transaction][rollback] has been closed, so you cannot rollback: ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr'));
108
+ lCore.log({}, '[DB][Transaction][rollback] has been closed, so you cannot rollback: ' + (this._ctr?.getPrototype('_config').const.path ?? 'no ctr'), '-error');
109
109
  return false;
110
110
  }
111
111
  const r = await this._conn.rollback();
package/lib/db.d.ts CHANGED
File without changes
package/lib/db.js CHANGED
File without changes
package/lib/dns.d.ts CHANGED
File without changes
package/lib/dns.js CHANGED
File without changes
package/lib/fs.d.ts CHANGED
File without changes
package/lib/fs.js CHANGED
File without changes
package/lib/kv.d.ts CHANGED
File without changes
package/lib/kv.js CHANGED
File without changes
package/lib/lan.d.ts CHANGED
File without changes
package/lib/lan.js CHANGED
File without changes
package/lib/lang.d.ts CHANGED
File without changes
package/lib/lang.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/lib/net.d.ts CHANGED
File without changes
package/lib/net.js CHANGED
File without changes
File without changes
package/lib/ratelimit.js CHANGED
File without changes
package/lib/s3.d.ts CHANGED
File without changes
package/lib/s3.js CHANGED
File without changes
package/lib/scan.d.ts CHANGED
File without changes
package/lib/scan.js CHANGED
File without changes
package/lib/session.d.ts CHANGED
File without changes
package/lib/session.js CHANGED
File without changes
package/lib/socket.d.ts CHANGED
File without changes
package/lib/socket.js CHANGED
File without changes
package/lib/sql.d.ts CHANGED
File without changes
package/lib/sql.js CHANGED
File without changes
package/lib/ssh/sftp.d.ts CHANGED
File without changes
package/lib/ssh/sftp.js CHANGED
File without changes
File without changes
package/lib/ssh/shell.js CHANGED
File without changes
package/lib/ssh.d.ts CHANGED
File without changes
package/lib/ssh.js CHANGED
File without changes
package/lib/text/tld.json CHANGED
File without changes
package/lib/text.d.ts CHANGED
File without changes
package/lib/text.js CHANGED
File without changes
package/lib/time.d.ts CHANGED
File without changes
package/lib/time.js CHANGED
File without changes
File without changes
package/lib/turnstile.js CHANGED
File without changes
package/lib/vector.d.ts CHANGED
File without changes
package/lib/vector.js CHANGED
File without changes
package/lib/ws.d.ts CHANGED
File without changes
package/lib/ws.js CHANGED
File without changes
package/lib/zip.d.ts CHANGED
File without changes
package/lib/zip.js CHANGED
File without changes
package/lib/zlib.d.ts CHANGED
File without changes
package/lib/zlib.js CHANGED
File without changes
package/main.d.ts CHANGED
File without changes
package/main.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maiyunnet/kebab",
3
- "version": "9.0.2",
3
+ "version": "9.1.0",
4
4
  "description": "Simple, easy-to-use, and fully-featured Node.js framework that is ready-to-use out of the box.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -28,12 +28,18 @@
28
28
  "@litert/mime": "^0.1.3",
29
29
  "@litert/redis": "^3.2.0",
30
30
  "@litert/websocket": "^0.2.8",
31
+ "@radix-ui/react-checkbox": "^1.3.3",
32
+ "@radix-ui/react-label": "^2.1.8",
33
+ "@radix-ui/react-slot": "^1.2.4",
34
+ "@radix-ui/react-switch": "^1.2.6",
35
+ "@tailwindcss/cli": "^4.2.2",
31
36
  "@types/ssh2": "^1.15.5",
32
37
  "@zilliz/milvus2-sdk-node": "^2.6.11",
33
38
  "ajv": "^8.18.0",
34
39
  "ajv-formats": "^3.0.1",
40
+ "class-variance-authority": "^0.7.1",
41
+ "clsx": "^2.1.1",
35
42
  "ejs": "^5.0.1",
36
- "@tailwindcss/cli": "^4.2.2",
37
43
  "esbuild": "^0.27.4",
38
44
  "jszip": "^3.10.1",
39
45
  "mysql2": "^3.20.0",
@@ -45,6 +51,7 @@
45
51
  "react-router-dom": "^7.13.1",
46
52
  "ssh2": "^1.17.0",
47
53
  "svg-captcha": "^1.4.0",
54
+ "tailwind-merge": "^3.5.0",
48
55
  "tencentcloud-sdk-nodejs": "^4.1.199"
49
56
  },
50
57
  "devDependencies": {
package/sys/child.d.ts CHANGED
File without changes
package/sys/child.js CHANGED
File without changes
package/sys/cmd.d.ts CHANGED
File without changes
package/sys/cmd.js CHANGED
@@ -351,10 +351,11 @@ async function run() {
351
351
  process.exit();
352
352
  }
353
353
  if (cmds[0] === 'build') {
354
- // --- 构建命令:使用 esbuild 打包入口 .tsx 文件为自包含 bundle ---
355
- // --- 只打包「直属于指定目录」的 .tsx,子目录内的组件文件不会被打包 ---
356
- // --- 推荐目录结构:stc/view/app.tsx(入口)+ stc/view/pages/home.tsx(子组件)---
357
- // --- 用法:node ./source/main build [-d www/myapp/stc/view] ---
354
+ // --- 构建命令:使用 esbuild 打包 *.page.tsx 文件为自包含 bundle ---
355
+ // --- 约定:文件名以 .page.tsx 结尾的为页面入口,会被打包;其余 .tsx 均视为组件,不参与打包 ---
356
+ // --- 与 Next.js 的 page.tsx 约定同理,开发者可自由组织目录结构,框架靠文件名区分入口与组件 ---
357
+ // --- 推荐目录结构:stc/view/home.page.tsx(入口)+ stc/view/components/header.tsx(组件)---
358
+ // --- 用法:node ./source/main build [-d www/myapp/stc] ---
358
359
  let targetDir = '';
359
360
  for (let i = 1; i < cmds.length; i++) {
360
361
  if ((cmds[i] === '-d' || cmds[i] === '--dir') && cmds[i + 1]) {
@@ -362,7 +363,7 @@ async function run() {
362
363
  i++;
363
364
  }
364
365
  }
365
- /** --- 扫描目录下直属的 .tsx 文件(非递归),子目录内文件不作为打包入口 --- */
366
+ /** --- 递归扫描目录,收集所有 *.page.tsx 文件作为打包入口 --- */
366
367
  const entryPoints = [];
367
368
  const scanDir = async (dir) => {
368
369
  const items = await lFs.readDir(dir);
@@ -370,13 +371,16 @@ async function run() {
370
371
  if (item.name === '.' || item.name === '..') {
371
372
  continue;
372
373
  }
373
- if (!item.isDirectory() && item.name.endsWith('.tsx')) {
374
+ if (item.isDirectory()) {
375
+ await scanDir(`${dir}/${item.name}`);
376
+ }
377
+ else if (item.name.endsWith('.page.tsx')) {
374
378
  entryPoints.push(`${dir}/${item.name}`);
375
379
  }
376
380
  }
377
381
  };
378
382
  if (targetDir) {
379
- // --- 指定了目录,只扫描该目录下的直属 .tsx ---
383
+ // --- 指定了目录:递归扫描该目录下所有 *.page.tsx ---
380
384
  const absTarget = targetDir.startsWith('/') || /^[A-Za-z]:/.test(targetDir)
381
385
  ? targetDir
382
386
  : kebab.ROOT_CWD + targetDir.replace(/^\//, '');
@@ -390,8 +394,7 @@ async function run() {
390
394
  }
391
395
  }
392
396
  else {
393
- // --- 未指定目录:扫描 www/*/stc/ 及其直接子目录下的 .tsx ---
394
- // --- 即扫描 stc/*.tsx 和 stc/*/*.tsx(如 stc/view/app.tsx),不再深入 ---
397
+ // --- 未指定目录:递归扫描所有站点的 www/*/stc/ ---
395
398
  const wwwItems = await lFs.readDir(kebab.WWW_CWD);
396
399
  for (const wwwItem of wwwItems) {
397
400
  if (wwwItem.name === '.' || wwwItem.name === '..' || !wwwItem.isDirectory()) {
@@ -401,16 +404,7 @@ async function run() {
401
404
  if (!await lFs.isDir(stcPath)) {
402
405
  continue;
403
406
  }
404
- // --- 扫描 stc/ 直属文件 ---
405
407
  await scanDir(stcPath);
406
- // --- 扫描 stc/ 的直接子目录(如 view/),取其顶层 .tsx 作为入口 ---
407
- const stcItems = await lFs.readDir(stcPath);
408
- for (const stcItem of stcItems) {
409
- if (stcItem.name === '.' || stcItem.name === '..' || !stcItem.isDirectory()) {
410
- continue;
411
- }
412
- await scanDir(`${stcPath}/${stcItem.name}`);
413
- }
414
408
  }
415
409
  }
416
410
  if (entryPoints.length === 0) {
@@ -420,17 +414,32 @@ async function run() {
420
414
  }
421
415
  lCore.display('KEBAB', 'BUILD', `Found ${entryPoints.length} file(s).`);
422
416
  const esbuild = await import('esbuild');
417
+ // --- 按 stc/ 根目录分组:同一站点内所有页面合并为一次 esbuild 构建 ---
418
+ // --- 不同站点(不同 stc/)相互隔离,同站点内跨子目录页面共享 chunk ---
419
+ // --- splitting: true 自动将任意共享 import(React/Header/utils 等)提取为独立 chunk ---
420
+ /**
421
+ * --- 查找路径中最后一段名为 stc 的祖先目录 ---
422
+ */
423
+ const getStcRoot = (filePath) => {
424
+ const parts = filePath.split('/');
425
+ const idx = parts.lastIndexOf('stc');
426
+ return idx >= 0 ? parts.slice(0, idx + 1).join('/') : parts.slice(0, -1).join('/');
427
+ };
428
+ const byStc = new Map();
423
429
  for (const entry of entryPoints) {
424
- const outfile = entry.replace(/\.tsx$/, '.bundle.js');
425
- lCore.display('KEBAB', 'BUILD', entry.replace(kebab.ROOT_CWD, ''), '→', outfile.replace(kebab.ROOT_CWD, ''));
426
- try {
427
- /** --- 组件文件名(不含扩展名),用于生成 stdin 入口的 import 语句 --- */
430
+ const stcRoot = getStcRoot(entry);
431
+ if (!byStc.has(stcRoot)) {
432
+ byStc.set(stcRoot, []);
433
+ }
434
+ byStc.get(stcRoot).push(entry);
435
+ }
436
+ for (const [stcRoot, entries] of byStc) {
437
+ // --- 为每个页面写入水合入口临时文件(实际文件,splitting 不支持 stdin 多入口)---
438
+ const tempFiles = [];
439
+ for (const entry of entries) {
428
440
  const basename = entry.split('/').pop().replace(/\.tsx$/, '');
429
- /** --- 组件所在目录,esbuild 以此为基准解析相对 import --- */
430
- const resolveDir = entry.substring(0, entry.lastIndexOf('/'));
431
- // --- stdin 入口:含水合逻辑,_routerBase 在 props 中时自动包裹 BrowserRouter ---
432
- // --- React/react-dom/react-router-dom 全部打入 bundle,浏览器只加载一个 JS ---
433
- const stdinContent = [
441
+ const entryDir = entry.substring(0, entry.lastIndexOf('/'));
442
+ const hydrateCode = [
434
443
  `import{hydrateRoot}from'react-dom/client';`,
435
444
  `import{createElement}from'react';`,
436
445
  `import{BrowserRouter}from'react-router-dom';`,
@@ -445,31 +454,52 @@ async function run() {
445
454
  `}`,
446
455
  `}`,
447
456
  ].join('');
457
+ const tempFile = `${entryDir}/${basename}.hydrate.tsx`;
458
+ await lFs.putContent(tempFile, hydrateCode);
459
+ tempFiles.push(tempFile);
460
+ lCore.display('KEBAB', 'BUILD', entry.replace(kebab.ROOT_CWD, ''), '→', entry.replace(kebab.ROOT_CWD, '').replace(/\.tsx$/, '.bundle.js'));
461
+ }
462
+ // --- JS 构建:整站一次构建,splitting 按实际共享情况自动提取 chunk ---
463
+ // --- outbase = outdir = stcRoot,入口保留子目录结构,chunk 落在 stcRoot 根 ---
464
+ try {
448
465
  await esbuild.build({
449
- 'stdin': {
450
- 'contents': stdinContent,
451
- 'resolveDir': resolveDir,
452
- 'sourcefile': basename + '.hydrate.tsx',
453
- 'loader': 'tsx',
454
- },
466
+ 'entryPoints': tempFiles,
455
467
  'bundle': true,
456
- // --- 无 external:React 全部打入 bundle,生成自包含文件,浏览器只加载一个 JS ---
468
+ 'splitting': true,
457
469
  'format': 'esm',
458
470
  'jsx': 'automatic',
459
471
  'jsxImportSource': 'react',
460
472
  'platform': 'browser',
461
473
  'target': 'es2022',
462
474
  'minify': true,
463
- 'outfile': outfile,
475
+ 'outbase': stcRoot,
476
+ 'outdir': stcRoot,
477
+ 'entryNames': '[dir]/[name]',
478
+ 'chunkNames': 'chunk-[hash]',
464
479
  });
465
- lCore.display('KEBAB', 'BUILD', 'JS', outfile.replace(kebab.ROOT_CWD, ''));
466
- // --- 构建 Tailwind CSS:生成同名 .css 文件 ---
480
+ // --- *.hydrate.js 重命名为 *.bundle.js ---
481
+ for (const tempFile of tempFiles) {
482
+ const hydrateJs = tempFile.replace(/\.tsx$/, '.js');
483
+ const bundleJs = hydrateJs.replace(/\.hydrate\.js$/, '.bundle.js');
484
+ await lFs.rename(hydrateJs, bundleJs);
485
+ lCore.display('KEBAB', 'BUILD', 'JS', bundleJs.replace(kebab.ROOT_CWD, ''));
486
+ }
487
+ }
488
+ catch (e) {
489
+ lCore.display('KEBAB', 'BUILD', 'JS FAILED', stcRoot.replace(kebab.ROOT_CWD, ''), e.message ?? '');
490
+ }
491
+ finally {
492
+ // --- 清理临时水合入口文件 ---
493
+ for (const tempFile of tempFiles) {
494
+ await lFs.unlink(tempFile);
495
+ }
496
+ }
497
+ // --- CSS:每个入口单独构建 Tailwind ---
498
+ for (const entry of entries) {
467
499
  const cssOut = entry.replace(/\.tsx$/, '.css');
468
- /** --- 临时 CSS 输入文件,@source 扫描当前目录及子目录所有 tsx 文件 --- */
469
500
  const tmpCss = entry + '.__tw__.css';
470
501
  await lFs.putContent(tmpCss, `@import "tailwindcss";\n@source "./**/*.tsx";\n`);
471
502
  try {
472
- /** --- 直接用当前 Node.js 执行 CLI 的 JS 入口,跨平台无需 shell --- */
473
503
  const twJs = fileURLToPath(new URL('../node_modules/@tailwindcss/cli/dist/index.mjs', import.meta.url));
474
504
  await new Promise((resolve, reject) => {
475
505
  const proc = childProcess.spawn(process.execPath, [twJs, '-i', tmpCss, '-o', cssOut, '--minify'], { 'shell': false });
@@ -493,9 +523,6 @@ async function run() {
493
523
  await lFs.unlink(tmpCss);
494
524
  }
495
525
  }
496
- catch (e) {
497
- lCore.display('KEBAB', 'BUILD', 'FAILED', entry.replace(kebab.ROOT_CWD, ''), e.message ?? '');
498
- }
499
526
  }
500
527
  lCore.display('DONE');
501
528
  process.exit();
package/sys/ctr.d.ts CHANGED
@@ -185,6 +185,14 @@ export declare class Ctr {
185
185
  */
186
186
  'staticPath'?: string;
187
187
  }): Promise<string>;
188
+ /**
189
+ * --- 递归扫描 JS 文件中的 import 语句,收集第三方 bare specifier ---
190
+ * @param filePath 当前要扫描的文件绝对路径
191
+ * @param scannedFiles 已扫描文件集(去重用)
192
+ * @param extraImports 收集到的第三方包名集合
193
+ * @param builtinImports 内置 import map,已有条目不重复添加
194
+ */
195
+ private _scanImports;
188
196
  /**
189
197
  * --- 设置校验错误返回值 ---
190
198
  * @param rtn 返回值数组