@simplysm/sd-cli 13.0.100 → 14.0.1

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 (409) hide show
  1. package/dist/commands/build.js +29 -19
  2. package/dist/commands/build.js.map +1 -6
  3. package/dist/commands/check.d.ts +1 -0
  4. package/dist/commands/check.d.ts.map +1 -1
  5. package/dist/commands/check.js +130 -115
  6. package/dist/commands/check.js.map +1 -6
  7. package/dist/commands/dev.d.ts +6 -7
  8. package/dist/commands/dev.d.ts.map +1 -1
  9. package/dist/commands/dev.js +24 -14
  10. package/dist/commands/dev.js.map +1 -6
  11. package/dist/commands/lint.d.ts +1 -1
  12. package/dist/commands/lint.js +158 -116
  13. package/dist/commands/lint.js.map +1 -6
  14. package/dist/commands/publish.d.ts.map +1 -1
  15. package/dist/commands/publish.js +637 -510
  16. package/dist/commands/publish.js.map +1 -6
  17. package/dist/commands/replace-deps.js +12 -12
  18. package/dist/commands/replace-deps.js.map +1 -6
  19. package/dist/commands/typecheck.d.ts +5 -30
  20. package/dist/commands/typecheck.d.ts.map +1 -1
  21. package/dist/commands/typecheck.js +144 -207
  22. package/dist/commands/typecheck.js.map +1 -6
  23. package/dist/commands/watch.d.ts +6 -4
  24. package/dist/commands/watch.d.ts.map +1 -1
  25. package/dist/commands/watch.js +25 -16
  26. package/dist/commands/watch.js.map +1 -6
  27. package/dist/engines/NgtscEngine.d.ts +47 -0
  28. package/dist/engines/NgtscEngine.d.ts.map +1 -0
  29. package/dist/engines/NgtscEngine.js +151 -0
  30. package/dist/engines/NgtscEngine.js.map +1 -0
  31. package/dist/engines/ServerEsbuildEngine.d.ts +47 -0
  32. package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -0
  33. package/dist/engines/ServerEsbuildEngine.js +159 -0
  34. package/dist/engines/ServerEsbuildEngine.js.map +1 -0
  35. package/dist/engines/TscEngine.d.ts +47 -0
  36. package/dist/engines/TscEngine.d.ts.map +1 -0
  37. package/dist/engines/TscEngine.js +153 -0
  38. package/dist/engines/TscEngine.js.map +1 -0
  39. package/dist/engines/ViteEngine.d.ts +49 -0
  40. package/dist/engines/ViteEngine.d.ts.map +1 -0
  41. package/dist/engines/ViteEngine.js +161 -0
  42. package/dist/engines/ViteEngine.js.map +1 -0
  43. package/dist/engines/index.d.ts +26 -0
  44. package/dist/engines/index.d.ts.map +1 -0
  45. package/dist/engines/index.js +30 -0
  46. package/dist/engines/index.js.map +1 -0
  47. package/dist/engines/types.d.ts +77 -0
  48. package/dist/engines/types.d.ts.map +1 -0
  49. package/dist/engines/types.js +2 -0
  50. package/dist/engines/types.js.map +1 -0
  51. package/dist/index.d.ts +0 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -2
  54. package/dist/index.js.map +1 -6
  55. package/dist/infra/ResultCollector.d.ts +1 -1
  56. package/dist/infra/ResultCollector.d.ts.map +1 -1
  57. package/dist/infra/ResultCollector.js +30 -27
  58. package/dist/infra/ResultCollector.js.map +1 -6
  59. package/dist/infra/SignalHandler.js +45 -42
  60. package/dist/infra/SignalHandler.js.map +1 -6
  61. package/dist/infra/WorkerManager.js +56 -53
  62. package/dist/infra/WorkerManager.js.map +1 -6
  63. package/dist/orchestrators/BuildOrchestrator.d.ts +33 -1
  64. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  65. package/dist/orchestrators/BuildOrchestrator.js +314 -309
  66. package/dist/orchestrators/BuildOrchestrator.js.map +1 -6
  67. package/dist/orchestrators/DevWatchOrchestrator.d.ts +60 -0
  68. package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -0
  69. package/dist/orchestrators/DevWatchOrchestrator.js +465 -0
  70. package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -0
  71. package/dist/sd-cli-entry.d.ts.map +1 -1
  72. package/dist/sd-cli-entry.js +190 -266
  73. package/dist/sd-cli-entry.js.map +1 -6
  74. package/dist/sd-cli.js +77 -49
  75. package/dist/sd-cli.js.map +1 -6
  76. package/dist/sd-config.types.d.ts +2 -0
  77. package/dist/sd-config.types.d.ts.map +1 -1
  78. package/dist/sd-config.types.js +2 -1
  79. package/dist/sd-config.types.js.map +1 -6
  80. package/dist/utils/angular-build.d.ts +77 -0
  81. package/dist/utils/angular-build.d.ts.map +1 -0
  82. package/dist/utils/angular-build.js +84 -0
  83. package/dist/utils/angular-build.js.map +1 -0
  84. package/dist/utils/build-env.js +9 -9
  85. package/dist/utils/build-env.js.map +1 -6
  86. package/dist/utils/concurrency.d.ts +15 -0
  87. package/dist/utils/concurrency.d.ts.map +1 -0
  88. package/dist/utils/concurrency.js +38 -0
  89. package/dist/utils/concurrency.js.map +1 -0
  90. package/dist/utils/copy-public.js +104 -87
  91. package/dist/utils/copy-public.js.map +1 -6
  92. package/dist/utils/copy-src.js +49 -35
  93. package/dist/utils/copy-src.js.map +1 -6
  94. package/dist/utils/esbuild-config.d.ts +0 -29
  95. package/dist/utils/esbuild-config.d.ts.map +1 -1
  96. package/dist/utils/esbuild-config.js +151 -218
  97. package/dist/utils/esbuild-config.js.map +1 -6
  98. package/dist/utils/ngtsc-build-core.d.ts +49 -0
  99. package/dist/utils/ngtsc-build-core.d.ts.map +1 -0
  100. package/dist/utils/ngtsc-build-core.js +250 -0
  101. package/dist/utils/ngtsc-build-core.js.map +1 -0
  102. package/dist/utils/output-path-rewriter.d.ts +23 -0
  103. package/dist/utils/output-path-rewriter.d.ts.map +1 -0
  104. package/dist/utils/output-path-rewriter.js +74 -0
  105. package/dist/utils/output-path-rewriter.js.map +1 -0
  106. package/dist/utils/output-utils.js +55 -40
  107. package/dist/utils/output-utils.js.map +1 -6
  108. package/dist/utils/package-utils.d.ts +8 -0
  109. package/dist/utils/package-utils.d.ts.map +1 -1
  110. package/dist/utils/package-utils.js +103 -73
  111. package/dist/utils/package-utils.js.map +1 -6
  112. package/dist/utils/rebuild-manager.js +41 -44
  113. package/dist/utils/rebuild-manager.js.map +1 -6
  114. package/dist/utils/replace-deps.js +283 -184
  115. package/dist/utils/replace-deps.js.map +1 -6
  116. package/dist/utils/scss-compiler.d.ts +10 -0
  117. package/dist/utils/scss-compiler.d.ts.map +1 -0
  118. package/dist/utils/scss-compiler.js +36 -0
  119. package/dist/utils/scss-compiler.js.map +1 -0
  120. package/dist/utils/sd-config.js +29 -19
  121. package/dist/utils/sd-config.js.map +1 -6
  122. package/dist/utils/tsc-build.d.ts +36 -0
  123. package/dist/utils/tsc-build.d.ts.map +1 -0
  124. package/dist/utils/tsc-build.js +130 -0
  125. package/dist/utils/tsc-build.js.map +1 -0
  126. package/dist/utils/tsconfig.d.ts +7 -26
  127. package/dist/utils/tsconfig.d.ts.map +1 -1
  128. package/dist/utils/tsconfig.js +39 -64
  129. package/dist/utils/tsconfig.js.map +1 -6
  130. package/dist/utils/typecheck-non-package.d.ts +18 -0
  131. package/dist/utils/typecheck-non-package.d.ts.map +1 -0
  132. package/dist/utils/typecheck-non-package.js +64 -0
  133. package/dist/utils/typecheck-non-package.js.map +1 -0
  134. package/dist/utils/typecheck-serialization.js +58 -40
  135. package/dist/utils/typecheck-serialization.js.map +1 -6
  136. package/dist/utils/worker-events.js +48 -40
  137. package/dist/utils/worker-events.js.map +1 -6
  138. package/dist/utils/worker-utils.js +48 -28
  139. package/dist/utils/worker-utils.js.map +1 -6
  140. package/dist/vitest-plugin.d.ts +9 -0
  141. package/dist/vitest-plugin.d.ts.map +1 -0
  142. package/dist/vitest-plugin.js +85 -0
  143. package/dist/vitest-plugin.js.map +1 -0
  144. package/dist/workers/library-build.worker.d.ts +54 -0
  145. package/dist/workers/library-build.worker.d.ts.map +1 -0
  146. package/dist/workers/library-build.worker.js +97 -0
  147. package/dist/workers/library-build.worker.js.map +1 -0
  148. package/dist/workers/lint.worker.js +9 -6
  149. package/dist/workers/lint.worker.js.map +1 -6
  150. package/dist/workers/ngtsc-build.worker.d.ts +23 -0
  151. package/dist/workers/ngtsc-build.worker.d.ts.map +1 -0
  152. package/dist/workers/ngtsc-build.worker.js +98 -0
  153. package/dist/workers/ngtsc-build.worker.js.map +1 -0
  154. package/dist/workers/{server.worker.d.ts → server-build.worker.d.ts} +39 -29
  155. package/dist/workers/server-build.worker.d.ts.map +1 -0
  156. package/dist/workers/server-build.worker.js +399 -0
  157. package/dist/workers/server-build.worker.js.map +1 -0
  158. package/dist/workers/server-runtime.worker.d.ts +3 -2
  159. package/dist/workers/server-runtime.worker.d.ts.map +1 -1
  160. package/dist/workers/server-runtime.worker.js +100 -95
  161. package/dist/workers/server-runtime.worker.js.map +1 -6
  162. package/dist/workers/vite-build.worker.d.ts +56 -0
  163. package/dist/workers/vite-build.worker.d.ts.map +1 -0
  164. package/dist/workers/vite-build.worker.js +167 -0
  165. package/dist/workers/vite-build.worker.js.map +1 -0
  166. package/package.json +10 -16
  167. package/src/commands/check.ts +21 -3
  168. package/src/commands/dev.ts +10 -8
  169. package/src/commands/lint.ts +1 -1
  170. package/src/commands/publish.ts +4 -0
  171. package/src/commands/typecheck.ts +89 -256
  172. package/src/commands/watch.ts +9 -8
  173. package/src/engines/NgtscEngine.ts +190 -0
  174. package/src/engines/ServerEsbuildEngine.ts +195 -0
  175. package/src/engines/TscEngine.ts +189 -0
  176. package/src/engines/ViteEngine.ts +203 -0
  177. package/src/engines/index.ts +49 -0
  178. package/src/engines/types.ts +79 -0
  179. package/src/index.ts +0 -3
  180. package/src/infra/ResultCollector.ts +1 -1
  181. package/src/orchestrators/BuildOrchestrator.ts +87 -157
  182. package/src/orchestrators/DevWatchOrchestrator.ts +573 -0
  183. package/src/sd-cli-entry.ts +13 -116
  184. package/src/sd-config.types.ts +2 -0
  185. package/src/utils/angular-build.ts +157 -0
  186. package/src/utils/concurrency.ts +43 -0
  187. package/src/utils/esbuild-config.ts +1 -122
  188. package/src/utils/ngtsc-build-core.ts +379 -0
  189. package/src/utils/output-path-rewriter.ts +82 -0
  190. package/src/utils/package-utils.ts +20 -0
  191. package/src/utils/scss-compiler.ts +58 -0
  192. package/src/utils/tsc-build.ts +175 -0
  193. package/src/utils/tsconfig.ts +27 -95
  194. package/src/utils/typecheck-non-package.ts +87 -0
  195. package/src/vitest-plugin.ts +118 -0
  196. package/src/workers/library-build.worker.ts +153 -0
  197. package/src/workers/ngtsc-build.worker.ts +146 -0
  198. package/src/workers/server-build.worker.ts +565 -0
  199. package/src/workers/server-runtime.worker.ts +17 -26
  200. package/src/workers/vite-build.worker.ts +252 -0
  201. package/tests/commands/check.spec.ts +276 -0
  202. package/tests/commands/dev.spec.ts +53 -0
  203. package/tests/commands/lint.spec.ts +243 -0
  204. package/tests/commands/publish.spec.ts +1159 -0
  205. package/tests/commands/typecheck.spec.ts +294 -0
  206. package/tests/commands/watch.spec.ts +53 -0
  207. package/tests/engines/engine-selection.spec.ts +247 -0
  208. package/tests/engines/ngtsc-engine.spec.ts +274 -0
  209. package/tests/engines/server-esbuild-engine.spec.ts +256 -0
  210. package/tests/engines/tsc-engine.spec.ts +213 -0
  211. package/tests/engines/vite-engine.spec.ts +358 -0
  212. package/tests/infra/result-collector.spec.ts +46 -0
  213. package/tests/infra/signal-handler.spec.ts +32 -0
  214. package/tests/infra/worker-manager.spec.ts +63 -0
  215. package/tests/orchestrators/build-orchestrator.spec.ts +772 -0
  216. package/tests/orchestrators/dev-watch-orchestrator.spec.ts +1173 -0
  217. package/tests/sd-cli-entry.spec.ts +49 -0
  218. package/tests/utils/angular-build.spec.ts +251 -0
  219. package/tests/utils/build-env.spec.ts +33 -0
  220. package/tests/utils/concurrency.spec.ts +65 -0
  221. package/tests/utils/copy-src.spec.ts +144 -0
  222. package/tests/utils/esbuild-config.spec.ts +186 -0
  223. package/tests/utils/external-modules.spec.ts +161 -0
  224. package/tests/utils/ngtsc-scss-refactor.spec.ts +66 -0
  225. package/tests/utils/output-path-rewriter.spec.ts +165 -0
  226. package/tests/utils/output-utils.spec.ts +104 -0
  227. package/tests/utils/package-utils.spec.ts +52 -0
  228. package/tests/utils/rebuild-manager.spec.ts +30 -27
  229. package/tests/utils/replace-deps.spec.ts +69 -0
  230. package/tests/utils/scss-compiler.spec.ts +131 -0
  231. package/tests/utils/sd-config.spec.ts +77 -0
  232. package/tests/utils/tsc-build.spec.ts +358 -0
  233. package/tests/utils/tsconfig-angular.spec.ts +71 -0
  234. package/tests/utils/typecheck-non-package.spec.ts +120 -0
  235. package/tests/utils/worker-events.spec.ts +155 -0
  236. package/tests/utils/worker-utils.spec.ts +43 -0
  237. package/tests/vitest-plugin-cwd.spec.ts +68 -0
  238. package/tests/vitest-plugin.spec.ts +103 -0
  239. package/tests/workers/library-build-worker.spec.ts +258 -0
  240. package/tests/workers/ngtsc-build-worker.spec.ts +187 -0
  241. package/tests/workers/server-build-worker.spec.ts +566 -0
  242. package/tests/workers/server-runtime-worker.spec.ts +251 -0
  243. package/README.md +0 -295
  244. package/dist/builders/BaseBuilder.d.ts +0 -88
  245. package/dist/builders/BaseBuilder.d.ts.map +0 -1
  246. package/dist/builders/BaseBuilder.js +0 -142
  247. package/dist/builders/BaseBuilder.js.map +0 -6
  248. package/dist/builders/DtsBuilder.d.ts +0 -22
  249. package/dist/builders/DtsBuilder.d.ts.map +0 -1
  250. package/dist/builders/DtsBuilder.js +0 -72
  251. package/dist/builders/DtsBuilder.js.map +0 -6
  252. package/dist/builders/LibraryBuilder.d.ts +0 -22
  253. package/dist/builders/LibraryBuilder.d.ts.map +0 -1
  254. package/dist/builders/LibraryBuilder.js +0 -85
  255. package/dist/builders/LibraryBuilder.js.map +0 -6
  256. package/dist/builders/types.d.ts +0 -55
  257. package/dist/builders/types.d.ts.map +0 -1
  258. package/dist/builders/types.js +0 -1
  259. package/dist/builders/types.js.map +0 -6
  260. package/dist/capacitor/capacitor.d.ts +0 -151
  261. package/dist/capacitor/capacitor.d.ts.map +0 -1
  262. package/dist/capacitor/capacitor.js +0 -694
  263. package/dist/capacitor/capacitor.js.map +0 -6
  264. package/dist/commands/device.d.ts +0 -22
  265. package/dist/commands/device.d.ts.map +0 -1
  266. package/dist/commands/device.js +0 -98
  267. package/dist/commands/device.js.map +0 -6
  268. package/dist/commands/init.d.ts +0 -14
  269. package/dist/commands/init.d.ts.map +0 -1
  270. package/dist/commands/init.js +0 -72
  271. package/dist/commands/init.js.map +0 -6
  272. package/dist/electron/electron.d.ts +0 -84
  273. package/dist/electron/electron.d.ts.map +0 -1
  274. package/dist/electron/electron.js +0 -263
  275. package/dist/electron/electron.js.map +0 -6
  276. package/dist/orchestrators/DevOrchestrator.d.ts +0 -83
  277. package/dist/orchestrators/DevOrchestrator.d.ts.map +0 -1
  278. package/dist/orchestrators/DevOrchestrator.js +0 -540
  279. package/dist/orchestrators/DevOrchestrator.js.map +0 -6
  280. package/dist/orchestrators/WatchOrchestrator.d.ts +0 -57
  281. package/dist/orchestrators/WatchOrchestrator.d.ts.map +0 -1
  282. package/dist/orchestrators/WatchOrchestrator.js +0 -199
  283. package/dist/orchestrators/WatchOrchestrator.js.map +0 -6
  284. package/dist/utils/tailwind-config-deps.d.ts +0 -8
  285. package/dist/utils/tailwind-config-deps.d.ts.map +0 -1
  286. package/dist/utils/tailwind-config-deps.js +0 -82
  287. package/dist/utils/tailwind-config-deps.js.map +0 -6
  288. package/dist/utils/template.d.ts +0 -14
  289. package/dist/utils/template.d.ts.map +0 -1
  290. package/dist/utils/template.js +0 -33
  291. package/dist/utils/template.js.map +0 -6
  292. package/dist/utils/vite-config.d.ts +0 -35
  293. package/dist/utils/vite-config.d.ts.map +0 -1
  294. package/dist/utils/vite-config.js +0 -259
  295. package/dist/utils/vite-config.js.map +0 -6
  296. package/dist/workers/client.worker.d.ts +0 -83
  297. package/dist/workers/client.worker.d.ts.map +0 -1
  298. package/dist/workers/client.worker.js +0 -111
  299. package/dist/workers/client.worker.js.map +0 -6
  300. package/dist/workers/dts.worker.d.ts +0 -75
  301. package/dist/workers/dts.worker.d.ts.map +0 -1
  302. package/dist/workers/dts.worker.js +0 -270
  303. package/dist/workers/dts.worker.js.map +0 -6
  304. package/dist/workers/library.worker.d.ts +0 -75
  305. package/dist/workers/library.worker.d.ts.map +0 -1
  306. package/dist/workers/library.worker.js +0 -166
  307. package/dist/workers/library.worker.js.map +0 -6
  308. package/dist/workers/server.worker.d.ts.map +0 -1
  309. package/dist/workers/server.worker.js +0 -482
  310. package/dist/workers/server.worker.js.map +0 -6
  311. package/src/builders/BaseBuilder.ts +0 -218
  312. package/src/builders/DtsBuilder.ts +0 -92
  313. package/src/builders/LibraryBuilder.ts +0 -110
  314. package/src/builders/types.ts +0 -60
  315. package/src/capacitor/capacitor.ts +0 -931
  316. package/src/commands/device.ts +0 -140
  317. package/src/commands/init.ts +0 -113
  318. package/src/electron/electron.ts +0 -362
  319. package/src/orchestrators/DevOrchestrator.ts +0 -744
  320. package/src/orchestrators/WatchOrchestrator.ts +0 -277
  321. package/src/utils/tailwind-config-deps.ts +0 -98
  322. package/src/utils/template.ts +0 -56
  323. package/src/utils/vite-config.ts +0 -390
  324. package/src/workers/client.worker.ts +0 -250
  325. package/src/workers/dts.worker.ts +0 -453
  326. package/src/workers/library.worker.ts +0 -316
  327. package/src/workers/server.worker.ts +0 -734
  328. package/templates/init/.gitignore.hbs +0 -34
  329. package/templates/init/.npmrc.hbs +0 -1
  330. package/templates/init/.prettierignore +0 -1
  331. package/templates/init/.prettierrc.yaml +0 -12
  332. package/templates/init/eslint.config.ts +0 -15
  333. package/templates/init/mise.toml +0 -3
  334. package/templates/init/package.json.hbs +0 -32
  335. package/templates/init/packages/client-admin/index.html.hbs +0 -144
  336. package/templates/init/packages/client-admin/package.json.hbs +0 -27
  337. package/templates/init/packages/client-admin/public/assets/logo-landscape.png +0 -0
  338. package/templates/init/packages/client-admin/public/assets/logo.png +0 -0
  339. package/templates/init/packages/client-admin/public/favicon.ico +0 -0
  340. package/templates/init/packages/client-admin/src/App.tsx +0 -42
  341. package/templates/init/packages/client-admin/src/dev/DevDialog.tsx +0 -34
  342. package/templates/init/packages/client-admin/src/events/AuthChangeEvent.ts +0 -3
  343. package/templates/init/packages/client-admin/src/main.css +0 -4
  344. package/templates/init/packages/client-admin/src/main.tsx.hbs +0 -146
  345. package/templates/init/packages/client-admin/src/providers/AppServiceProvider.tsx.hbs +0 -103
  346. package/templates/init/packages/client-admin/src/providers/AppStructureProvider.tsx +0 -84
  347. package/templates/init/packages/client-admin/src/providers/AuthProvider.tsx.hbs +0 -96
  348. package/templates/init/packages/client-admin/src/providers/configureSharedData.ts.hbs +0 -67
  349. package/templates/init/packages/client-admin/src/views/auth/LoginView.tsx +0 -132
  350. package/templates/init/packages/client-admin/src/views/home/HomeView.tsx +0 -108
  351. package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeDetail.tsx.hbs +0 -243
  352. package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeSheet.tsx.hbs +0 -271
  353. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleDetail.tsx.hbs +0 -146
  354. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionDetail.tsx.hbs +0 -121
  355. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionView.tsx +0 -52
  356. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleSheet.tsx.hbs +0 -125
  357. package/templates/init/packages/client-admin/src/views/home/main/MainView.tsx.hbs +0 -13
  358. package/templates/init/packages/client-admin/src/views/home/my-info/MyInfoDetail.tsx.hbs +0 -241
  359. package/templates/init/packages/client-admin/src/views/home/system/system-log/SystemLogSheet.tsx.hbs +0 -169
  360. package/templates/init/packages/client-admin/src/views/not-found/NotFoundView.tsx +0 -15
  361. package/templates/init/packages/client-admin/tailwind.config.ts +0 -10
  362. package/templates/init/packages/db-main/package.json.hbs +0 -13
  363. package/templates/init/packages/db-main/src/MainDbContext.ts +0 -22
  364. package/templates/init/packages/db-main/src/dataLogExt.ts +0 -127
  365. package/templates/init/packages/db-main/src/index.ts +0 -14
  366. package/templates/init/packages/db-main/src/tables/base/Employee.ts +0 -24
  367. package/templates/init/packages/db-main/src/tables/base/EmployeeConfig.ts +0 -13
  368. package/templates/init/packages/db-main/src/tables/base/Role.ts +0 -9
  369. package/templates/init/packages/db-main/src/tables/base/RolePermission.ts +0 -13
  370. package/templates/init/packages/db-main/src/tables/system/_DataLog.ts +0 -19
  371. package/templates/init/packages/db-main/src/tables/system/_Log.ts +0 -16
  372. package/templates/init/packages/server/package.json.hbs +0 -20
  373. package/templates/init/packages/server/public-dev/dev//354/264/210/352/270/260/355/231/224.xlsx +0 -0
  374. package/templates/init/packages/server/src/index.ts +0 -4
  375. package/templates/init/packages/server/src/main.ts.hbs +0 -34
  376. package/templates/init/packages/server/src/services/AuthService.ts.hbs +0 -171
  377. package/templates/init/packages/server/src/services/DevService.ts.hbs +0 -94
  378. package/templates/init/packages/server/src/services/EmployeeService.ts.hbs +0 -122
  379. package/templates/init/packages/server/src/services/RoleService.ts.hbs +0 -59
  380. package/templates/init/pnpm-workspace.yaml +0 -15
  381. package/templates/init/sd.config.ts.hbs +0 -48
  382. package/templates/init/tsconfig.json.hbs +0 -39
  383. package/templates/init/vitest.config.ts +0 -36
  384. package/tests/capacitor-exclude.spec.ts +0 -78
  385. package/tests/capacitor.spec.ts +0 -49
  386. package/tests/copy-src.spec.ts +0 -50
  387. package/tests/electron-exclude.spec.ts +0 -61
  388. package/tests/get-compiler-options-for-package.spec.ts +0 -80
  389. package/tests/get-package-source-files.spec.ts +0 -139
  390. package/tests/get-types-from-package-json.spec.ts +0 -92
  391. package/tests/infra/ResultCollector.spec.ts +0 -30
  392. package/tests/infra/SignalHandler.spec.ts +0 -38
  393. package/tests/infra/WorkerManager.spec.ts +0 -63
  394. package/tests/load-ignore-patterns.spec.ts +0 -163
  395. package/tests/load-sd-config.spec.ts +0 -100
  396. package/tests/package-utils.spec.ts +0 -188
  397. package/tests/parse-root-tsconfig.spec.ts +0 -89
  398. package/tests/publish-config-narrowing.spec.ts +0 -20
  399. package/tests/replace-deps.spec.ts +0 -308
  400. package/tests/run-lint.spec.ts +0 -366
  401. package/tests/run-typecheck.spec.ts +0 -544
  402. package/tests/run-watch.spec.ts +0 -76
  403. package/tests/sd-cli.spec.ts +0 -265
  404. package/tests/sd-public-dev-plugin-mime.spec.ts +0 -19
  405. package/tests/tailwind-config-deps.spec.ts +0 -30
  406. package/tests/template.spec.ts +0 -70
  407. package/tests/vite-config-exclude.spec.ts +0 -35
  408. package/tests/vite-config-outdir.spec.ts +0 -38
  409. package/tests/write-changed-output-files.spec.ts +0 -97
@@ -1,931 +0,0 @@
1
- import path from "path";
2
- import { fsx } from "@simplysm/core-node";
3
- import { env } from "@simplysm/core-common";
4
- import { consola } from "consola";
5
- import sharp from "sharp";
6
- import type { SdCapacitorConfig } from "../sd-config.types";
7
- import { execa } from "execa";
8
-
9
- /**
10
- * package.json type
11
- */
12
- interface NpmConfig {
13
- name: string;
14
- version: string;
15
- dependencies?: Record<string, string>;
16
- devDependencies?: Record<string, string>;
17
- peerDependencies?: Record<string, string>;
18
- volta?: unknown;
19
- }
20
-
21
- /**
22
- * Configuration validation error
23
- */
24
- class CapacitorConfigError extends Error {
25
- constructor(message: string) {
26
- super(message);
27
- this.name = "CapacitorConfigError";
28
- }
29
- }
30
-
31
- /**
32
- * Capacitor project management class
33
- *
34
- * - Initialize Capacitor project
35
- * - Build Android APK/AAB
36
- * - Run app on device
37
- */
38
- export class Capacitor {
39
- private static readonly _ANDROID_KEYSTORE_FILE_NAME = "android.keystore";
40
- private static readonly _LOCK_FILE_NAME = ".capacitor.lock";
41
- private static readonly _logger = consola.withTag("sd:cli:capacitor");
42
- private static readonly _utf8Decoder = new TextDecoder("utf-8", { fatal: true });
43
- private static readonly _fallbackDecoder = new TextDecoder("euc-kr");
44
-
45
- private readonly _capPath: string;
46
- private readonly _platforms: string[];
47
- private readonly _npmConfig: NpmConfig;
48
-
49
- private constructor(
50
- private readonly _pkgPath: string,
51
- private readonly _config: SdCapacitorConfig,
52
- npmConfig: NpmConfig,
53
- private readonly _exclude: string[],
54
- ) {
55
- this._platforms = Object.keys(this._config.platform ?? {});
56
- this._npmConfig = npmConfig;
57
- this._capPath = path.resolve(this._pkgPath, ".capacitor");
58
- }
59
-
60
- /**
61
- * Create Capacitor instance (with configuration validation)
62
- */
63
- static async create(
64
- pkgPath: string,
65
- config: SdCapacitorConfig,
66
- exclude?: string[],
67
- ): Promise<Capacitor> {
68
- // F5: validate runtime configuration
69
- Capacitor._validateConfig(config);
70
-
71
- const npmConfig = await fsx.readJson<NpmConfig>(path.resolve(pkgPath, "package.json"));
72
- return new Capacitor(pkgPath, config, npmConfig, exclude ?? []);
73
- }
74
-
75
- /**
76
- * F5: Validate configuration
77
- */
78
- private static _validateConfig(config: SdCapacitorConfig): void {
79
- if (typeof config.appId !== "string" || config.appId.trim() === "") {
80
- throw new CapacitorConfigError("capacitor.appId is required.");
81
- }
82
- if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/i.test(config.appId)) {
83
- throw new CapacitorConfigError(`capacitor.appId format is invalid: ${config.appId}`);
84
- }
85
- if (typeof config.appName !== "string" || config.appName.trim() === "") {
86
- throw new CapacitorConfigError("capacitor.appName is required.");
87
- }
88
- if (!/^[\p{L}\p{N} \-]+$/u.test(config.appName)) {
89
- throw new CapacitorConfigError(`capacitor.appName contains invalid characters: ${config.appName}`);
90
- }
91
- if (config.platform != null) {
92
- const platforms = Object.keys(config.platform);
93
- for (const p of platforms) {
94
- if (p !== "android") {
95
- throw new CapacitorConfigError(`unsupported platform: ${p} (currently only android is supported)`);
96
- }
97
- }
98
- }
99
- }
100
-
101
- /**
102
- * Execute a Capacitor CLI command via npx
103
- */
104
- private async _execCap(args: string[]): Promise<string> {
105
- return this._exec("npx", ["cap", ...args], this._capPath);
106
- }
107
-
108
- /**
109
- * Execute capacitor-assets CLI command via npx
110
- */
111
- private async _execCapAssets(args: string[]): Promise<string> {
112
- return this._exec("npx", ["capacitor-assets", ...args], this._capPath);
113
- }
114
-
115
- /**
116
- * Execute command (with logging)
117
- */
118
- private async _exec(cmd: string, args: string[], cwd: string): Promise<string> {
119
- Capacitor._logger.debug(`executed command: ${cmd} ${args.join(" ")}`);
120
-
121
- const result = await execa(cmd, args, { cwd, reject: false, encoding: "buffer" });
122
- const stdout = Capacitor._decodeOutput(result.stdout);
123
-
124
- if (result.exitCode !== 0) {
125
- const stderr = Capacitor._decodeOutput(result.stderr);
126
- throw new Error(`${cmd} ${args.join(" ")} failed (exit ${result.exitCode}): ${stderr || stdout}`);
127
- }
128
-
129
- Capacitor._logger.debug(`execution result: ${stdout}`);
130
- return stdout;
131
- }
132
-
133
- /**
134
- * Decode command output (UTF-8 first, fallback to EUC-KR for Windows CP949)
135
- */
136
- private static _decodeOutput(bytes: Uint8Array): string {
137
- try {
138
- return Capacitor._utf8Decoder.decode(bytes);
139
- } catch {
140
- return Capacitor._fallbackDecoder.decode(bytes);
141
- }
142
- }
143
-
144
- /**
145
- * F10: Acquire lock to prevent concurrent execution
146
- */
147
- private async _acquireLock(): Promise<void> {
148
- const lockPath = path.resolve(this._capPath, Capacitor._LOCK_FILE_NAME);
149
- if (await fsx.exists(lockPath)) {
150
- const lockContent = await fsx.read(lockPath);
151
- throw new Error(
152
- `Another Capacitor operation is in progress (PID: ${lockContent}). ` +
153
- `If there's an issue, delete the ${lockPath} file.`,
154
- );
155
- }
156
- await fsx.mkdir(this._capPath);
157
- await fsx.write(lockPath, String(process.pid));
158
- }
159
-
160
- /**
161
- * F10: Release lock
162
- */
163
- private async _releaseLock(): Promise<void> {
164
- const lockPath = path.resolve(this._capPath, Capacitor._LOCK_FILE_NAME);
165
- await fsx.rm(lockPath);
166
- }
167
-
168
- /**
169
- * F4: Validate external tools
170
- */
171
- private async _validateTools(): Promise<void> {
172
- // Check Android SDK
173
- const sdkPath = await this._findAndroidSdk();
174
- if (sdkPath == null) {
175
- throw new Error(
176
- "Android SDK not found.\n" +
177
- "1. Install Android Studio or\n" +
178
- "2. Set ANDROID_HOME or ANDROID_SDK_ROOT environment variable.",
179
- );
180
- }
181
-
182
- // Check Java (only for android platform)
183
- if (this._platforms.includes("android")) {
184
- const javaPath = await this._findJava21();
185
- if (javaPath == null) {
186
- Capacitor._logger.warn(
187
- "Java 21 not found. Gradle may use embedded JDK or the build may fail.",
188
- );
189
- }
190
- }
191
- }
192
-
193
- /**
194
- * Initialize Capacitor project
195
- *
196
- * 1. Create package.json and install dependencies
197
- * 2. Create capacitor.config.ts
198
- * 3. Add platform (android)
199
- * 4. Set up icon
200
- * 5. Configure Android native settings
201
- * 6. Run cap sync or cap copy
202
- */
203
- async initialize(): Promise<void> {
204
- await this._acquireLock();
205
-
206
- try {
207
- // F4: Validate external tools
208
- await this._validateTools();
209
-
210
- // 1. Initialize Capacitor project
211
- const changed = await this._initCap();
212
-
213
- // 2. Create Capacitor config file
214
- await this._writeCapConf();
215
-
216
- // 3. Manage platform (F12: idempotent - skip if already exists)
217
- await this._addPlatforms();
218
-
219
- // 4. Set up icon (F6: error recovery)
220
- await this._setupIcon();
221
-
222
- // 5. Configure Android native settings
223
- if (this._platforms.includes("android")) {
224
- await this._configureAndroid();
225
- }
226
-
227
- // 6. Synchronize web assets
228
- if (changed) {
229
- await this._execCap(["sync"]);
230
- } else {
231
- await this._execCap(["copy"]);
232
- }
233
- } finally {
234
- await this._releaseLock();
235
- }
236
- }
237
-
238
- /**
239
- * Build Android APK/AAB
240
- */
241
- async build(outPath: string): Promise<void> {
242
- await this._acquireLock();
243
-
244
- try {
245
- const buildType = this._config.debug ? "debug" : "release";
246
-
247
- for (const platform of this._platforms) {
248
- await this._execCap(["copy", platform]);
249
-
250
- if (platform === "android") {
251
- await this._buildAndroid(outPath, buildType);
252
- } else {
253
- throw new Error(`unsupported platform: ${platform}`);
254
- }
255
- }
256
- } finally {
257
- await this._releaseLock();
258
- }
259
- }
260
-
261
- /**
262
- * Run app on device (connect WebView to development server)
263
- */
264
- async runOnDevice(url?: string): Promise<void> {
265
- // F11: Validate URL
266
- if (url != null) {
267
- this._validateUrl(url);
268
- await this._updateServerUrl(url);
269
- }
270
-
271
- for (const platform of this._platforms) {
272
- await this._execCap(["copy", platform]);
273
-
274
- try {
275
- await this._execCap(["run", platform]);
276
- } catch (err) {
277
- if (platform === "android") {
278
- try {
279
- await this._exec("adb", ["kill-server"], this._capPath);
280
- } catch {
281
- // adb kill-server failure is ignored
282
- }
283
- }
284
- throw err;
285
- }
286
- }
287
- }
288
-
289
- /**
290
- * F11: Validate URL
291
- */
292
- private _validateUrl(url: string): void {
293
- try {
294
- const parsed = new URL(url);
295
- if (!["http:", "https:"].includes(parsed.protocol)) {
296
- throw new Error(`unsupported protocol: ${parsed.protocol}`);
297
- }
298
- } catch (err) {
299
- if (err instanceof TypeError) {
300
- throw new Error(`invalid URL: ${url}`);
301
- }
302
- throw err;
303
- }
304
- }
305
-
306
- //#region Private - Initialization
307
-
308
- /**
309
- * Basic Capacitor project initialization (package.json, pnpm install, cap init)
310
- */
311
- private async _initCap(): Promise<boolean> {
312
- const depChanged = await this._setupNpmConf();
313
- const nodeModulesExists = await fsx.exists(path.resolve(this._capPath, "node_modules"));
314
-
315
- if (!depChanged && nodeModulesExists) return false;
316
-
317
- // pnpm install
318
- const installResult = await this._exec("npm", ["install"], this._capPath);
319
- Capacitor._logger.debug(`npm install completed: ${installResult}`);
320
-
321
- // F12: cap init idempotency - execute only when capacitor.config.ts does not exist
322
- const configPath = path.resolve(this._capPath, "capacitor.config.ts");
323
- if (!(await fsx.exists(configPath))) {
324
- await this._execCap(["init", this._config.appId, this._config.appId]);
325
- }
326
-
327
- // Create default www/index.html
328
- const wwwPath = path.resolve(this._capPath, "www");
329
- await fsx.mkdir(wwwPath);
330
- await fsx.write(
331
- path.resolve(wwwPath, "index.html"),
332
- "<!DOCTYPE html><html><head></head><body></body></html>",
333
- );
334
-
335
- return true;
336
- }
337
-
338
- /**
339
- * Configure package.json
340
- */
341
- private async _setupNpmConf(): Promise<boolean> {
342
- const projNpmConfigPath = path.resolve(this._pkgPath, "../../package.json");
343
-
344
- // F3: Check if file exists
345
- if (!(await fsx.exists(projNpmConfigPath))) {
346
- throw new Error(`root package.json not found: ${projNpmConfigPath}`);
347
- }
348
-
349
- const projNpmConfig = await fsx.readJson<NpmConfig>(projNpmConfigPath);
350
-
351
- const capNpmConfPath = path.resolve(this._capPath, "package.json");
352
- const orgCapNpmConf: NpmConfig = (await fsx.exists(capNpmConfPath))
353
- ? await fsx.readJson<NpmConfig>(capNpmConfPath)
354
- : { name: "", version: "" };
355
-
356
- const capNpmConf: NpmConfig = { ...orgCapNpmConf };
357
- capNpmConf.name = this._config.appId;
358
- capNpmConf.version = this._npmConfig.version;
359
- if (projNpmConfig.volta != null) {
360
- capNpmConf.volta = projNpmConfig.volta;
361
- }
362
-
363
- // Default dependencies
364
- capNpmConf.dependencies = capNpmConf.dependencies ?? {};
365
- capNpmConf.dependencies["@capacitor/core"] = "^7.0.0";
366
- capNpmConf.dependencies["@capacitor/app"] = "^7.0.0";
367
- for (const platform of this._platforms) {
368
- capNpmConf.dependencies[`@capacitor/${platform}`] = "^7.0.0";
369
- }
370
-
371
- capNpmConf.devDependencies = capNpmConf.devDependencies ?? {};
372
- capNpmConf.devDependencies["@capacitor/cli"] = "^7.0.0";
373
- capNpmConf.devDependencies["@capacitor/assets"] = "^3.0.0";
374
-
375
- // Configure plugin packages
376
- const mainDeps = {
377
- ...this._npmConfig.dependencies,
378
- ...this._npmConfig.devDependencies,
379
- ...this._npmConfig.peerDependencies,
380
- };
381
-
382
- const usePlugins = Object.keys(this._config.plugins ?? {});
383
-
384
- const prevPlugins = Object.keys(capNpmConf.dependencies).filter(
385
- (item) =>
386
- !["@capacitor/core", "@capacitor/android", "@capacitor/ios", "@capacitor/app"].includes(
387
- item,
388
- ),
389
- );
390
-
391
- // Remove unused plugins
392
- for (const prevPlugin of prevPlugins) {
393
- if (!usePlugins.includes(prevPlugin)) {
394
- delete capNpmConf.dependencies[prevPlugin];
395
- Capacitor._logger.debug(`plugin removed: ${prevPlugin}`);
396
- }
397
- }
398
-
399
- // Add new plugins
400
- for (const plugin of usePlugins) {
401
- if (!(plugin in capNpmConf.dependencies)) {
402
- const version = mainDeps[plugin] ?? "*";
403
- capNpmConf.dependencies[plugin] = version;
404
- Capacitor._logger.debug(`plugin added: ${plugin}@${version}`);
405
- }
406
- }
407
-
408
- // Add exclude packages
409
- for (const excludePkg of this._exclude) {
410
- if (!(excludePkg in capNpmConf.dependencies)) {
411
- const version = mainDeps[excludePkg] ?? "*";
412
- capNpmConf.dependencies[excludePkg] = version;
413
- Capacitor._logger.debug(`exclude package added: ${excludePkg}@${version}`);
414
- }
415
- }
416
-
417
- // Save
418
- await fsx.mkdir(this._capPath);
419
- await fsx.writeJson(capNpmConfPath, capNpmConf, { space: 2 });
420
-
421
- // Check if dependencies changed
422
- const isChanged =
423
- orgCapNpmConf.volta !== capNpmConf.volta ||
424
- JSON.stringify(orgCapNpmConf.dependencies) !== JSON.stringify(capNpmConf.dependencies) ||
425
- JSON.stringify(orgCapNpmConf.devDependencies) !== JSON.stringify(capNpmConf.devDependencies);
426
-
427
- return isChanged;
428
- }
429
-
430
- /**
431
- * Create capacitor.config.ts
432
- */
433
- private async _writeCapConf(): Promise<void> {
434
- const confPath = path.resolve(this._capPath, "capacitor.config.ts");
435
-
436
- // Create plugin options
437
- const pluginOptions: Record<string, Record<string, unknown>> = {};
438
- for (const [pluginName, options] of Object.entries(this._config.plugins ?? {})) {
439
- if (options !== true) {
440
- const configKey = this._toPascalCase(pluginName.split("/").at(-1)!);
441
- pluginOptions[configKey] = options;
442
- }
443
- }
444
-
445
- const pluginsConfigStr =
446
- Object.keys(pluginOptions).length > 0
447
- ? JSON.stringify(pluginOptions, null, 2).replace(/^/gm, " ").trim()
448
- : "{}";
449
-
450
- const configContent = `import type { CapacitorConfig } from "@capacitor/cli";
451
-
452
- const config: CapacitorConfig = {
453
- appId: "${this._config.appId}",
454
- appName: "${this._config.appName}",
455
- server: {
456
- androidScheme: "http",
457
- cleartext: true
458
- },
459
- android: {},
460
- plugins: ${pluginsConfigStr},
461
- };
462
-
463
- export default config;
464
- `;
465
-
466
- await fsx.write(confPath, configContent);
467
- }
468
-
469
- /**
470
- * Add platform (F12: ensure idempotency)
471
- */
472
- private async _addPlatforms(): Promise<void> {
473
- for (const platform of this._platforms) {
474
- const platformPath = path.resolve(this._capPath, platform);
475
- if (await fsx.exists(platformPath)) {
476
- Capacitor._logger.debug(`platform already exists: ${platform}`);
477
- continue;
478
- }
479
-
480
- await this._execCap(["add", platform]);
481
- }
482
- }
483
-
484
- /**
485
- * Set up icon (F6: error recovery)
486
- */
487
- private async _setupIcon(): Promise<void> {
488
- const assetsDirPath = path.resolve(this._capPath, "assets");
489
-
490
- if (this._config.icon != null) {
491
- const iconSource = path.resolve(this._pkgPath, this._config.icon);
492
-
493
- // F6: Check if source icon exists
494
- if (!(await fsx.exists(iconSource))) {
495
- Capacitor._logger.warn(
496
- `icon file not found: ${iconSource}. Using default icon.`,
497
- );
498
- return;
499
- }
500
-
501
- try {
502
- await fsx.mkdir(assetsDirPath);
503
-
504
- // Create icon
505
- const logoPath = path.resolve(assetsDirPath, "logo.png");
506
-
507
- const logoSize = Math.floor(1024 * 0.6);
508
- const padding = Math.floor((1024 - logoSize) / 2);
509
-
510
- // F6: Handle sharp errors
511
- await sharp(iconSource)
512
- .resize(logoSize, logoSize, {
513
- fit: "contain",
514
- background: { r: 0, g: 0, b: 0, alpha: 0 },
515
- })
516
- .extend({
517
- top: padding,
518
- bottom: padding,
519
- left: padding,
520
- right: padding,
521
- background: { r: 0, g: 0, b: 0, alpha: 0 },
522
- })
523
- .toFile(logoPath);
524
-
525
- await this._execCapAssets([
526
- "generate",
527
- "--iconBackgroundColor",
528
- "#ffffff",
529
- "--splashBackgroundColor",
530
- "#ffffff",
531
- ]);
532
- } catch (err) {
533
- Capacitor._logger.warn(
534
- `icon generation failed: ${err instanceof Error ? err.message : err}. Using default icon.`,
535
- );
536
- // F6: Continue even if it fails (use default icon)
537
- }
538
- } else {
539
- await fsx.rm(assetsDirPath);
540
- }
541
- }
542
-
543
- //#endregion
544
-
545
- //#region Private - Android Configuration
546
-
547
- /**
548
- * Configure Android native settings
549
- */
550
- private async _configureAndroid(): Promise<void> {
551
- const androidPath = path.resolve(this._capPath, "android");
552
-
553
- // F3: Check if Android directory exists
554
- if (!(await fsx.exists(androidPath))) {
555
- throw new Error(`Android project directory not found: ${androidPath}`);
556
- }
557
-
558
- await this._configureAndroidJavaHomePath(androidPath);
559
- await this._configureAndroidSdkPath(androidPath);
560
- await this._configureAndroidManifest(androidPath);
561
- await this._configureAndroidBuildGradle(androidPath);
562
- }
563
-
564
- /**
565
- * Set up JAVA_HOME path (gradle.properties)
566
- */
567
- private async _configureAndroidJavaHomePath(androidPath: string): Promise<void> {
568
- const gradlePropsPath = path.resolve(androidPath, "gradle.properties");
569
-
570
- // F3: Check if file exists
571
- if (!(await fsx.exists(gradlePropsPath))) {
572
- Capacitor._logger.warn(`gradle.properties file not found: ${gradlePropsPath}`);
573
- return;
574
- }
575
-
576
- let content = await fsx.read(gradlePropsPath);
577
-
578
- const java21Path = await this._findJava21();
579
- if (java21Path != null && !content.includes("org.gradle.java.home")) {
580
- // F9: Improved Windows path escaping
581
- const escapedPath = java21Path.replace(/\\/g, "\\\\");
582
- content += `\norg.gradle.java.home=${escapedPath}\n`;
583
- await fsx.write(gradlePropsPath, content);
584
- }
585
- }
586
-
587
- /**
588
- * Auto-detect Java 21 path
589
- */
590
- private async _findJava21(): Promise<string | undefined> {
591
- const patterns = [
592
- "C:/Program Files/Amazon Corretto/jdk21*",
593
- "C:/Program Files/Eclipse Adoptium/jdk-21*",
594
- "C:/Program Files/Java/jdk-21*",
595
- "C:/Program Files/Microsoft/jdk-21*",
596
- "/usr/lib/jvm/java-21*",
597
- "/usr/lib/jvm/temurin-21*",
598
- ];
599
-
600
- for (const pattern of patterns) {
601
- const matches = await fsx.glob(pattern);
602
- if (matches.length > 0) {
603
- return matches.sort().at(-1);
604
- }
605
- }
606
-
607
- return undefined;
608
- }
609
-
610
- /**
611
- * Set up Android SDK path (local.properties)
612
- */
613
- private async _configureAndroidSdkPath(androidPath: string): Promise<void> {
614
- const localPropsPath = path.resolve(androidPath, "local.properties");
615
-
616
- const sdkPath = await this._findAndroidSdk();
617
- if (sdkPath != null) {
618
- // F9: Always use forward slash (Gradle compatible)
619
- await fsx.write(localPropsPath, `sdk.dir=${sdkPath.replace(/\\/g, "/")}\n`);
620
- } else {
621
- throw new Error(
622
- "Android SDK not found.\n" +
623
- "1. Install Android Studio or\n" +
624
- "2. Set ANDROID_HOME or ANDROID_SDK_ROOT environment variable.",
625
- );
626
- }
627
- }
628
-
629
- /**
630
- * Search for Android SDK path
631
- */
632
- private async _findAndroidSdk(): Promise<string | undefined> {
633
- const fromEnv = (env["ANDROID_HOME"] ?? env["ANDROID_SDK_ROOT"]) as string | undefined;
634
- if (fromEnv != null && (await fsx.exists(fromEnv))) {
635
- return fromEnv;
636
- }
637
-
638
- const candidates = [
639
- path.resolve((env["LOCALAPPDATA"] as string | undefined) ?? "", "Android/Sdk"),
640
- path.resolve((env["HOME"] as string | undefined) ?? "", "Android/Sdk"),
641
- "C:/Program Files/Android/Sdk",
642
- "C:/Android/Sdk",
643
- ];
644
-
645
- for (const candidate of candidates) {
646
- if (await fsx.exists(candidate)) {
647
- return candidate;
648
- }
649
- }
650
-
651
- return undefined;
652
- }
653
-
654
- /**
655
- * Modify AndroidManifest.xml (F3: add error handling)
656
- */
657
- private async _configureAndroidManifest(androidPath: string): Promise<void> {
658
- const manifestPath = path.resolve(androidPath, "app/src/main/AndroidManifest.xml");
659
-
660
- // F3: Check if file exists
661
- if (!(await fsx.exists(manifestPath))) {
662
- throw new Error(`AndroidManifest.xml file not found: ${manifestPath}`);
663
- }
664
-
665
- let content = await fsx.read(manifestPath);
666
-
667
- // Configure usesCleartextTraffic
668
- if (!content.includes("android:usesCleartextTraffic")) {
669
- content = content.replace("<application", '<application android:usesCleartextTraffic="true"');
670
- }
671
-
672
- // Configure additional permissions
673
- const permissions = this._config.platform?.android?.permissions ?? [];
674
- for (const perm of permissions) {
675
- const permTag = `<uses-permission android:name="android.permission.${perm.name}"`;
676
- if (!content.includes(permTag)) {
677
- const maxSdkAttr =
678
- perm.maxSdkVersion != null ? ` android:maxSdkVersion="${perm.maxSdkVersion}"` : "";
679
- const ignoreAttr = perm.ignore != null ? ` tools:ignore="${perm.ignore}"` : "";
680
- const permLine = ` ${permTag}${maxSdkAttr}${ignoreAttr} />\n`;
681
-
682
- if (perm.ignore != null && !content.includes("xmlns:tools=")) {
683
- content = content.replace(
684
- "<manifest xmlns:android",
685
- '<manifest xmlns:tools="http://schemas.android.com/tools" xmlns:android',
686
- );
687
- }
688
-
689
- content = content.replace("</manifest>", `${permLine}</manifest>`);
690
- }
691
- }
692
-
693
- // Configure additional application settings
694
- const appConfig = this._config.platform?.android?.config;
695
- if (appConfig) {
696
- for (const [key, value] of Object.entries(appConfig)) {
697
- const attr = `android:${key}="${value}"`;
698
- if (!content.includes(`android:${key}=`)) {
699
- content = content.replace("<application", `<application ${attr}`);
700
- }
701
- }
702
- }
703
-
704
- // Configure intentFilters
705
- const intentFilters = this._config.platform?.android?.intentFilters ?? [];
706
- for (const filter of intentFilters) {
707
- const filterKey = filter.action ?? filter.category ?? "";
708
- if (filterKey && !content.includes(filterKey)) {
709
- const actionLine = filter.action != null ? `<action android:name="${filter.action}"/>` : "";
710
- const categoryLine =
711
- filter.category != null ? `<category android:name="${filter.category}"/>` : "";
712
-
713
- content = content.replace(
714
- /(<activity[\s\S]*?android:name="\.MainActivity"[\s\S]*?>)/,
715
- `$1
716
- <intent-filter>
717
- ${actionLine}
718
- ${categoryLine}
719
- </intent-filter>`,
720
- );
721
- }
722
- }
723
-
724
- await fsx.write(manifestPath, content);
725
- }
726
-
727
- /**
728
- * Modify build.gradle (F3: add error handling)
729
- */
730
- private async _configureAndroidBuildGradle(androidPath: string): Promise<void> {
731
- const buildGradlePath = path.resolve(androidPath, "app/build.gradle");
732
-
733
- // F3: Check if file exists
734
- if (!(await fsx.exists(buildGradlePath))) {
735
- throw new Error(`build.gradle file not found: ${buildGradlePath}`);
736
- }
737
-
738
- let content = await fsx.read(buildGradlePath);
739
-
740
- // Configure versionName and versionCode
741
- const version = this._npmConfig.version;
742
- const cleanVersion = version.replace(/-.*$/, "");
743
- const versionParts = cleanVersion.split(".");
744
- const versionCode =
745
- parseInt(versionParts[0] ?? "0") * 10000 +
746
- parseInt(versionParts[1] ?? "0") * 100 +
747
- parseInt(versionParts[2] ?? "0");
748
-
749
- content = content.replace(/versionCode \d+/, `versionCode ${versionCode}`);
750
- content = content.replace(/versionName "[^"]+"/, `versionName "${version}"`);
751
-
752
- // Configure SDK version
753
- if (this._config.platform?.android?.sdkVersion != null) {
754
- const sdkVersion = this._config.platform.android.sdkVersion;
755
- content = content.replace(/minSdkVersion .+/, `minSdkVersion ${sdkVersion}`);
756
- content = content.replace(/targetSdkVersion .+/, `targetSdkVersion ${sdkVersion}`);
757
- } else {
758
- content = content.replace(/minSdkVersion .+/, `minSdkVersion rootProject.ext.minSdkVersion`);
759
- content = content.replace(
760
- /targetSdkVersion .+/,
761
- `targetSdkVersion rootProject.ext.targetSdkVersion`,
762
- );
763
- }
764
-
765
- // Configure signing
766
- const keystorePath = path.resolve(this._capPath, Capacitor._ANDROID_KEYSTORE_FILE_NAME);
767
- const signConfig = this._config.platform?.android?.sign;
768
- if (signConfig) {
769
- const keystoreSource = path.resolve(this._pkgPath, signConfig.keystore);
770
- // F3: Check if keystore file exists
771
- if (!(await fsx.exists(keystoreSource))) {
772
- throw new Error(`keystore file not found: ${keystoreSource}`);
773
- }
774
- await fsx.copy(keystoreSource, keystorePath);
775
-
776
- // F9: Convert relative path to forward slash
777
- const keystoreRelativePath = path
778
- .relative(path.dirname(buildGradlePath), keystorePath)
779
- .replace(/\\/g, "/");
780
- const keystoreType = signConfig.keystoreType ?? "jks";
781
-
782
- if (!content.includes("signingConfigs")) {
783
- const signingConfigsBlock = `
784
- signingConfigs {
785
- release {
786
- storeFile file("${keystoreRelativePath}")
787
- storePassword '${Capacitor._escapeGroovyString(signConfig.storePassword)}'
788
- keyAlias '${Capacitor._escapeGroovyString(signConfig.alias)}'
789
- keyPassword '${Capacitor._escapeGroovyString(signConfig.password)}'
790
- storeType "${keystoreType}"
791
- }
792
- }
793
- `;
794
- content = content.replace(/(android\s*\{)/, (match) => `${match}${signingConfigsBlock}`);
795
- }
796
-
797
- if (!content.includes("signingConfig signingConfigs.release")) {
798
- content = content.replace(
799
- /(buildTypes\s*\{[\s\S]*?release\s*\{)/,
800
- `$1\n signingConfig signingConfigs.release`,
801
- );
802
- }
803
- } else {
804
- await fsx.rm(keystorePath);
805
- }
806
-
807
- await fsx.write(buildGradlePath, content);
808
- }
809
-
810
- /**
811
- * Escape single quotes in Groovy string (for use in single-quoted strings)
812
- * Groovy: 'string with \'single quote\' inside'
813
- */
814
- private static _escapeGroovyString(value: string): string {
815
- return value.replace(/'/g, "\\'");
816
- }
817
-
818
- //#endregion
819
-
820
- //#region Private - Build
821
-
822
- /**
823
- * Build Android
824
- */
825
- private async _buildAndroid(outPath: string, buildType: string): Promise<void> {
826
- const androidPath = path.resolve(this._capPath, "android");
827
- const targetOutPath = path.resolve(outPath, "android");
828
-
829
- const isBundle = this._config.platform?.android?.bundle;
830
- const gradleTask =
831
- buildType === "release" ? (isBundle ? "bundleRelease" : "assembleRelease") : "assembleDebug";
832
-
833
- // Execute Gradle build (cross-platform)
834
- // F9: Run via cmd.exe on Windows (because shell: false)
835
- if (process.platform === "win32") {
836
- await this._exec("cmd", ["/c", "gradlew.bat", gradleTask, "--no-daemon"], androidPath);
837
- } else {
838
- await this._exec("sh", ["./gradlew", gradleTask, "--no-daemon"], androidPath);
839
- }
840
-
841
- // Copy build output
842
- await this._copyAndroidBuildOutput(androidPath, targetOutPath, buildType);
843
- }
844
-
845
- /**
846
- * Copy Android build output
847
- */
848
- private async _copyAndroidBuildOutput(
849
- androidPath: string,
850
- targetOutPath: string,
851
- buildType: string,
852
- ): Promise<void> {
853
- const isBundle = this._config.platform?.android?.bundle;
854
- const isSigned = Boolean(this._config.platform?.android?.sign);
855
-
856
- const ext = isBundle ? "aab" : "apk";
857
- const outputType = isBundle ? "bundle" : "apk";
858
- const fileName = isSigned ? `app-${buildType}.${ext}` : `app-${buildType}-unsigned.${ext}`;
859
-
860
- const sourcePath = path.resolve(
861
- androidPath,
862
- "app/build/outputs",
863
- outputType,
864
- buildType,
865
- fileName,
866
- );
867
-
868
- const actualPath = (await fsx.exists(sourcePath))
869
- ? sourcePath
870
- : path.resolve(
871
- androidPath,
872
- "app/build/outputs",
873
- outputType,
874
- buildType,
875
- `app-${buildType}.${ext}`,
876
- );
877
-
878
- if (!(await fsx.exists(actualPath))) {
879
- Capacitor._logger.warn(`build output not found: ${actualPath}`);
880
- return;
881
- }
882
-
883
- const outputFileName = `${this._config.appName}${isSigned ? "" : "-unsigned"}-latest.${ext}`;
884
-
885
- await fsx.mkdir(targetOutPath);
886
- await fsx.copy(actualPath, path.resolve(targetOutPath, outputFileName));
887
-
888
- // Save per-version
889
- const updatesPath = path.resolve(targetOutPath, "updates");
890
- await fsx.mkdir(updatesPath);
891
- await fsx.copy(actualPath, path.resolve(updatesPath, `${this._npmConfig.version}.${ext}`));
892
- }
893
-
894
- //#endregion
895
-
896
- //#region Private - Device Execution
897
-
898
- /**
899
- * Update server.url in capacitor.config.ts
900
- */
901
- private async _updateServerUrl(url: string): Promise<void> {
902
- const configPath = path.resolve(this._capPath, "capacitor.config.ts");
903
-
904
- if (!(await fsx.exists(configPath))) return;
905
-
906
- let content = await fsx.read(configPath);
907
-
908
- if (content.includes("url:")) {
909
- content = content.replace(/url:\s*"[^"]*"/, `url: "${url}"`);
910
- } else if (content.includes("server:")) {
911
- content = content.replace(/server:\s*\{/, `server: {\n url: "${url}",`);
912
- }
913
-
914
- await fsx.write(configPath, content);
915
- }
916
-
917
- //#endregion
918
-
919
- //#region Private - Utilities
920
-
921
- /**
922
- * Convert string to PascalCase
923
- */
924
- private _toPascalCase(str: string): string {
925
- return str
926
- .replace(/[-_](.)/g, (_, c: string) => c.toUpperCase())
927
- .replace(/^./, (c) => c.toUpperCase());
928
- }
929
-
930
- //#endregion
931
- }