@simplysm/sd-cli 14.1.9 → 14.1.10

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 (260) hide show
  1. package/dist/capacitor/capacitor-icon.js +2 -2
  2. package/dist/capacitor/capacitor-icon.js.map +1 -1
  3. package/dist/capacitor/capacitor-npm-config.d.ts +1 -1
  4. package/dist/capacitor/capacitor-npm-config.d.ts.map +1 -1
  5. package/dist/capacitor/capacitor-npm-config.js +11 -18
  6. package/dist/capacitor/capacitor-npm-config.js.map +1 -1
  7. package/dist/capacitor/capacitor.d.ts +1 -1
  8. package/dist/capacitor/capacitor.js +2 -2
  9. package/dist/capacitor/capacitor.js.map +1 -1
  10. package/dist/commands/check.d.ts.map +1 -1
  11. package/dist/commands/check.js +1 -0
  12. package/dist/commands/check.js.map +1 -1
  13. package/dist/commands/device.js +2 -2
  14. package/dist/commands/device.js.map +1 -1
  15. package/dist/commands/init/generators/root.d.ts.map +1 -1
  16. package/dist/commands/init/generators/root.js +0 -1
  17. package/dist/commands/init/generators/root.js.map +1 -1
  18. package/dist/commands/init/init-client.js +1 -1
  19. package/dist/commands/init/init-client.js.map +1 -1
  20. package/dist/commands/init/init.js +2 -2
  21. package/dist/commands/init/init.js.map +1 -1
  22. package/dist/commands/publish/deployment-phase.d.ts.map +1 -1
  23. package/dist/commands/publish/deployment-phase.js +1 -0
  24. package/dist/commands/publish/deployment-phase.js.map +1 -1
  25. package/dist/commands/publish/npm-publisher.js +3 -3
  26. package/dist/commands/publish/npm-publisher.js.map +1 -1
  27. package/dist/commands/publish/post-publish-phase.d.ts.map +1 -1
  28. package/dist/commands/publish/post-publish-phase.js +1 -0
  29. package/dist/commands/publish/post-publish-phase.js.map +1 -1
  30. package/dist/commands/publish/publish-command.d.ts.map +1 -1
  31. package/dist/commands/publish/publish-command.js +7 -12
  32. package/dist/commands/publish/publish-command.js.map +1 -1
  33. package/dist/deps/replace-deps/collect-deps.js +4 -4
  34. package/dist/deps/replace-deps/collect-deps.js.map +1 -1
  35. package/dist/deps/replace-deps/replace-deps-resolve.d.ts +4 -12
  36. package/dist/deps/replace-deps/replace-deps-resolve.d.ts.map +1 -1
  37. package/dist/deps/replace-deps/replace-deps-resolve.js +13 -49
  38. package/dist/deps/replace-deps/replace-deps-resolve.js.map +1 -1
  39. package/dist/deps/replace-deps/replace-deps.d.ts +2 -2
  40. package/dist/deps/replace-deps/replace-deps.js +3 -3
  41. package/dist/deps/server-externals/server-production-files.d.ts +3 -3
  42. package/dist/deps/server-externals/server-production-files.d.ts.map +1 -1
  43. package/dist/deps/server-externals/server-production-files.js +24 -17
  44. package/dist/deps/server-externals/server-production-files.js.map +1 -1
  45. package/dist/electron/electron.d.ts.map +1 -1
  46. package/dist/electron/electron.js +6 -11
  47. package/dist/electron/electron.js.map +1 -1
  48. package/dist/engines/BaseEngine.d.ts +1 -0
  49. package/dist/engines/BaseEngine.d.ts.map +1 -1
  50. package/dist/engines/BaseEngine.js +2 -1
  51. package/dist/engines/BaseEngine.js.map +1 -1
  52. package/dist/esbuild/esbuild-config.d.ts +6 -2
  53. package/dist/esbuild/esbuild-config.d.ts.map +1 -1
  54. package/dist/esbuild/esbuild-config.js +4 -3
  55. package/dist/esbuild/esbuild-config.js.map +1 -1
  56. package/dist/esbuild/esbuild-tsc-plugin.d.ts.map +1 -1
  57. package/dist/esbuild/esbuild-tsc-plugin.js +3 -1
  58. package/dist/esbuild/esbuild-tsc-plugin.js.map +1 -1
  59. package/dist/orchestrators/BaseOrchestrator.d.ts.map +1 -1
  60. package/dist/orchestrators/BaseOrchestrator.js +1 -0
  61. package/dist/orchestrators/BaseOrchestrator.js.map +1 -1
  62. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  63. package/dist/orchestrators/BuildOrchestrator.js +3 -5
  64. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  65. package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
  66. package/dist/orchestrators/DevOrchestrator.js +1 -0
  67. package/dist/orchestrators/DevOrchestrator.js.map +1 -1
  68. package/dist/orchestrators/ServerRuntimeManager.d.ts.map +1 -1
  69. package/dist/orchestrators/ServerRuntimeManager.js +4 -0
  70. package/dist/orchestrators/ServerRuntimeManager.js.map +1 -1
  71. package/dist/orchestrators/TypecheckOrchestrator.d.ts.map +1 -1
  72. package/dist/orchestrators/TypecheckOrchestrator.js +4 -6
  73. package/dist/orchestrators/TypecheckOrchestrator.js.map +1 -1
  74. package/dist/runtime/engine-watch-events.d.ts.map +1 -1
  75. package/dist/runtime/engine-watch-events.js +5 -0
  76. package/dist/runtime/engine-watch-events.js.map +1 -1
  77. package/dist/runtime/worker-events.d.ts +1 -0
  78. package/dist/runtime/worker-events.d.ts.map +1 -1
  79. package/dist/sd-cli-entry.d.ts.map +1 -1
  80. package/dist/sd-cli-entry.js +0 -4
  81. package/dist/sd-cli-entry.js.map +1 -1
  82. package/dist/sd-cli.js +2 -0
  83. package/dist/sd-cli.js.map +1 -1
  84. package/dist/typecheck/typecheck-non-package.d.ts.map +1 -1
  85. package/dist/typecheck/typecheck-non-package.js +10 -0
  86. package/dist/typecheck/typecheck-non-package.js.map +1 -1
  87. package/dist/utils/package-utils.d.ts +8 -6
  88. package/dist/utils/package-utils.d.ts.map +1 -1
  89. package/dist/utils/package-utils.js +26 -24
  90. package/dist/utils/package-utils.js.map +1 -1
  91. package/dist/utils/workspace-utils.d.ts +17 -0
  92. package/dist/utils/workspace-utils.d.ts.map +1 -0
  93. package/dist/utils/workspace-utils.js +95 -0
  94. package/dist/utils/workspace-utils.js.map +1 -0
  95. package/dist/workers/client.worker.d.ts +1 -0
  96. package/dist/workers/client.worker.d.ts.map +1 -1
  97. package/dist/workers/client.worker.js +8 -3
  98. package/dist/workers/client.worker.js.map +1 -1
  99. package/dist/workers/library-build.worker.d.ts +1 -0
  100. package/dist/workers/library-build.worker.d.ts.map +1 -1
  101. package/dist/workers/library-build.worker.js +3 -2
  102. package/dist/workers/library-build.worker.js.map +1 -1
  103. package/dist/workers/server-build.worker.d.ts +1 -0
  104. package/dist/workers/server-build.worker.d.ts.map +1 -1
  105. package/dist/workers/server-build.worker.js +12 -12
  106. package/dist/workers/server-build.worker.js.map +1 -1
  107. package/dist/workers/server-esbuild-context.d.ts.map +1 -1
  108. package/dist/workers/server-esbuild-context.js +4 -2
  109. package/dist/workers/server-esbuild-context.js.map +1 -1
  110. package/dist/workers/server-runtime.worker.d.ts +1 -0
  111. package/dist/workers/server-runtime.worker.d.ts.map +1 -1
  112. package/dist/workers/server-runtime.worker.js +9 -3
  113. package/dist/workers/server-runtime.worker.js.map +1 -1
  114. package/dist/workers/server-watch-manager.d.ts +1 -1
  115. package/dist/workers/server-watch-manager.d.ts.map +1 -1
  116. package/dist/workers/server-watch-manager.js +2 -2
  117. package/dist/workers/server-watch-manager.js.map +1 -1
  118. package/package.json +11 -11
  119. package/src/capacitor/capacitor-icon.ts +2 -2
  120. package/src/capacitor/capacitor-npm-config.ts +10 -19
  121. package/src/capacitor/capacitor.ts +2 -2
  122. package/src/commands/check.ts +1 -0
  123. package/src/commands/device.ts +2 -2
  124. package/src/commands/init/generators/root.ts +0 -1
  125. package/src/commands/init/init-client.ts +1 -1
  126. package/src/commands/init/init.ts +2 -2
  127. package/src/commands/init/templates/workspace-root/mise.toml.hbs +1 -1
  128. package/src/commands/publish/deployment-phase.ts +1 -0
  129. package/src/commands/publish/npm-publisher.ts +3 -3
  130. package/src/commands/publish/post-publish-phase.ts +1 -0
  131. package/src/commands/publish/publish-command.ts +7 -15
  132. package/src/deps/replace-deps/collect-deps.ts +4 -4
  133. package/src/deps/replace-deps/replace-deps-resolve.ts +13 -56
  134. package/src/deps/replace-deps/replace-deps.ts +3 -3
  135. package/src/deps/server-externals/server-production-files.ts +31 -18
  136. package/src/electron/electron.ts +7 -13
  137. package/src/engines/BaseEngine.ts +3 -2
  138. package/src/esbuild/esbuild-config.ts +12 -3
  139. package/src/esbuild/esbuild-tsc-plugin.ts +4 -1
  140. package/src/orchestrators/BaseOrchestrator.ts +1 -0
  141. package/src/orchestrators/BuildOrchestrator.ts +3 -5
  142. package/src/orchestrators/DevOrchestrator.ts +1 -0
  143. package/src/orchestrators/ServerRuntimeManager.ts +4 -0
  144. package/src/orchestrators/TypecheckOrchestrator.ts +4 -6
  145. package/src/runtime/engine-watch-events.ts +7 -1
  146. package/src/runtime/worker-events.ts +1 -0
  147. package/src/sd-cli-entry.ts +0 -9
  148. package/src/sd-cli.ts +2 -0
  149. package/src/typecheck/typecheck-non-package.ts +11 -0
  150. package/src/utils/package-utils.ts +30 -23
  151. package/src/utils/workspace-utils.ts +117 -0
  152. package/src/workers/client.worker.ts +9 -4
  153. package/src/workers/library-build.worker.ts +4 -3
  154. package/src/workers/server-build.worker.ts +13 -13
  155. package/src/workers/server-esbuild-context.ts +5 -2
  156. package/src/workers/server-runtime.worker.ts +10 -3
  157. package/src/workers/server-watch-manager.ts +3 -3
  158. package/tests/capacitor/capacitor-build.spec.ts +142 -142
  159. package/tests/capacitor/capacitor-init.spec.ts +181 -181
  160. package/tests/capacitor/capacitor-npm-config.acc.spec.ts +114 -114
  161. package/tests/capacitor/capacitor-npm-config.spec.ts +94 -94
  162. package/tests/commands/publish-manifest.acc.spec.ts +67 -0
  163. package/tests/deps/replace-deps/collect-deps.acc.spec.ts +16 -1
  164. package/tests/deps/replace-deps/replace-deps-resolve.acc.spec.ts +9 -5
  165. package/tests/deps/replace-deps/replace-deps-setup.acc.spec.ts +3 -3
  166. package/tests/deps/server-externals/server-production-files.spec.ts +68 -0
  167. package/tests/electron/electron.spec.ts +608 -608
  168. package/tests/ts-compiler/fixtures/non-angular-pkg/.cache/typecheck-browser.tsbuildinfo +1 -1
  169. package/tests/ts-compiler/fixtures/non-angular-pkg/.cache/typecheck-node.tsbuildinfo +1 -1
  170. package/tests/ts-compiler/fixtures/non-angular-pkg/.cache/typecheck.tsbuildinfo +1 -1
  171. package/tests/utils/engine-watch-events.spec.ts +17 -0
  172. package/tests/utils/esbuild-config.spec.ts +15 -0
  173. package/tests/utils/package-utils.spec.ts +36 -4
  174. package/tests/utils/replace-deps-watch.acc.spec.ts +4 -4
  175. package/tests/utils/replace-deps-watch.spec.ts +3 -3
  176. package/tests/utils/replace-deps.spec.ts +1 -35
  177. package/tests/utils/workspace-utils.spec.ts +87 -0
  178. package/tests/workers/library-build-worker.spec.ts +1 -1
  179. package/tests/workers/server-esbuild-context.spec.ts +4 -3
  180. package/dist/commands/reinstall.d.ts +0 -13
  181. package/dist/commands/reinstall.d.ts.map +0 -1
  182. package/dist/commands/reinstall.js +0 -56
  183. package/dist/commands/reinstall.js.map +0 -1
  184. package/src/commands/init/templates/workspace-root/pnpm-workspace.yaml +0 -5
  185. package/src/commands/reinstall.ts +0 -63
  186. package/tests/angular/angular-compiler-hmr-removal.verify.md +0 -16
  187. package/tests/angular/onbuild-lint-removal.verify.md +0 -8
  188. package/tests/angular/vite-angular-plugin-sdtscompiler.verify.md +0 -13
  189. package/tests/angular/vite-angular-plugin-vitest.verify.md +0 -20
  190. package/tests/capacitor/capacitor-android-exports.verify.md +0 -11
  191. package/tests/commands/publish-npm-local-split.verify.md +0 -9
  192. package/tests/commands/publish-responsibility-split.verify.md +0 -13
  193. package/tests/commands/publish-set.verify.md +0 -7
  194. package/tests/commands/publish-storage-split.verify.md +0 -8
  195. package/tests/commands/slice3-severity-cleanup.verify.md +0 -12
  196. package/tests/deps/deps-directory-separation.verify.md +0 -15
  197. package/tests/deps/replace-deps/replace-deps-perf.verify.md +0 -15
  198. package/tests/deps/server-externals/mise-toml-parse-intent.verify.md +0 -16
  199. package/tests/electron/electron-symlink-cleanup.verify.md +0 -8
  200. package/tests/engines/engine-duplicate-output-removal.verify.md +0 -10
  201. package/tests/engines/engine-typecheck-selection.verify.md +0 -8
  202. package/tests/engines/esbuild-client-engine.verify.md +0 -15
  203. package/tests/engines/normalize-result.verify.md +0 -9
  204. package/tests/engines/vite-dependency-cleanup.verify.md +0 -24
  205. package/tests/esbuild/esbuild-angular-compiler-plugin-hmr.verify.md +0 -23
  206. package/tests/esbuild/esbuild-angular-compiler-plugin-onload.verify.md +0 -21
  207. package/tests/esbuild/esbuild-angular-compiler-plugin-onstart-extraction.verify.md +0 -16
  208. package/tests/esbuild/esbuild-angular-compiler-plugin-sdtscompiler.verify.md +0 -15
  209. package/tests/esbuild/esbuild-angular-compiler-plugin-stylesheet.verify.md +0 -31
  210. package/tests/esbuild/esbuild-angular-compiler-plugin-worker.verify.md +0 -59
  211. package/tests/esbuild/esbuild-angular-compiler-plugin.verify.md +0 -21
  212. package/tests/esbuild/esbuild-postcss-plugin-chunking.verify.md +0 -17
  213. package/tests/esbuild/esbuild-tsc-plugin-imports.verify.md +0 -13
  214. package/tests/esbuild/esbuild-worker-plugin-node.verify.md +0 -12
  215. package/tests/esbuild/esbuild-worker-plugin.verify.md +0 -7
  216. package/tests/orchestrators/dist-delete-watcher.verify.md +0 -10
  217. package/tests/orchestrators/orchestrator-baseenv.verify.md +0 -10
  218. package/tests/orchestrators/orchestrator-diagnostic-formatting.verify.md +0 -10
  219. package/tests/orchestrators/orchestrator-initializemode-signature.verify.md +0 -9
  220. package/tests/orchestrators/slice1-stdout-to-consola.verify.md +0 -10
  221. package/tests/sd-cli-catch-all.verify.md +0 -7
  222. package/tests/sd-cli-log-tag.verify.md +0 -11
  223. package/tests/ts-compiler/SdTsCompiler-affected-files.verify.md +0 -8
  224. package/tests/ts-compiler/SdTsCompiler-crash-handling.verify.md +0 -24
  225. package/tests/ts-compiler/SdTsCompiler-diagnostics.verify.md +0 -12
  226. package/tests/ts-compiler/SdTsCompiler-emit.verify.md +0 -9
  227. package/tests/ts-compiler/SdTsCompiler.verify.md +0 -41
  228. package/tests/ts-compiler/scss-lint-integration.verify.md +0 -14
  229. package/tests/utils/copy-public-outdir.verify.md +0 -8
  230. package/tests/utils/dev-http-server.verify.md +0 -8
  231. package/tests/utils/engine-watch-events.verify.md +0 -17
  232. package/tests/utils/esbuild-client-config-integration.verify.md +0 -9
  233. package/tests/utils/esbuild-client-config-postcss.verify.md +0 -6
  234. package/tests/utils/esbuild-client-config.verify.md +0 -26
  235. package/tests/utils/esbuild-index-html.verify.md +0 -10
  236. package/tests/utils/esbuild-pwa.verify.md +0 -9
  237. package/tests/utils/esbuild-scss-plugin.verify.md +0 -8
  238. package/tests/utils/hmr-service.verify.md +0 -17
  239. package/tests/utils/lint-core-import-paths.verify.md +0 -10
  240. package/tests/utils/replace-deps-split.verify.md +0 -15
  241. package/tests/utils/replace-deps-watch.verify.md +0 -9
  242. package/tests/utils/server-production-files-import-paths.verify.md +0 -14
  243. package/tests/utils/vite-config-cleanup.verify.md +0 -7
  244. package/tests/workers/build-watch-paths-library.verify.md +0 -10
  245. package/tests/workers/build-watch-paths-ngtsc-server.verify.md +0 -12
  246. package/tests/workers/client-worker-browser-support.verify.md +0 -7
  247. package/tests/workers/client-worker-cleanup.verify.md +0 -8
  248. package/tests/workers/client-worker-initial-build-error.verify.md +0 -7
  249. package/tests/workers/client-worker-initial-build-warnings.verify.md +0 -7
  250. package/tests/workers/client-worker-mtime-incremental.verify.md +0 -10
  251. package/tests/workers/client-worker-onend-sync.verify.md +0 -7
  252. package/tests/workers/client-worker-refactor.verify.md +0 -22
  253. package/tests/workers/client-worker-ts-cache-invalidation.verify.md +0 -12
  254. package/tests/workers/dev-port-file.verify.md +0 -6
  255. package/tests/workers/ngtsc-build-rootnames-refresh.verify.md +0 -8
  256. package/tests/workers/server-build-context-dispose.verify.md +0 -8
  257. package/tests/workers/server-build-worker-plugin.verify.md +0 -9
  258. package/tests/workers/server-build-worker-refactoring.verify.md +0 -14
  259. package/tests/workers/server-esbuild-context-integration.verify.md +0 -10
  260. package/tests/workers/server-esbuild-context-tsc.verify.md +0 -7
@@ -0,0 +1,117 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { globSync } from "glob";
4
+ import { pathx } from "@simplysm/core-node";
5
+
6
+ export interface WorkspacePackageDir {
7
+ dirName: string;
8
+ packageName: string;
9
+ relPath: string;
10
+ absPath: string;
11
+ }
12
+
13
+ interface WorkspaceRootPackageJson {
14
+ workspaces?: unknown;
15
+ }
16
+
17
+ interface WorkspacePackageJson {
18
+ name?: unknown;
19
+ }
20
+
21
+ /**
22
+ * root package.json의 workspaces 값을 Bun workspace 패턴 배열로 정규화한다.
23
+ */
24
+ export function parsePackageJsonWorkspaces(workspaces: unknown): string[] {
25
+ const rawPatterns = Array.isArray(workspaces)
26
+ ? workspaces
27
+ : typeof workspaces === "object" && workspaces != null && Array.isArray((workspaces as { packages?: unknown }).packages)
28
+ ? (workspaces as { packages: unknown[] }).packages
29
+ : [];
30
+
31
+ return rawPatterns
32
+ .filter((item): item is string => typeof item === "string")
33
+ .map((item) => item.trim())
34
+ .filter((item) => item !== "");
35
+ }
36
+
37
+ export function readWorkspacePatterns(workspaceRoot: string): string[] {
38
+ const packageJsonPath = path.join(workspaceRoot, "package.json");
39
+ if (!fs.existsSync(packageJsonPath)) return [];
40
+
41
+ const pkgJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as WorkspaceRootPackageJson;
42
+ return parsePackageJsonWorkspaces(pkgJson.workspaces);
43
+ }
44
+
45
+ export function findWorkspaceRoot(startDir: string): string | undefined {
46
+ let current = path.resolve(startDir);
47
+ if (fs.existsSync(current) && fs.statSync(current).isFile()) {
48
+ current = path.dirname(current);
49
+ }
50
+
51
+ while (true) {
52
+ if (readWorkspacePatterns(current).length > 0) {
53
+ return pathx.posix(current);
54
+ }
55
+
56
+ const parent = path.dirname(current);
57
+ if (parent === current) return undefined;
58
+ current = parent;
59
+ }
60
+ }
61
+
62
+ function splitWorkspacePatterns(patterns: string[]): { include: string[]; exclude: string[] } {
63
+ const include: string[] = [];
64
+ const exclude: string[] = [];
65
+
66
+ for (const pattern of patterns) {
67
+ if (pattern.startsWith("!")) {
68
+ const excluded = pattern.slice(1).trim();
69
+ if (excluded !== "") exclude.push(excluded);
70
+ } else {
71
+ include.push(pattern);
72
+ }
73
+ }
74
+
75
+ return { include, exclude };
76
+ }
77
+
78
+ /**
79
+ * Bun workspace root의 package.json#workspaces 기준으로 실제 패키지 디렉터리를 수집한다.
80
+ */
81
+ export function collectWorkspacePackages(workspaceRoot: string): WorkspacePackageDir[] {
82
+ const root = pathx.posix(path.resolve(workspaceRoot));
83
+ const patterns = readWorkspacePatterns(root);
84
+ const { include, exclude } = splitWorkspacePatterns(patterns);
85
+ const absDirs = new Set<string>();
86
+
87
+ for (const pattern of include) {
88
+ const matches = globSync(pattern, {
89
+ cwd: root,
90
+ absolute: true,
91
+ nodir: false,
92
+ ignore: exclude,
93
+ });
94
+
95
+ for (const match of matches) {
96
+ const absPath = pathx.posix(path.resolve(match));
97
+ const packageJsonPath = path.join(absPath, "package.json");
98
+ if (!fs.existsSync(packageJsonPath)) continue;
99
+ if (!fs.statSync(absPath).isDirectory()) continue;
100
+ absDirs.add(absPath);
101
+ }
102
+ }
103
+
104
+ return [...absDirs]
105
+ .sort((a, b) => a.localeCompare(b))
106
+ .map((absPath): WorkspacePackageDir => {
107
+ const relPath = pathx.posix(path.relative(root, absPath));
108
+ const pkgJson = JSON.parse(fs.readFileSync(path.join(absPath, "package.json"), "utf-8")) as WorkspacePackageJson;
109
+ const packageName = typeof pkgJson.name === "string" ? pkgJson.name : path.basename(absPath);
110
+ return {
111
+ dirName: path.basename(absPath),
112
+ packageName,
113
+ relPath,
114
+ absPath,
115
+ };
116
+ });
117
+ }
@@ -57,7 +57,7 @@ export interface ClientWorkerEvents extends Record<string, unknown> {
57
57
  buildStart: Record<string, never>;
58
58
  build: ClientBuildResult;
59
59
  serverReady: { port: number };
60
- error: { message: string };
60
+ error: { message: string; stack?: string };
61
61
  }
62
62
 
63
63
  //#endregion
@@ -207,6 +207,7 @@ async function build(info: ClientBuildInfo): Promise<ClientBuildResult> {
207
207
  errors.push(errNs.message(err));
208
208
  }
209
209
  logger.debug(`[${info.name}] client worker build 예외: ${errors.join("\n")}`);
210
+ logger.debug(`[${info.name}] client worker build 예외 스택:\n${errNs.stack(err)}`);
210
211
  return { success: false, errors };
211
212
  }
212
213
  }
@@ -313,8 +314,10 @@ function createDevBuildEndHandler(
313
314
  }
314
315
  } catch (err) {
315
316
  const message = errNs.message(err);
317
+ const stack = errNs.stack(err);
318
+ logger.debug(`client dev build end 예외 스택:\n${stack}`);
316
319
  if (!isInitialBuild) {
317
- sender.send("error", { message });
320
+ sender.send("error", { message, stack });
318
321
  } else {
319
322
  isInitialBuild = false;
320
323
  initialBuildResolve?.({
@@ -423,7 +426,7 @@ async function startWatch(info: ClientBuildInfo): Promise<ClientBuildResult> {
423
426
  hmrService?.broadcast({ type: "full-reload" });
424
427
  sender.send("build", { success: true });
425
428
  } catch (err) {
426
- sender.send("error", { message: errNs.message(err) });
429
+ sender.send("error", { message: errNs.message(err), stack: errNs.stack(err) });
427
430
  }
428
431
  });
429
432
 
@@ -437,7 +440,9 @@ async function startWatch(info: ClientBuildInfo): Promise<ClientBuildResult> {
437
440
  return initialResult;
438
441
  } catch (err) {
439
442
  const message = errNs.message(err);
440
- sender.send("error", { message });
443
+ const stack = errNs.stack(err);
444
+ logger.debug(`[${info.name}] client worker startWatch 예외 스택:\n${stack}`);
445
+ sender.send("error", { message, stack });
441
446
  return { success: false, errors: [message] };
442
447
  }
443
448
  }
@@ -36,7 +36,7 @@ export interface CombinedBuildEvent {
36
36
  export interface LibraryBuildWorkerEvents extends Record<string, unknown> {
37
37
  buildStart: Record<string, never>;
38
38
  build: CombinedBuildEvent;
39
- error: { message: string };
39
+ error: { message: string; stack?: string };
40
40
  }
41
41
 
42
42
  //#endregion
@@ -135,6 +135,7 @@ async function build(info: LibraryBuildInfo): Promise<LibraryBuildResult> {
135
135
  } catch (err) {
136
136
  const message = errNs.message(err);
137
137
  logger.debug(`[${info.name}] library worker build 예외 발생: ${message}`);
138
+ logger.debug(`[${info.name}] library worker build 예외 스택:\n${errNs.stack(err)}`);
138
139
  return {
139
140
  build: { success: false, errors: [message], diagnostics: [] },
140
141
  };
@@ -323,11 +324,11 @@ async function startWatch(info: LibraryBuildInfo): Promise<void> {
323
324
  const event = buildWatchEvent(info, result, changedScssFiles);
324
325
  sender.send("build", event);
325
326
  } catch (err) {
326
- sender.send("error", { message: errNs.message(err) });
327
+ sender.send("error", { message: errNs.message(err), stack: errNs.stack(err) });
327
328
  }
328
329
  });
329
330
  } catch (err) {
330
- sender.send("error", { message: errNs.message(err) });
331
+ sender.send("error", { message: errNs.message(err), stack: errNs.stack(err) });
331
332
  }
332
333
  }
333
334
 
@@ -92,7 +92,7 @@ export interface ServerCombinedBuildEvent {
92
92
  export interface ServerBuildWorkerEvents extends Record<string, unknown> {
93
93
  buildStart: Record<string, never>;
94
94
  build: ServerCombinedBuildEvent;
95
- error: { message: string };
95
+ error: { message: string; stack?: string };
96
96
  }
97
97
 
98
98
  //#endregion
@@ -169,7 +169,7 @@ async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
169
169
  jsResult = await esbuild.build({ ...esbuildOptions, plugins: [createWorkerBundlePlugin(), tscPlugin.plugin] })
170
170
  .then(async (result) => {
171
171
  if (result.outputFiles) {
172
- await writeChangedOutputFiles(result.outputFiles);
172
+ await writeChangedOutputFiles(result.outputFiles, { rewriteJsExtensions: false });
173
173
  }
174
174
  const errors = formatEsbuildMessages(result.errors, "error");
175
175
  const warnings = formatEsbuildMessages(result.warnings, "warning");
@@ -179,11 +179,14 @@ async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
179
179
  warnings: warnings.length > 0 ? warnings : undefined,
180
180
  };
181
181
  })
182
- .catch((err) => ({
183
- success: false,
184
- errors: [errNs.message(err)],
185
- warnings: undefined,
186
- }));
182
+ .catch((err) => {
183
+ logger.debug(`[${info.name}] server esbuild build 예외 스택:\n${errNs.stack(err)}`);
184
+ return {
185
+ success: false,
186
+ errors: [errNs.message(err)],
187
+ warnings: undefined,
188
+ };
189
+ });
187
190
 
188
191
  tscErrors = tscPlugin.getErrors() ?? [];
189
192
  tscDiagnostics = tscPlugin.getDiagnostics();
@@ -231,11 +234,8 @@ async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
231
234
  };
232
235
  } catch (err) {
233
236
  const message = errNs.message(err);
234
- const stack = err instanceof Error ? err.stack : undefined;
235
237
  logger.debug(`[${info.name}] server worker build 예외: ${message}`);
236
- if (stack != null) {
237
- logger.debug(`[${info.name}] 스택 트레이스:\n${stack}`);
238
- }
238
+ logger.debug(`[${info.name}] 스택 트레이스:\n${errNs.stack(err)}`);
239
239
  return {
240
240
  build: { success: false, errors: [message], diagnostics: [] },
241
241
  mainJsPath,
@@ -358,11 +358,11 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
358
358
  initialExternals: cachedExternal,
359
359
  onBuildStart: () => sender.send("buildStart", {}),
360
360
  onBuild: (result) => sender.send("build", result),
361
- onError: (message) => sender.send("error", { message }),
361
+ onError: (message, stack) => sender.send("error", { message, stack }),
362
362
  rebuild: () => rebuildAll(),
363
363
  });
364
364
  } catch (err) {
365
- sender.send("error", { message: errNs.message(err) });
365
+ sender.send("error", { message: errNs.message(err), stack: errNs.stack(err) });
366
366
  }
367
367
  }
368
368
 
@@ -1,6 +1,6 @@
1
1
  import type ts from "typescript";
2
2
  import esbuild from "esbuild";
3
- import { err as errNs } from "@simplysm/core-common";
3
+ import { createLogger, err as errNs } from "@simplysm/core-common";
4
4
  import { formatEsbuildMessages } from "../utils/output-utils";
5
5
  import {
6
6
  createServerEsbuildOptions,
@@ -12,6 +12,8 @@ import type { SerializedDiagnostic } from "../typecheck/typecheck-serialization"
12
12
  import type { LintWithProgramResult } from "../lint/lint-with-program";
13
13
  import { createWorkerBundlePlugin } from "../esbuild/esbuild-worker-plugin";
14
14
 
15
+ const logger = createLogger("sd:cli:server-esbuild-context");
16
+
15
17
  /**
16
18
  * esbuild watch context 생성 옵션
17
19
  */
@@ -87,6 +89,7 @@ export async function rebuild(): Promise<{
87
89
  try {
88
90
  result = await context.rebuild();
89
91
  } catch (err) {
92
+ logger.debug(`server esbuild rebuild 예외 스택:\n${errNs.stack(err)}`);
90
93
  const tscErrors = tscPlugin?.getErrors() ?? [];
91
94
  const allErrors = [errNs.message(err), ...tscErrors];
92
95
  return {
@@ -101,7 +104,7 @@ export async function rebuild(): Promise<{
101
104
  }
102
105
 
103
106
  if (result.outputFiles) {
104
- await writeChangedOutputFiles(result.outputFiles);
107
+ await writeChangedOutputFiles(result.outputFiles, { rewriteJsExtensions: false });
105
108
  }
106
109
 
107
110
  const esbuildErrors = formatEsbuildMessages(result.errors, "error");
@@ -32,6 +32,7 @@ export interface ServerRuntimeReadyEvent {
32
32
  */
33
33
  export interface ServerRuntimeErrorEvent {
34
34
  message: string;
35
+ stack?: string;
35
36
  }
36
37
 
37
38
  /**
@@ -73,18 +74,22 @@ async function cleanup(): Promise<void> {
73
74
  // 서버 listen() 이후 발생하는 런타임 에러를 잡아 커스텀 "error" 이벤트로 전송
74
75
  // (이 핸들러 없이는 워커가 크래시해도 빌드 Promise가 resolve되지 않아 프로세스가 중단될 수 있다)
75
76
  process.on("uncaughtException", (err) => {
76
- logger.error("서버 런타임 미처리 에러", err);
77
+ logger.error("서버 런타임 미처리 에러", errNs.message(err));
78
+ logger.debug(`서버 런타임 미처리 에러 스택:\n${errNs.stack(err)}`);
77
79
  sender.send("error", {
78
80
  message: errNs.message(err),
81
+ stack: errNs.stack(err),
79
82
  });
80
83
  // 이벤트 전송 후 종료할 수 있도록 대기
81
84
  setTimeout(() => process.exit(1), 500);
82
85
  });
83
86
 
84
87
  process.on("unhandledRejection", (reason) => {
85
- logger.error("서버 런타임 미처리 Promise 거부", reason);
88
+ logger.error("서버 런타임 미처리 Promise 거부", errNs.message(reason));
89
+ logger.debug(`서버 런타임 미처리 Promise 거부 스택:\n${errNs.stack(reason)}`);
86
90
  sender.send("error", {
87
91
  message: errNs.message(reason),
92
+ stack: errNs.stack(reason),
88
93
  });
89
94
  // 이벤트 전송 후 종료할 수 있도록 대기
90
95
  setTimeout(() => process.exit(1), 500);
@@ -195,9 +200,11 @@ async function start(info: ServerRuntimeStartInfo): Promise<void> {
195
200
 
196
201
  sender.send("serverReady", { port: server.options.port });
197
202
  } catch (err) {
198
- logger.error("서버 런타임 시작 실패", err);
203
+ logger.error("서버 런타임 시작 실패", errNs.message(err));
204
+ logger.debug(`서버 런타임 시작 실패 스택:\n${errNs.stack(err)}`);
199
205
  sender.send("error", {
200
206
  message: errNs.message(err),
207
+ stack: errNs.stack(err),
201
208
  });
202
209
  }
203
210
  }
@@ -25,7 +25,7 @@ export interface ServerWatchLoopConfig {
25
25
  /** 빌드 완료 이벤트 콜백 */
26
26
  onBuild: (result: { build: { success: boolean; errors?: string[]; warnings?: string[] }; mainJsPath: string }) => void;
27
27
  /** 에러 이벤트 콜백 */
28
- onError: (message: string) => void;
28
+ onError: (message: string, stack?: string) => void;
29
29
  /** 리빌드 실행 콜백 */
30
30
  rebuild: () => Promise<{ build: { success: boolean; errors?: string[]; warnings?: string[] }; mainJsPath: string }>;
31
31
  }
@@ -99,7 +99,7 @@ export async function startServerWatchLoop(config: ServerWatchLoopConfig): Promi
99
99
 
100
100
  const hasRelevantChange = changes.some((c) => {
101
101
  if (metafileAbsPaths.has(c.path)) return true;
102
- // pnpm symlink 경로와 esbuild resolved 경로 불일치 대응
102
+ // package manager symlink 경로와 esbuild resolved 경로 불일치 대응
103
103
  try {
104
104
  const realPath = pathx.posix(fs.realpathSync(c.path));
105
105
  return metafileAbsPaths.has(realPath);
@@ -116,7 +116,7 @@ export async function startServerWatchLoop(config: ServerWatchLoopConfig): Promi
116
116
  logger.debug("변경된 파일이 빌드에 포함되지 않아 리빌드 건너뜀");
117
117
  }
118
118
  } catch (err) {
119
- config.onError(errNs.message(err));
119
+ config.onError(errNs.message(err), errNs.stack(err));
120
120
  }
121
121
  });
122
122
 
@@ -1,142 +1,142 @@
1
- /* eslint-disable no-restricted-properties -- 테스트 환경변수 조작 필요 */
2
- import { describe, it, expect, vi, beforeAll, afterAll, beforeEach, afterEach } from "vitest";
3
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import path from "node:path";
6
- import { fsx, cpx } from "@simplysm/core-node";
7
-
8
- // sharp는 외부 npm 네이티브 라이브러리로 이미지 처리 시간이 크고 결정적 검증에 mock 필요
9
- vi.mock("sharp", () => ({
10
- default: vi.fn().mockReturnValue({
11
- resize: vi.fn().mockReturnThis(),
12
- composite: vi.fn().mockReturnThis(),
13
- png: vi.fn().mockReturnThis(),
14
- toBuffer: vi.fn().mockResolvedValue(new Uint8Array([0])),
15
- toFile: vi.fn().mockResolvedValue(undefined),
16
- }),
17
- }));
18
-
19
- const mockFsxExists = vi.spyOn(fsx, "exists");
20
- const mockFsxRead = vi.spyOn(fsx, "read");
21
- vi.spyOn(fsx, "write").mockResolvedValue(undefined);
22
- const mockFsxReadJson = vi.spyOn(fsx, "readJson");
23
- vi.spyOn(fsx, "writeJson").mockResolvedValue(undefined);
24
- vi.spyOn(fsx, "mkdir").mockResolvedValue(undefined);
25
- vi.spyOn(fsx, "rm").mockResolvedValue(undefined);
26
- const mockFsxGlob = vi.spyOn(fsx, "glob").mockResolvedValue([]);
27
- vi.spyOn(fsx, "copy").mockResolvedValue(undefined);
28
-
29
- vi.spyOn(cpx, "spawn").mockResolvedValue({ stdout: "", stderr: "", exitCode: 0 });
30
- vi.spyOn(cpx, "spawnSync").mockReturnValue({ stdout: "", stderr: "", exitCode: 0 });
31
-
32
- let tmpRoot: string;
33
- let PKG_PATH: string;
34
-
35
- beforeAll(() => {
36
- tmpRoot = mkdtempSync(path.join(tmpdir(), "cap-build-"));
37
- writeFileSync(path.join(tmpRoot, "pnpm-workspace.yaml"), "");
38
- PKG_PATH = path.join(tmpRoot, "pkg");
39
- mkdirSync(path.join(PKG_PATH, ".capacitor"), { recursive: true });
40
- });
41
-
42
- afterAll(() => {
43
- rmSync(tmpRoot, { recursive: true, force: true });
44
- });
45
-
46
- function setupDefaultMocks() {
47
- mockFsxExists.mockResolvedValue(true);
48
-
49
- mockFsxReadJson.mockImplementation(((p: string) => {
50
- const normalized = p.replace(/\\/g, "/");
51
- if (normalized.includes(".capacitor/package.json")) {
52
- return {
53
- name: "com.test.app",
54
- version: "1.2.3",
55
- dependencies: {
56
- "@capacitor/core": "^7.0.0",
57
- "@capacitor/app": "^7.0.0",
58
- "@capacitor/android": "^7.0.0",
59
- },
60
- devDependencies: {
61
- "@capacitor/cli": "^7.0.0",
62
- "@capacitor/assets": "^3.0.0",
63
- },
64
- };
65
- }
66
- return { name: "test-pkg", version: "1.2.3" };
67
- }) as never);
68
-
69
- mockFsxRead.mockImplementation(((p: string) => {
70
- if (p.includes("AndroidManifest.xml")) {
71
- return '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n<application>\n</application>\n</manifest>';
72
- }
73
- if (p.includes("build.gradle")) {
74
- return `android {
75
- defaultConfig {
76
- versionCode 1
77
- versionName "1.0"
78
- minSdkVersion rootProject.ext.minSdkVersion
79
- targetSdkVersion rootProject.ext.targetSdkVersion
80
- }
81
- buildTypes { release { } }
82
- }`;
83
- }
84
- if (p.includes("gradle.properties")) {
85
- return "org.gradle.jvmargs=-Xmx2048m";
86
- }
87
- return "";
88
- }) as never);
89
-
90
- mockFsxGlob.mockImplementation(((pattern: string) => {
91
- if (pattern.includes("Corretto") || pattern.includes("jdk")) {
92
- return ["C:/Program Files/Amazon Corretto/jdk21.0.1"];
93
- }
94
- return [];
95
- }) as never);
96
-
97
- process.env["ANDROID_HOME"] = "C:/Android/Sdk";
98
- }
99
-
100
- let savedEnv: Record<string, string | undefined>;
101
- beforeEach(() => {
102
- savedEnv = { ...process.env };
103
- });
104
- afterEach(() => {
105
- process.env = savedEnv;
106
- });
107
-
108
- describe("Capacitor 빌드", () => {
109
- beforeEach(() => {
110
- vi.clearAllMocks();
111
- setupDefaultMocks();
112
- });
113
-
114
- describe("서명", () => {
115
- it("keystore 파일이 없으면 에러가 발생한다", async () => {
116
- const { Capacitor } = await import("../../src/capacitor/capacitor.js");
117
-
118
- // keystore 파일만 존재하지 않도록 설정
119
- mockFsxExists.mockImplementation(((p: string) => {
120
- if (p.includes("my-release.keystore")) return false;
121
- return true;
122
- }) as never);
123
-
124
- const cap = await Capacitor.create(PKG_PATH, {
125
- appId: "com.test.app",
126
- appName: "Test App",
127
- platform: {
128
- android: {
129
- sign: {
130
- keystore: "my-release.keystore",
131
- storePassword: "store123",
132
- alias: "my-key",
133
- password: "key123",
134
- },
135
- },
136
- },
137
- });
138
-
139
- await expect(cap.build("/fake/out")).rejects.toThrow("keystore");
140
- });
141
- });
142
- });
1
+ /* eslint-disable no-restricted-properties -- 테스트 환경변수 조작 필요 */
2
+ import { describe, it, expect, vi, beforeAll, afterAll, beforeEach, afterEach } from "vitest";
3
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import path from "node:path";
6
+ import { fsx, cpx } from "@simplysm/core-node";
7
+
8
+ // sharp는 외부 npm 네이티브 라이브러리로 이미지 처리 시간이 크고 결정적 검증에 mock 필요
9
+ vi.mock("sharp", () => ({
10
+ default: vi.fn().mockReturnValue({
11
+ resize: vi.fn().mockReturnThis(),
12
+ composite: vi.fn().mockReturnThis(),
13
+ png: vi.fn().mockReturnThis(),
14
+ toBuffer: vi.fn().mockResolvedValue(new Uint8Array([0])),
15
+ toFile: vi.fn().mockResolvedValue(undefined),
16
+ }),
17
+ }));
18
+
19
+ const mockFsxExists = vi.spyOn(fsx, "exists");
20
+ const mockFsxRead = vi.spyOn(fsx, "read");
21
+ vi.spyOn(fsx, "write").mockResolvedValue(undefined);
22
+ const mockFsxReadJson = vi.spyOn(fsx, "readJson");
23
+ vi.spyOn(fsx, "writeJson").mockResolvedValue(undefined);
24
+ vi.spyOn(fsx, "mkdir").mockResolvedValue(undefined);
25
+ vi.spyOn(fsx, "rm").mockResolvedValue(undefined);
26
+ const mockFsxGlob = vi.spyOn(fsx, "glob").mockResolvedValue([]);
27
+ vi.spyOn(fsx, "copy").mockResolvedValue(undefined);
28
+
29
+ vi.spyOn(cpx, "spawn").mockResolvedValue({ stdout: "", stderr: "", exitCode: 0 });
30
+ vi.spyOn(cpx, "spawnSync").mockReturnValue({ stdout: "", stderr: "", exitCode: 0 });
31
+
32
+ let tmpRoot: string;
33
+ let PKG_PATH: string;
34
+
35
+ beforeAll(() => {
36
+ tmpRoot = mkdtempSync(path.join(tmpdir(), "cap-build-"));
37
+ writeFileSync(path.join(tmpRoot, "package.json"), JSON.stringify({ private: true, workspaces: ["pkg"] }));
38
+ PKG_PATH = path.join(tmpRoot, "pkg");
39
+ mkdirSync(path.join(PKG_PATH, ".capacitor"), { recursive: true });
40
+ });
41
+
42
+ afterAll(() => {
43
+ rmSync(tmpRoot, { recursive: true, force: true });
44
+ });
45
+
46
+ function setupDefaultMocks() {
47
+ mockFsxExists.mockResolvedValue(true);
48
+
49
+ mockFsxReadJson.mockImplementation(((p: string) => {
50
+ const normalized = p.replace(/\\/g, "/");
51
+ if (normalized.includes(".capacitor/package.json")) {
52
+ return {
53
+ name: "com.test.app",
54
+ version: "1.2.3",
55
+ dependencies: {
56
+ "@capacitor/core": "^7.0.0",
57
+ "@capacitor/app": "^7.0.0",
58
+ "@capacitor/android": "^7.0.0",
59
+ },
60
+ devDependencies: {
61
+ "@capacitor/cli": "^7.0.0",
62
+ "@capacitor/assets": "^3.0.0",
63
+ },
64
+ };
65
+ }
66
+ return { name: "test-pkg", version: "1.2.3" };
67
+ }) as never);
68
+
69
+ mockFsxRead.mockImplementation(((p: string) => {
70
+ if (p.includes("AndroidManifest.xml")) {
71
+ return '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n<application>\n</application>\n</manifest>';
72
+ }
73
+ if (p.includes("build.gradle")) {
74
+ return `android {
75
+ defaultConfig {
76
+ versionCode 1
77
+ versionName "1.0"
78
+ minSdkVersion rootProject.ext.minSdkVersion
79
+ targetSdkVersion rootProject.ext.targetSdkVersion
80
+ }
81
+ buildTypes { release { } }
82
+ }`;
83
+ }
84
+ if (p.includes("gradle.properties")) {
85
+ return "org.gradle.jvmargs=-Xmx2048m";
86
+ }
87
+ return "";
88
+ }) as never);
89
+
90
+ mockFsxGlob.mockImplementation(((pattern: string) => {
91
+ if (pattern.includes("Corretto") || pattern.includes("jdk")) {
92
+ return ["C:/Program Files/Amazon Corretto/jdk21.0.1"];
93
+ }
94
+ return [];
95
+ }) as never);
96
+
97
+ process.env["ANDROID_HOME"] = "C:/Android/Sdk";
98
+ }
99
+
100
+ let savedEnv: Record<string, string | undefined>;
101
+ beforeEach(() => {
102
+ savedEnv = { ...process.env };
103
+ });
104
+ afterEach(() => {
105
+ process.env = savedEnv;
106
+ });
107
+
108
+ describe("Capacitor 빌드", () => {
109
+ beforeEach(() => {
110
+ vi.clearAllMocks();
111
+ setupDefaultMocks();
112
+ });
113
+
114
+ describe("서명", () => {
115
+ it("keystore 파일이 없으면 에러가 발생한다", async () => {
116
+ const { Capacitor } = await import("../../src/capacitor/capacitor.js");
117
+
118
+ // keystore 파일만 존재하지 않도록 설정
119
+ mockFsxExists.mockImplementation(((p: string) => {
120
+ if (p.includes("my-release.keystore")) return false;
121
+ return true;
122
+ }) as never);
123
+
124
+ const cap = await Capacitor.create(PKG_PATH, {
125
+ appId: "com.test.app",
126
+ appName: "Test App",
127
+ platform: {
128
+ android: {
129
+ sign: {
130
+ keystore: "my-release.keystore",
131
+ storePassword: "store123",
132
+ alias: "my-key",
133
+ password: "key123",
134
+ },
135
+ },
136
+ },
137
+ });
138
+
139
+ await expect(cap.build("/fake/out")).rejects.toThrow("keystore");
140
+ });
141
+ });
142
+ });