@playcraft/cli 0.0.53 → 0.0.56

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 (242) hide show
  1. package/README.md +21 -0
  2. package/REMIX.md +103 -19
  3. package/dist/atom-tree/fusion-hints.js +8 -3
  4. package/dist/atom-tree/fusion-intent.js +62 -0
  5. package/dist/atom-tree/selection.js +3 -1
  6. package/dist/commands/create.js +439 -159
  7. package/dist/commands/image.js +79 -69
  8. package/dist/commands/levels.js +29 -0
  9. package/dist/commands/patterns.js +75 -0
  10. package/dist/commands/remix.js +47 -0
  11. package/dist/commands/skills.js +48 -17
  12. package/dist/index.js +13 -6
  13. package/dist/remix/clone-source.js +208 -0
  14. package/dist/remix/clone.js +255 -0
  15. package/dist/scaffold/templates.js +35 -0
  16. package/dist/utils/agent-api-client.js +10 -3
  17. package/dist/utils/remix-image-attachments.js +4 -32
  18. package/dist/utils/sandbox-path.js +20 -0
  19. package/package.json +3 -3
  20. package/project-template/.claude/agents/creative-director.md +114 -0
  21. package/project-template/.claude/agents/developer.md +5 -2
  22. package/project-template/.claude/agents/game-designer.md +41 -20
  23. package/project-template/.claude/agents/refs/README.md +8 -0
  24. package/project-template/.claude/agents/refs/creative-director-guide.md +35 -0
  25. package/project-template/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  26. package/project-template/.claude/agents/refs/creative-discovery-guide.md +76 -0
  27. package/project-template/.claude/agents/refs/creative-north-star-examples.md +82 -0
  28. package/project-template/.claude/agents/refs/developer-dev-handoff.md +1 -1
  29. package/project-template/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  30. package/project-template/.claude/agents/refs/game-designer-gameplay-sufficiency.md +4 -4
  31. package/project-template/.claude/agents/reviewer.md +11 -0
  32. package/project-template/.claude/hooks/README.md +12 -12
  33. package/project-template/.claude/hooks/block-developer-preflight.mjs +49 -13
  34. package/project-template/.claude/hooks/block-ring1-tasklist.mjs +1 -1
  35. package/project-template/.claude/hooks/user-prompt.mjs +3 -13
  36. package/project-template/.claude/hooks/validate-workflow-stop.mjs +209 -12
  37. package/project-template/.claude/settings.json +2 -2
  38. package/project-template/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  39. package/project-template/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -1
  40. package/project-template/.claude/snapshot.mjs +0 -1
  41. package/project-template/.codebuddy/settings.json +1 -1
  42. package/project-template/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  43. package/project-template/CLAUDE.md +9 -9
  44. package/project-template/docs/project-state.md +1 -1
  45. package/project-template/docs/team/agent-conduct.md +18 -18
  46. package/project-template/docs/team/collaboration.md +19 -7
  47. package/project-template/docs/team/workflow-changelog.md +15 -0
  48. package/project-template/fusion-patterns/catalog.json +77 -0
  49. package/project-template/package.json +2 -0
  50. package/project-template/skills/_shared/scripts/create-playable.mjs +31 -11
  51. package/project-template/skills/_shared/scripts/dev-start.mjs +3 -3
  52. package/project-template/skills/_shared/scripts/disclosure-config.mjs +65 -39
  53. package/project-template/skills/_shared/scripts/harness-smoke.mjs +3 -1
  54. package/project-template/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  55. package/project-template/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  56. package/project-template/skills/_shared/scripts/slice-set.mjs +29 -2
  57. package/project-template/skills/_shared/scripts/state-get.mjs +124 -7
  58. package/project-template/skills/_shared/scripts/state-store.mjs +25 -2
  59. package/project-template/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  60. package/project-template/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  61. package/project-template/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  62. package/project-template/skills/playable-record/scripts/record-playable.mjs +2 -1
  63. package/project-template/skills/playable-record/scripts/verify-contract.mjs +4 -4
  64. package/project-template/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  65. package/project-template/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  66. package/project-template/skills/playable-record/scripts/verify-slice.mjs +2 -2
  67. package/project-template/templates/design-brief.template.md +34 -0
  68. package/project-template/templates/design-methodology.md +1 -2
  69. package/project-template/templates/design.template.md +65 -0
  70. package/project-template/templates/developer-log.template.md +12 -1
  71. package/project-template/templates/five-axis-framework.md +12 -0
  72. package/project-template/templates/game-designer-creative-process.template.md +99 -0
  73. package/project-template/templates/level-sketch.template.json +56 -0
  74. package/project-template/templates/north-star.template.md +43 -0
  75. package/project-template/templates/project-state.template.md +1 -1
  76. package/project-template/vite.config.ts +4 -5
  77. package/project-templates/phaser/.claude/agents/creative-director.md +114 -0
  78. package/project-templates/phaser/.claude/agents/game-designer.md +41 -20
  79. package/project-templates/phaser/.claude/agents/refs/creative-director-guide.md +35 -0
  80. package/project-templates/phaser/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  81. package/project-templates/phaser/.claude/agents/refs/creative-discovery-guide.md +76 -0
  82. package/project-templates/phaser/.claude/agents/refs/creative-north-star-examples.md +82 -0
  83. package/project-templates/phaser/.claude/agents/refs/developer-dev-handoff.md +1 -1
  84. package/project-templates/phaser/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  85. package/project-templates/phaser/.claude/agents/reviewer.md +11 -0
  86. package/project-templates/phaser/.claude/hooks/block-ring1-tasklist.mjs +2 -2
  87. package/project-templates/phaser/.claude/hooks/user-prompt.mjs +17 -18
  88. package/project-templates/phaser/.claude/hooks/validate-workflow-stop.mjs +209 -12
  89. package/project-templates/phaser/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  90. package/project-templates/phaser/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  91. package/project-templates/phaser/CLAUDE.md +88 -54
  92. package/project-templates/phaser/docs/DESIGN.md +25 -0
  93. package/project-templates/phaser/docs/project-state.md +1 -1
  94. package/project-templates/phaser/docs/team/collaboration.md +19 -7
  95. package/project-templates/phaser/docs/team/workflow-changelog.md +14 -0
  96. package/project-templates/phaser/fusion-patterns/catalog.json +77 -0
  97. package/project-templates/phaser/package.json +3 -1
  98. package/project-templates/phaser/skills/_shared/scripts/dev-start.mjs +3 -3
  99. package/project-templates/phaser/skills/_shared/scripts/disclosure-config.mjs +65 -39
  100. package/project-templates/phaser/skills/_shared/scripts/harness-smoke.mjs +3 -1
  101. package/project-templates/phaser/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  102. package/project-templates/phaser/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  103. package/project-templates/phaser/skills/_shared/scripts/state-get.mjs +124 -7
  104. package/project-templates/phaser/skills/_shared/scripts/state-store.mjs +25 -2
  105. package/project-templates/phaser/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  106. package/project-templates/phaser/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  107. package/project-templates/phaser/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  108. package/project-templates/phaser/skills/playable-record/scripts/record-playable.mjs +2 -1
  109. package/project-templates/phaser/skills/playable-record/scripts/verify-contract.mjs +4 -4
  110. package/project-templates/phaser/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  111. package/project-templates/phaser/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  112. package/project-templates/phaser/skills/playable-record/scripts/verify-slice.mjs +2 -2
  113. package/project-templates/phaser/templates/design-brief.template.md +42 -11
  114. package/project-templates/phaser/templates/design-methodology.md +1 -2
  115. package/project-templates/phaser/templates/design.template.md +77 -0
  116. package/project-templates/phaser/templates/five-axis-framework.md +50 -1
  117. package/project-templates/phaser/templates/game-designer-creative-process.template.md +99 -0
  118. package/project-templates/phaser/templates/level-sketch.template.json +56 -0
  119. package/project-templates/phaser/templates/north-star.template.md +43 -0
  120. package/project-templates/phaser/templates/project-state.template.md +1 -1
  121. package/project-templates/phaser/vite.config.ts +4 -5
  122. package/project-templates/playable-template-phaser/.playcraftignore +10 -0
  123. package/project-templates/playable-template-phaser/CLAUDE.md +178 -0
  124. package/project-templates/playable-template-phaser/PLAYCRAFT-MIGRATION.md +328 -0
  125. package/project-templates/playable-template-phaser/README.md +381 -0
  126. package/project-templates/playable-template-phaser/assets/brain.png +0 -0
  127. package/project-templates/playable-template-phaser/assets/button.png +0 -0
  128. package/project-templates/playable-template-phaser/assets/interact.png +0 -0
  129. package/project-templates/playable-template-phaser/assets/logo.webp +0 -0
  130. package/project-templates/playable-template-phaser/assets/warn_brain.png +0 -0
  131. package/project-templates/playable-template-phaser/builds.config.js +12 -0
  132. package/project-templates/playable-template-phaser/globals.d.ts +2 -0
  133. package/project-templates/playable-template-phaser/index.html +12 -0
  134. package/project-templates/playable-template-phaser/package.json +45 -0
  135. package/project-templates/playable-template-phaser/scripts/base64-to-mp3.js +340 -0
  136. package/project-templates/playable-template-phaser/scripts/convert-to-webp.js +370 -0
  137. package/project-templates/playable-template-phaser/src/Game.ts +154 -0
  138. package/project-templates/playable-template-phaser/src/config/LoadingConfig.ts +40 -0
  139. package/project-templates/playable-template-phaser/src/config/ThemeConfig.ts +81 -0
  140. package/project-templates/playable-template-phaser/src/index.css +18 -0
  141. package/project-templates/playable-template-phaser/src/index.html +9 -0
  142. package/project-templates/playable-template-phaser/src/index.ts +30 -0
  143. package/project-templates/playable-template-phaser/src/json5.d.ts +4 -0
  144. package/project-templates/playable-template-phaser/src/managers/AudioManager.ts +559 -0
  145. package/project-templates/playable-template-phaser/src/scenes/MainScene.ts +301 -0
  146. package/project-templates/playable-template-phaser/src/scenes/PreloadScene.ts +256 -0
  147. package/project-templates/playable-template-phaser/src/theme/README.md +194 -0
  148. package/project-templates/playable-template-phaser/src/theme/blue-theme/assets/.gitkeep +24 -0
  149. package/project-templates/playable-template-phaser/src/theme/blue-theme/index.ts +27 -0
  150. package/project-templates/playable-template-phaser/src/theme/blue-theme/theme.data.json5 +19 -0
  151. package/project-templates/playable-template-phaser/src/theme/green-theme/assets/.gitkeep +3 -0
  152. package/project-templates/playable-template-phaser/src/theme/green-theme/index.ts +20 -0
  153. package/project-templates/playable-template-phaser/src/theme/green-theme/theme.data.json5 +19 -0
  154. package/project-templates/playable-template-phaser/src/theme/index.ts +1 -0
  155. package/project-templates/playable-template-phaser/src/theme/red-theme/assets/.gitkeep +3 -0
  156. package/project-templates/playable-template-phaser/src/theme/red-theme/index.ts +20 -0
  157. package/project-templates/playable-template-phaser/src/theme/red-theme/theme.data.json5 +19 -0
  158. package/project-templates/playable-template-phaser/src/theme/theme.metadata.md +60 -0
  159. package/project-templates/playable-template-phaser/src/theme/theme.schema.json5 +149 -0
  160. package/project-templates/playable-template-phaser/src/ui/BrainAgeUI.ts +681 -0
  161. package/project-templates/playable-template-phaser/src/ui/CountdownUI.ts +565 -0
  162. package/project-templates/playable-template-phaser/src/ui/PlayButton.ts +51 -0
  163. package/project-templates/playable-template-phaser/src/ui/ScoreUI.ts +207 -0
  164. package/project-templates/playable-template-phaser/src/ui/SuccessUI.ts +43 -0
  165. package/project-templates/playable-template-phaser/src/ui/index.ts +15 -0
  166. package/project-templates/playable-template-phaser/tsconfig.json +26 -0
  167. package/project-templates/playable-template-phaser/vite.config.ts +64 -0
  168. package/project-templates/playable-template-phaser//346/226/260/346/211/213/345/274/200/345/217/221/346/214/207/345/215/227.md +209 -0
  169. package/project-templates/playable-template-three/README.md +795 -0
  170. package/project-templates/playable-template-three/assets/Stork.glb +0 -0
  171. package/project-templates/playable-template-three/assets/button.png +0 -0
  172. package/project-templates/playable-template-three/assets/logo.webp +0 -0
  173. package/project-templates/playable-template-three/build.json +7 -0
  174. package/project-templates/playable-template-three/builds.config.example.js +151 -0
  175. package/project-templates/playable-template-three/builds.config.js +158 -0
  176. package/project-templates/playable-template-three/package.json +36 -0
  177. package/project-templates/playable-template-three/scripts/convert-to-webp.js +370 -0
  178. package/project-templates/playable-template-three/scripts/theme-builds.js +514 -0
  179. package/project-templates/playable-template-three/src/Game.ts +215 -0
  180. package/project-templates/playable-template-three/src/config/LoadingConfig.ts +27 -0
  181. package/project-templates/playable-template-three/src/config/ThemeConfig.ts +36 -0
  182. package/project-templates/playable-template-three/src/global.d.ts +108 -0
  183. package/project-templates/playable-template-three/src/index.css +18 -0
  184. package/project-templates/playable-template-three/src/index.html +9 -0
  185. package/project-templates/playable-template-three/src/index.ts +20 -0
  186. package/project-templates/playable-template-three/src/managers/AudioManager.ts +538 -0
  187. package/project-templates/playable-template-three/src/scenes/PreloadScene.ts +305 -0
  188. package/project-templates/playable-template-three/src/theme/README.md +225 -0
  189. package/project-templates/playable-template-three/src/theme/blue-theme/assets/.gitkeep +28 -0
  190. package/project-templates/playable-template-three/src/theme/blue-theme/index.ts +45 -0
  191. package/project-templates/playable-template-three/src/theme/current.ts +12 -0
  192. package/project-templates/playable-template-three/src/theme/green-theme/assets/.gitkeep +3 -0
  193. package/project-templates/playable-template-three/src/theme/green-theme/index.ts +34 -0
  194. package/project-templates/playable-template-three/src/theme/red-theme/assets/.gitkeep +3 -0
  195. package/project-templates/playable-template-three/src/theme/red-theme/index.ts +34 -0
  196. package/project-templates/playable-template-three/tsconfig.json +24 -0
  197. package/project-templates/threejs/.claude/agents/creative-director.md +114 -0
  198. package/project-templates/threejs/.claude/agents/game-designer.md +41 -20
  199. package/project-templates/threejs/.claude/agents/refs/creative-director-guide.md +35 -0
  200. package/project-templates/threejs/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  201. package/project-templates/threejs/.claude/agents/refs/creative-discovery-guide.md +76 -0
  202. package/project-templates/threejs/.claude/agents/refs/creative-north-star-examples.md +82 -0
  203. package/project-templates/threejs/.claude/agents/refs/developer-dev-handoff.md +1 -1
  204. package/project-templates/threejs/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  205. package/project-templates/threejs/.claude/agents/reviewer.md +11 -0
  206. package/project-templates/threejs/.claude/hooks/block-ring1-tasklist.mjs +2 -2
  207. package/project-templates/threejs/.claude/hooks/user-prompt.mjs +17 -18
  208. package/project-templates/threejs/.claude/hooks/validate-workflow-stop.mjs +209 -12
  209. package/project-templates/threejs/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  210. package/project-templates/threejs/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  211. package/project-templates/threejs/CLAUDE.md +88 -54
  212. package/project-templates/threejs/docs/DESIGN.md +25 -0
  213. package/project-templates/threejs/docs/project-state.md +1 -1
  214. package/project-templates/threejs/docs/team/collaboration.md +19 -7
  215. package/project-templates/threejs/docs/team/workflow-changelog.md +14 -0
  216. package/project-templates/threejs/fusion-patterns/catalog.json +77 -0
  217. package/project-templates/threejs/package.json +3 -1
  218. package/project-templates/threejs/skills/_shared/scripts/dev-start.mjs +3 -3
  219. package/project-templates/threejs/skills/_shared/scripts/disclosure-config.mjs +65 -39
  220. package/project-templates/threejs/skills/_shared/scripts/harness-smoke.mjs +3 -1
  221. package/project-templates/threejs/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  222. package/project-templates/threejs/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  223. package/project-templates/threejs/skills/_shared/scripts/state-get.mjs +124 -7
  224. package/project-templates/threejs/skills/_shared/scripts/state-store.mjs +25 -2
  225. package/project-templates/threejs/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  226. package/project-templates/threejs/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  227. package/project-templates/threejs/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  228. package/project-templates/threejs/skills/playable-record/scripts/record-playable.mjs +2 -1
  229. package/project-templates/threejs/skills/playable-record/scripts/verify-contract.mjs +4 -4
  230. package/project-templates/threejs/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  231. package/project-templates/threejs/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  232. package/project-templates/threejs/skills/playable-record/scripts/verify-slice.mjs +2 -2
  233. package/project-templates/threejs/templates/design-brief.template.md +42 -11
  234. package/project-templates/threejs/templates/design-methodology.md +1 -2
  235. package/project-templates/threejs/templates/design.template.md +77 -0
  236. package/project-templates/threejs/templates/five-axis-framework.md +50 -1
  237. package/project-templates/threejs/templates/game-designer-creative-process.template.md +99 -0
  238. package/project-templates/threejs/templates/level-sketch.template.json +56 -0
  239. package/project-templates/threejs/templates/north-star.template.md +43 -0
  240. package/project-templates/threejs/templates/project-state.template.md +1 -1
  241. package/project-templates/threejs/vite.config.ts +4 -5
  242. package/project-template/docs/DESIGN.md +0 -65
@@ -0,0 +1,514 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * 批量构建脚本 - Three.js Template
5
+ *
6
+ * 功能:
7
+ * - 支持多主题自动切换构建(可选)
8
+ * - 支持多渠道批量构建
9
+ * - 自动生成规范文件名
10
+ * - 灵活的配置系统
11
+ *
12
+ * 使用方法:
13
+ * npm run build:all # 构建所有(使用默认配置)
14
+ * npm run builds # 同上(别名)
15
+ * npm run builds -- -t "theme1,theme2" # 构建指定主题
16
+ * npm run builds -- -c "google,facebook" # 构建指定渠道
17
+ * npm run builds -- -t "theme1" -c "google" # 构建指定主题和渠道
18
+ *
19
+ * 配置文件(可选):
20
+ * 在项目根目录创建 builds.config.js 来自定义配置
21
+ * 如果不存在配置文件,将使用默认配置(无主题切换,多渠道构建)
22
+ */
23
+
24
+ const { execSync } = require('child_process');
25
+ const fs = require('fs');
26
+ const path = require('path');
27
+
28
+ // ==================== 默认配置 ====================
29
+
30
+ const DEFAULT_CONFIG = {
31
+ // 项目基本信息
32
+ projectName: 'Three Game',
33
+ projectCode: '0001',
34
+
35
+ // 渠道列表
36
+ channels: [
37
+ 'google',
38
+ 'facebook',
39
+ 'tiktok',
40
+ 'applovin',
41
+ 'ironSource',
42
+ 'unity',
43
+ 'moloco',
44
+ 'liftoff',
45
+ 'bigoads'
46
+ ],
47
+
48
+ // 主题配置
49
+ themes: {
50
+ enabled: false, // 是否启用主题切换
51
+ sourceDir: 'src/theme', // 主题目录
52
+ entryFile: 'src/index.ts', // 入口文件
53
+ whitelist: null, // 主题白名单函数(返回允许的主题列表)
54
+ },
55
+
56
+ // 文件命名配置
57
+ naming: {
58
+ language: 'EN',
59
+ format: '无',
60
+ supplier: 'AIX',
61
+ follower: 'ZQL',
62
+ // 自定义文件名生成函数
63
+ // generator: (themeName, channel, date, month) => { ... }
64
+ },
65
+
66
+ // 构建配置
67
+ build: {
68
+ outputDir: 'dist-all', // 批量构建输出目录
69
+ tempDir: 'dist', // 单次构建临时目录
70
+ continueOnError: true, // 单个渠道失败后是否继续
71
+ cleanTemp: true, // 构建后是否清理临时目录
72
+ }
73
+ };
74
+
75
+ // ==================== 工具函数 ====================
76
+
77
+ let isExiting = false;
78
+ let backupContent = null;
79
+ let backupPath = null;
80
+
81
+ function log(message, level = 'info') {
82
+ const timestamp = new Date().toLocaleTimeString();
83
+ const colors = {
84
+ info: '\x1b[36m', // cyan
85
+ success: '\x1b[32m', // green
86
+ error: '\x1b[31m', // red
87
+ warn: '\x1b[33m', // yellow
88
+ debug: '\x1b[90m' // gray
89
+ };
90
+ const reset = '\x1b[0m';
91
+ const prefix = {
92
+ info: '📦',
93
+ success: '✅',
94
+ error: '❌',
95
+ warn: '⚠️',
96
+ debug: '🔍'
97
+ }[level] || 'ℹ️';
98
+
99
+ console.log(`${colors[level]}[${timestamp}] ${prefix} ${message}${reset}`);
100
+ }
101
+
102
+ function getCurrentMonth() {
103
+ const month = new Date().getMonth() + 1;
104
+ return `${month}月`;
105
+ }
106
+
107
+ function getCurrentDate() {
108
+ const date = new Date();
109
+ const year = date.getFullYear();
110
+ const month = String(date.getMonth() + 1).padStart(2, '0');
111
+ const day = String(date.getDate()).padStart(2, '0');
112
+ return `${year}${month}${day}`;
113
+ }
114
+
115
+ /**
116
+ * 默认文件名生成器
117
+ */
118
+ function defaultFileNameGenerator(config, themeName, channel) {
119
+ const parts = [
120
+ themeName,
121
+ config.naming.language,
122
+ config.naming.format,
123
+ getCurrentMonth(),
124
+ getCurrentDate(),
125
+ config.naming.supplier,
126
+ config.naming.follower,
127
+ channel.toLowerCase()
128
+ ];
129
+
130
+ return parts.join('-');
131
+ }
132
+
133
+ /**
134
+ * 获取所有主题
135
+ */
136
+ function getAllThemes(config) {
137
+ const themesDir = path.join(PROJECT_ROOT, config.themes.sourceDir);
138
+
139
+ if (!fs.existsSync(themesDir)) {
140
+ log(`主题目录不存在: ${themesDir}`, 'warn');
141
+ return [];
142
+ }
143
+
144
+ const entries = fs.readdirSync(themesDir, { withFileTypes: true });
145
+ let themes = entries
146
+ .filter(entry => entry.isDirectory())
147
+ .map(entry => entry.name)
148
+ .sort();
149
+
150
+ // 应用白名单过滤
151
+ if (config.themes.whitelist && typeof config.themes.whitelist === 'function') {
152
+ themes = themes.filter(config.themes.whitelist);
153
+ log(`应用主题白名单过滤: ${themes.length} 个主题`, 'debug');
154
+ }
155
+
156
+ return themes;
157
+ }
158
+
159
+ /**
160
+ * 备份入口文件
161
+ */
162
+ function backupEntryFile(config) {
163
+ const entryPath = path.join(PROJECT_ROOT, config.themes.entryFile);
164
+
165
+ if (!fs.existsSync(entryPath)) {
166
+ throw new Error(`入口文件不存在: ${entryPath}`);
167
+ }
168
+
169
+ backupPath = entryPath;
170
+ backupContent = fs.readFileSync(entryPath, 'utf8');
171
+ log(`已备份入口文件: ${config.themes.entryFile}`, 'debug');
172
+ }
173
+
174
+ /**
175
+ * 恢复入口文件
176
+ */
177
+ function restoreEntryFile() {
178
+ if (backupPath && backupContent) {
179
+ fs.writeFileSync(backupPath, backupContent, 'utf8');
180
+ log(`已恢复入口文件`, 'debug');
181
+ backupPath = null;
182
+ backupContent = null;
183
+ }
184
+ }
185
+
186
+ /**
187
+ * 切换主题
188
+ */
189
+ function switchTheme(config, themeName) {
190
+ const entryPath = path.join(PROJECT_ROOT, config.themes.entryFile);
191
+ const themePath = `./${config.themes.sourceDir}/${themeName}`;
192
+
193
+ const content = `export { default } from '${themePath}';\n`;
194
+ fs.writeFileSync(entryPath, content, 'utf8');
195
+
196
+ log(`已切换主题: ${themeName}`, 'debug');
197
+ }
198
+
199
+ /**
200
+ * 构建单个渠道
201
+ */
202
+ function buildChannel(config, themeName, channel, outputDir) {
203
+ log(`开始构建: 主题=${themeName}, 渠道=${channel}`);
204
+
205
+ try {
206
+ // 生成文件名
207
+ const fileName = config.naming.generator
208
+ ? config.naming.generator(themeName, channel, getCurrentDate(), getCurrentMonth())
209
+ : defaultFileNameGenerator(config, themeName, channel);
210
+
211
+ log(`文件名: ${fileName}`, 'debug');
212
+
213
+ // 更新 build.json
214
+ const buildJsonPath = path.join(PROJECT_ROOT, 'build.json');
215
+ const buildConfig = JSON.parse(fs.readFileSync(buildJsonPath, 'utf8'));
216
+ const originalFileName = buildConfig.filename;
217
+ buildConfig.filename = fileName;
218
+ fs.writeFileSync(buildJsonPath, JSON.stringify(buildConfig, null, 2));
219
+
220
+ // 执行构建
221
+ execSync(`npm run build -- ${channel}`, {
222
+ cwd: PROJECT_ROOT,
223
+ stdio: 'inherit'
224
+ });
225
+
226
+ // 恢复 build.json
227
+ buildConfig.filename = originalFileName;
228
+ fs.writeFileSync(buildJsonPath, JSON.stringify(buildConfig, null, 2));
229
+
230
+ // 移动构建产物
231
+ const tempDistDir = path.join(PROJECT_ROOT, config.build.tempDir);
232
+ const targetDir = path.join(outputDir, themeName, channel);
233
+
234
+ if (fs.existsSync(tempDistDir)) {
235
+ if (!fs.existsSync(targetDir)) {
236
+ fs.mkdirSync(targetDir, { recursive: true });
237
+ }
238
+
239
+ const files = fs.readdirSync(tempDistDir);
240
+ files.forEach(file => {
241
+ const srcPath = path.join(tempDistDir, file);
242
+ const destPath = path.join(targetDir, file);
243
+
244
+ if (fs.statSync(srcPath).isDirectory()) {
245
+ copyDirRecursive(srcPath, destPath);
246
+ } else {
247
+ fs.copyFileSync(srcPath, destPath);
248
+ }
249
+ });
250
+
251
+ log(`✓ 构建成功: ${themeName}/${channel}`, 'success');
252
+ }
253
+ } catch (error) {
254
+ log(`构建失败: ${themeName}/${channel} - ${error.message}`, 'error');
255
+
256
+ if (!config.build.continueOnError) {
257
+ throw error;
258
+ }
259
+ }
260
+ }
261
+
262
+ /**
263
+ * 递归复制目录
264
+ */
265
+ function copyDirRecursive(src, dest) {
266
+ if (!fs.existsSync(dest)) {
267
+ fs.mkdirSync(dest, { recursive: true });
268
+ }
269
+
270
+ const entries = fs.readdirSync(src, { withFileTypes: true });
271
+
272
+ for (const entry of entries) {
273
+ const srcPath = path.join(src, entry.name);
274
+ const destPath = path.join(dest, entry.name);
275
+
276
+ if (entry.isDirectory()) {
277
+ copyDirRecursive(srcPath, destPath);
278
+ } else {
279
+ fs.copyFileSync(srcPath, destPath);
280
+ }
281
+ }
282
+ }
283
+
284
+ /**
285
+ * 清理临时目录
286
+ */
287
+ function cleanTempDir(config) {
288
+ const tempDir = path.join(PROJECT_ROOT, config.build.tempDir);
289
+ if (fs.existsSync(tempDir)) {
290
+ fs.rmSync(tempDir, { recursive: true, force: true });
291
+ }
292
+ }
293
+
294
+ /**
295
+ * 解析命令行参数
296
+ */
297
+ function parseArgs() {
298
+ const args = process.argv.slice(2);
299
+ const result = {
300
+ themes: null,
301
+ channels: null
302
+ };
303
+
304
+ for (let i = 0; i < args.length; i++) {
305
+ if (args[i] === '-t' || args[i] === '--themes') {
306
+ result.themes = args[i + 1]?.split(',').map(t => t.trim()).filter(Boolean);
307
+ i++;
308
+ } else if (args[i] === '-c' || args[i] === '--channels') {
309
+ result.channels = args[i + 1]?.split(',').map(c => c.trim()).filter(Boolean);
310
+ i++;
311
+ }
312
+ }
313
+
314
+ return result;
315
+ }
316
+
317
+ /**
318
+ * 加载配置文件
319
+ */
320
+ function loadConfig() {
321
+ const configPath = path.join(PROJECT_ROOT, 'builds.config.js');
322
+
323
+ if (fs.existsSync(configPath)) {
324
+ log('加载配置文件: builds.config.js', 'debug');
325
+ const userConfig = require(configPath);
326
+ return mergeConfig(DEFAULT_CONFIG, userConfig);
327
+ }
328
+
329
+ log('使用默认配置', 'debug');
330
+ return { ...DEFAULT_CONFIG };
331
+ }
332
+
333
+ /**
334
+ * 深度合并配置
335
+ */
336
+ function mergeConfig(defaultConfig, userConfig) {
337
+ const result = { ...defaultConfig };
338
+
339
+ for (const key in userConfig) {
340
+ if (typeof userConfig[key] === 'object' && !Array.isArray(userConfig[key])) {
341
+ result[key] = { ...defaultConfig[key], ...userConfig[key] };
342
+ } else {
343
+ result[key] = userConfig[key];
344
+ }
345
+ }
346
+
347
+ return result;
348
+ }
349
+
350
+ /**
351
+ * 打印配置摘要
352
+ */
353
+ function printSummary(config, themes, channels) {
354
+ const totalBuilds = themes.length * channels.length;
355
+
356
+ log('========================================');
357
+ log('开始批量构建任务');
358
+ log(`项目: ${config.projectName}`);
359
+ log(`主题数: ${themes.length}`);
360
+ log(`渠道数: ${channels.length}`);
361
+ log(`总构建数: ${totalBuilds}`);
362
+ log('========================================\n');
363
+
364
+ if (themes.length <= 10) {
365
+ log(`主题列表: ${themes.join(', ')}`, 'debug');
366
+ }
367
+ log(`渠道列表: ${channels.join(', ')}`, 'debug');
368
+ log('');
369
+ }
370
+
371
+ // ==================== 主函数 ====================
372
+
373
+ const PROJECT_ROOT = path.resolve(__dirname, '..');
374
+
375
+ async function main() {
376
+ const startTime = Date.now();
377
+
378
+ try {
379
+ // 加载配置
380
+ const config = loadConfig();
381
+
382
+ // 解析命令行参数
383
+ const args = parseArgs();
384
+
385
+ // 确定主题列表
386
+ let themes;
387
+ if (!config.themes.enabled) {
388
+ // 不启用主题切换,使用项目名称作为单一"主题"
389
+ themes = [config.projectName.replace(/\s+/g, '-')];
390
+ } else if (args.themes) {
391
+ themes = args.themes;
392
+ log(`使用命令行指定的主题: ${themes.join(', ')}`, 'info');
393
+ } else {
394
+ themes = getAllThemes(config);
395
+ if (themes.length === 0) {
396
+ throw new Error('未找到任何主题');
397
+ }
398
+ }
399
+
400
+ // 确定渠道列表
401
+ const channels = args.channels || config.channels;
402
+
403
+ // 打印摘要
404
+ printSummary(config, themes, channels);
405
+
406
+ // 备份入口文件(如果启用主题切换)
407
+ if (config.themes.enabled) {
408
+ backupEntryFile(config);
409
+ }
410
+
411
+ // 批量构建
412
+ const outputDir = path.join(PROJECT_ROOT, config.build.outputDir);
413
+ let completedBuilds = 0;
414
+ const totalBuilds = themes.length * channels.length;
415
+ const failedBuilds = [];
416
+
417
+ for (const theme of themes) {
418
+ // 切换主题
419
+ if (config.themes.enabled) {
420
+ switchTheme(config, theme);
421
+ }
422
+
423
+ for (const channel of channels) {
424
+ try {
425
+ // 清理临时目录
426
+ if (config.build.cleanTemp) {
427
+ cleanTempDir(config);
428
+ }
429
+
430
+ // 构建
431
+ buildChannel(config, theme, channel, outputDir);
432
+
433
+ completedBuilds++;
434
+ const progress = Math.round(completedBuilds / totalBuilds * 100);
435
+ log(`进度: ${completedBuilds}/${totalBuilds} (${progress}%)`, 'success');
436
+ } catch (error) {
437
+ failedBuilds.push({ theme, channel, error: error.message });
438
+
439
+ if (!config.build.continueOnError) {
440
+ throw error;
441
+ }
442
+
443
+ log(`跳过失败的构建: ${theme}/${channel}`, 'warn');
444
+ }
445
+ }
446
+ }
447
+
448
+ // 恢复入口文件
449
+ if (config.themes.enabled) {
450
+ restoreEntryFile();
451
+ }
452
+
453
+ // 清理临时目录
454
+ if (config.build.cleanTemp) {
455
+ cleanTempDir(config);
456
+ }
457
+
458
+ // 打印结果
459
+ const duration = ((Date.now() - startTime) / 1000 / 60).toFixed(2);
460
+ log('\n========================================');
461
+ log('🎉 所有构建任务完成!', 'success');
462
+ log(`✓ 成功构建: ${completedBuilds}/${totalBuilds}`);
463
+
464
+ if (failedBuilds.length > 0) {
465
+ log(`✗ 失败构建: ${failedBuilds.length}`, 'warn');
466
+ failedBuilds.forEach(({ theme, channel, error }) => {
467
+ log(` - ${theme}/${channel}: ${error}`, 'error');
468
+ });
469
+ }
470
+
471
+ log(`⏱️ 耗时: ${duration} 分钟`);
472
+ log(`📁 输出目录: ${outputDir}`);
473
+ log('========================================');
474
+
475
+ } catch (error) {
476
+ log(`严重错误: ${error.message}`, 'error');
477
+ console.error(error);
478
+
479
+ // 确保恢复入口文件
480
+ restoreEntryFile();
481
+
482
+ process.exit(1);
483
+ }
484
+ }
485
+
486
+ // ==================== 信号处理 ====================
487
+
488
+ function cleanup() {
489
+ if (isExiting) return;
490
+ isExiting = true;
491
+
492
+ log('\n收到中断信号,正在清理...', 'warn');
493
+
494
+ restoreEntryFile();
495
+
496
+ const config = loadConfig();
497
+ if (config.build.cleanTemp) {
498
+ cleanTempDir(config);
499
+ }
500
+
501
+ process.exit(1);
502
+ }
503
+
504
+ process.on('SIGINT', cleanup);
505
+ process.on('SIGTERM', cleanup);
506
+
507
+ // ==================== 执行 ====================
508
+
509
+ main().catch(error => {
510
+ log(`未捕获的错误: ${error.message}`, 'error');
511
+ console.error(error);
512
+ restoreEntryFile();
513
+ process.exit(1);
514
+ });