@simplysm/sd-cli 14.0.7 → 14.0.9

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 (265) hide show
  1. package/dist/angular/client-transform-stylesheet.d.ts.map +1 -1
  2. package/dist/angular/client-transform-stylesheet.js.map +1 -1
  3. package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
  4. package/dist/angular/vite-angular-plugin.js +15 -8
  5. package/dist/angular/vite-angular-plugin.js.map +1 -1
  6. package/dist/angular/vite-postcss-inline-plugin.d.ts.map +1 -1
  7. package/dist/angular/vite-postcss-inline-plugin.js.map +1 -1
  8. package/dist/capacitor/capacitor.d.ts +1 -1
  9. package/dist/capacitor/capacitor.d.ts.map +1 -1
  10. package/dist/capacitor/capacitor.js +43 -44
  11. package/dist/capacitor/capacitor.js.map +1 -1
  12. package/dist/commands/build.d.ts.map +1 -1
  13. package/dist/commands/build.js.map +1 -1
  14. package/dist/commands/check.d.ts.map +1 -1
  15. package/dist/commands/check.js +2 -2
  16. package/dist/commands/check.js.map +1 -1
  17. package/dist/commands/dev.d.ts.map +1 -1
  18. package/dist/commands/dev.js.map +1 -1
  19. package/dist/commands/lint.d.ts.map +1 -1
  20. package/dist/commands/lint.js.map +1 -1
  21. package/dist/commands/publish.d.ts.map +1 -1
  22. package/dist/commands/publish.js +12 -13
  23. package/dist/commands/publish.js.map +1 -1
  24. package/dist/commands/replace-deps.d.ts.map +1 -1
  25. package/dist/commands/replace-deps.js.map +1 -1
  26. package/dist/commands/typecheck.d.ts.map +1 -1
  27. package/dist/commands/typecheck.js +12 -12
  28. package/dist/commands/typecheck.js.map +1 -1
  29. package/dist/commands/watch.d.ts.map +1 -1
  30. package/dist/commands/watch.js.map +1 -1
  31. package/dist/electron/electron.d.ts.map +1 -1
  32. package/dist/electron/electron.js +42 -37
  33. package/dist/electron/electron.js.map +1 -1
  34. package/dist/engines/BaseEngine.d.ts +1 -5
  35. package/dist/engines/BaseEngine.d.ts.map +1 -1
  36. package/dist/engines/BaseEngine.js +7 -16
  37. package/dist/engines/BaseEngine.js.map +1 -1
  38. package/dist/engines/NgtscEngine.d.ts.map +1 -1
  39. package/dist/engines/NgtscEngine.js +10 -11
  40. package/dist/engines/NgtscEngine.js.map +1 -1
  41. package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -1
  42. package/dist/engines/ServerEsbuildEngine.js +10 -11
  43. package/dist/engines/ServerEsbuildEngine.js.map +1 -1
  44. package/dist/engines/TscEngine.d.ts.map +1 -1
  45. package/dist/engines/TscEngine.js +10 -11
  46. package/dist/engines/TscEngine.js.map +1 -1
  47. package/dist/engines/ViteEngine.d.ts.map +1 -1
  48. package/dist/engines/ViteEngine.js +3 -13
  49. package/dist/engines/ViteEngine.js.map +1 -1
  50. package/dist/engines/index.d.ts.map +1 -1
  51. package/dist/engines/index.js.map +1 -1
  52. package/dist/engines/types.d.ts +3 -6
  53. package/dist/engines/types.d.ts.map +1 -1
  54. package/dist/engines/types.js.map +1 -1
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js.map +1 -1
  57. package/dist/infra/ResultCollector.d.ts +1 -1
  58. package/dist/infra/ResultCollector.d.ts.map +1 -1
  59. package/dist/infra/ResultCollector.js.map +1 -1
  60. package/dist/infra/SignalHandler.d.ts.map +1 -1
  61. package/dist/infra/SignalHandler.js.map +1 -1
  62. package/dist/infra/WorkerManager.d.ts.map +1 -1
  63. package/dist/infra/WorkerManager.js.map +1 -1
  64. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  65. package/dist/orchestrators/BuildOrchestrator.js +30 -61
  66. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  67. package/dist/orchestrators/DevWatchOrchestrator.d.ts +2 -0
  68. package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
  69. package/dist/orchestrators/DevWatchOrchestrator.js +40 -44
  70. package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
  71. package/dist/sd-cli-entry.d.ts.map +1 -1
  72. package/dist/sd-cli-entry.js +2 -13
  73. package/dist/sd-cli-entry.js.map +1 -1
  74. package/dist/sd-cli.d.ts.map +1 -1
  75. package/dist/sd-cli.js +5 -5
  76. package/dist/sd-cli.js.map +1 -1
  77. package/dist/sd-config.types.d.ts.map +1 -1
  78. package/dist/sd-config.types.js.map +1 -1
  79. package/dist/utils/SdCliReporter.d.ts +18 -0
  80. package/dist/utils/SdCliReporter.d.ts.map +1 -0
  81. package/dist/utils/SdCliReporter.js +144 -0
  82. package/dist/utils/SdCliReporter.js.map +1 -0
  83. package/dist/utils/angular-build.d.ts.map +1 -1
  84. package/dist/utils/angular-build.js.map +1 -1
  85. package/dist/utils/angular-compiler.d.ts.map +1 -1
  86. package/dist/utils/angular-compiler.js +11 -4
  87. package/dist/utils/angular-compiler.js.map +1 -1
  88. package/dist/utils/build-env.d.ts.map +1 -1
  89. package/dist/utils/build-env.js +2 -1
  90. package/dist/utils/build-env.js.map +1 -1
  91. package/dist/utils/concurrency.d.ts.map +1 -1
  92. package/dist/utils/concurrency.js.map +1 -1
  93. package/dist/utils/copy-public.d.ts.map +1 -1
  94. package/dist/utils/copy-public.js +21 -21
  95. package/dist/utils/copy-public.js.map +1 -1
  96. package/dist/utils/copy-src.d.ts.map +1 -1
  97. package/dist/utils/copy-src.js +12 -12
  98. package/dist/utils/copy-src.js.map +1 -1
  99. package/dist/utils/diagnostic-utils.d.ts.map +1 -1
  100. package/dist/utils/diagnostic-utils.js +3 -2
  101. package/dist/utils/diagnostic-utils.js.map +1 -1
  102. package/dist/utils/engine-stop.d.ts.map +1 -1
  103. package/dist/utils/engine-stop.js.map +1 -1
  104. package/dist/utils/esbuild-config.d.ts.map +1 -1
  105. package/dist/utils/esbuild-config.js +2 -0
  106. package/dist/utils/esbuild-config.js.map +1 -1
  107. package/dist/utils/generate-pwa-icons.d.ts.map +1 -1
  108. package/dist/utils/generate-pwa-icons.js.map +1 -1
  109. package/dist/utils/hmr-candidates.d.ts.map +1 -1
  110. package/dist/utils/hmr-candidates.js.map +1 -1
  111. package/dist/utils/lint-utils.d.ts.map +1 -1
  112. package/dist/utils/lint-utils.js.map +1 -1
  113. package/dist/utils/lint-with-program.d.ts.map +1 -1
  114. package/dist/utils/lint-with-program.js +7 -3
  115. package/dist/utils/lint-with-program.js.map +1 -1
  116. package/dist/utils/ngtsc-build-core.d.ts +2 -10
  117. package/dist/utils/ngtsc-build-core.d.ts.map +1 -1
  118. package/dist/utils/ngtsc-build-core.js +16 -15
  119. package/dist/utils/ngtsc-build-core.js.map +1 -1
  120. package/dist/utils/orchestrator-utils.d.ts.map +1 -1
  121. package/dist/utils/orchestrator-utils.js.map +1 -1
  122. package/dist/utils/output-path-rewriter.d.ts.map +1 -1
  123. package/dist/utils/output-path-rewriter.js +7 -7
  124. package/dist/utils/output-path-rewriter.js.map +1 -1
  125. package/dist/utils/output-utils.d.ts.map +1 -1
  126. package/dist/utils/output-utils.js +1 -1
  127. package/dist/utils/output-utils.js.map +1 -1
  128. package/dist/utils/package-utils.d.ts +4 -0
  129. package/dist/utils/package-utils.d.ts.map +1 -1
  130. package/dist/utils/package-utils.js +34 -13
  131. package/dist/utils/package-utils.js.map +1 -1
  132. package/dist/utils/rebuild-manager.d.ts +1 -1
  133. package/dist/utils/rebuild-manager.d.ts.map +1 -1
  134. package/dist/utils/rebuild-manager.js +3 -1
  135. package/dist/utils/rebuild-manager.js.map +1 -1
  136. package/dist/utils/replace-deps.d.ts.map +1 -1
  137. package/dist/utils/replace-deps.js +10 -10
  138. package/dist/utils/replace-deps.js.map +1 -1
  139. package/dist/utils/scss-compiler.d.ts.map +1 -1
  140. package/dist/utils/scss-compiler.js.map +1 -1
  141. package/dist/utils/sd-config.d.ts.map +1 -1
  142. package/dist/utils/sd-config.js +2 -3
  143. package/dist/utils/sd-config.js.map +1 -1
  144. package/dist/utils/tsc-build.d.ts +3 -1
  145. package/dist/utils/tsc-build.d.ts.map +1 -1
  146. package/dist/utils/tsc-build.js +7 -4
  147. package/dist/utils/tsc-build.js.map +1 -1
  148. package/dist/utils/tsconfig.d.ts.map +1 -1
  149. package/dist/utils/tsconfig.js.map +1 -1
  150. package/dist/utils/typecheck-non-package.d.ts.map +1 -1
  151. package/dist/utils/typecheck-non-package.js +10 -5
  152. package/dist/utils/typecheck-non-package.js.map +1 -1
  153. package/dist/utils/typecheck-serialization.d.ts.map +1 -1
  154. package/dist/utils/typecheck-serialization.js.map +1 -1
  155. package/dist/utils/vite-config.d.ts.map +1 -1
  156. package/dist/utils/vite-config.js +2 -1
  157. package/dist/utils/vite-config.js.map +1 -1
  158. package/dist/utils/vite-scope-watch-plugin.d.ts.map +1 -1
  159. package/dist/utils/vite-scope-watch-plugin.js.map +1 -1
  160. package/dist/utils/worker-events.d.ts +1 -1
  161. package/dist/utils/worker-events.d.ts.map +1 -1
  162. package/dist/utils/worker-events.js +1 -0
  163. package/dist/utils/worker-events.js.map +1 -1
  164. package/dist/utils/worker-utils.d.ts +1 -1
  165. package/dist/utils/worker-utils.d.ts.map +1 -1
  166. package/dist/utils/worker-utils.js +3 -1
  167. package/dist/utils/worker-utils.js.map +1 -1
  168. package/dist/vitest-plugin.d.ts.map +1 -1
  169. package/dist/vitest-plugin.js +2 -0
  170. package/dist/vitest-plugin.js.map +1 -1
  171. package/dist/workers/client.worker.d.ts.map +1 -1
  172. package/dist/workers/client.worker.js +4 -0
  173. package/dist/workers/client.worker.js.map +1 -1
  174. package/dist/workers/library-build.worker.d.ts +2 -10
  175. package/dist/workers/library-build.worker.d.ts.map +1 -1
  176. package/dist/workers/library-build.worker.js +38 -14
  177. package/dist/workers/library-build.worker.js.map +1 -1
  178. package/dist/workers/lint.worker.d.ts.map +1 -1
  179. package/dist/workers/lint.worker.js.map +1 -1
  180. package/dist/workers/ngtsc-build.worker.d.ts.map +1 -1
  181. package/dist/workers/ngtsc-build.worker.js +40 -14
  182. package/dist/workers/ngtsc-build.worker.js.map +1 -1
  183. package/dist/workers/server-build.worker.d.ts +2 -10
  184. package/dist/workers/server-build.worker.d.ts.map +1 -1
  185. package/dist/workers/server-build.worker.js +30 -22
  186. package/dist/workers/server-build.worker.js.map +1 -1
  187. package/dist/workers/server-runtime.worker.d.ts.map +1 -1
  188. package/dist/workers/server-runtime.worker.js.map +1 -1
  189. package/package.json +4 -5
  190. package/src/angular/vite-angular-plugin.ts +18 -9
  191. package/src/capacitor/capacitor.ts +43 -44
  192. package/src/commands/check.ts +2 -2
  193. package/src/commands/publish.ts +12 -13
  194. package/src/commands/typecheck.ts +12 -12
  195. package/src/electron/electron.ts +44 -38
  196. package/src/engines/BaseEngine.ts +8 -19
  197. package/src/engines/NgtscEngine.ts +11 -11
  198. package/src/engines/ServerEsbuildEngine.ts +11 -11
  199. package/src/engines/TscEngine.ts +11 -11
  200. package/src/engines/ViteEngine.ts +3 -14
  201. package/src/engines/types.ts +3 -6
  202. package/src/infra/ResultCollector.ts +1 -1
  203. package/src/orchestrators/BuildOrchestrator.ts +31 -62
  204. package/src/orchestrators/DevWatchOrchestrator.ts +41 -44
  205. package/src/sd-cli-entry.ts +2 -12
  206. package/src/sd-cli.ts +8 -5
  207. package/src/utils/SdCliReporter.ts +177 -0
  208. package/src/utils/angular-compiler.ts +11 -4
  209. package/src/utils/build-env.ts +2 -1
  210. package/src/utils/copy-public.ts +21 -21
  211. package/src/utils/copy-src.ts +12 -12
  212. package/src/utils/diagnostic-utils.ts +3 -2
  213. package/src/utils/esbuild-config.ts +2 -0
  214. package/src/utils/lint-with-program.ts +7 -3
  215. package/src/utils/ngtsc-build-core.ts +18 -18
  216. package/src/utils/output-path-rewriter.ts +7 -7
  217. package/src/utils/output-utils.ts +1 -1
  218. package/src/utils/package-utils.ts +37 -13
  219. package/src/utils/rebuild-manager.ts +4 -2
  220. package/src/utils/replace-deps.ts +10 -10
  221. package/src/utils/sd-config.ts +2 -3
  222. package/src/utils/tsc-build.ts +9 -4
  223. package/src/utils/typecheck-non-package.ts +10 -5
  224. package/src/utils/vite-config.ts +2 -1
  225. package/src/utils/worker-events.ts +2 -1
  226. package/src/utils/worker-utils.ts +3 -1
  227. package/src/vitest-plugin.ts +5 -0
  228. package/src/workers/client.worker.ts +4 -0
  229. package/src/workers/library-build.worker.ts +48 -18
  230. package/src/workers/ngtsc-build.worker.ts +48 -13
  231. package/src/workers/server-build.worker.ts +32 -26
  232. package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +11 -7
  233. package/tests/angular/vite-angular-plugin-lint.spec.ts +6 -1
  234. package/tests/capacitor/capacitor-build.spec.ts +14 -7
  235. package/tests/capacitor/capacitor-icon.spec.ts +14 -7
  236. package/tests/capacitor/capacitor-init.spec.ts +13 -6
  237. package/tests/capacitor/capacitor-run.spec.ts +18 -11
  238. package/tests/capacitor/capacitor-workspace.spec.ts +13 -6
  239. package/tests/commands/check.spec.ts +5 -2
  240. package/tests/commands/publish.spec.ts +4 -4
  241. package/tests/commands/typecheck.spec.ts +20 -31
  242. package/tests/electron/electron.spec.ts +32 -23
  243. package/tests/engines/base-engine.spec.ts +15 -21
  244. package/tests/engines/engine-lint-integration.spec.ts +5 -10
  245. package/tests/engines/ngtsc-engine.spec.ts +27 -41
  246. package/tests/engines/server-esbuild-engine.spec.ts +18 -29
  247. package/tests/engines/tsc-engine.spec.ts +14 -23
  248. package/tests/engines/vite-engine.spec.ts +10 -15
  249. package/tests/infra/result-collector.spec.ts +2 -2
  250. package/tests/orchestrators/build-orchestrator.spec.ts +19 -29
  251. package/tests/orchestrators/dev-watch-orchestrator.spec.ts +110 -95
  252. package/tests/utils/copy-src.spec.ts +25 -19
  253. package/tests/utils/diagnostic-utils.spec.ts +72 -0
  254. package/tests/utils/ngtsc-build-core-angular-compiler.spec.ts +2 -3
  255. package/tests/utils/output-path-rewriter.spec.ts +7 -6
  256. package/tests/utils/output-utils.spec.ts +5 -5
  257. package/tests/utils/rebuild-manager.spec.ts +1 -1
  258. package/tests/utils/sd-config.spec.ts +4 -0
  259. package/tests/utils/tsc-build.spec.ts +23 -5
  260. package/tests/workers/library-build-worker.spec.ts +113 -20
  261. package/tests/workers/ngtsc-build-lint.spec.ts +3 -6
  262. package/tests/workers/ngtsc-build-worker.spec.ts +11 -13
  263. package/tests/workers/server-build-lint.spec.ts +4 -1
  264. package/tests/workers/server-build-worker.spec.ts +25 -25
  265. package/tests/angular/migration-cleanup.spec.ts +0 -59
@@ -2,6 +2,7 @@ import path from "path";
2
2
  import fs from "fs";
3
3
  import { consola } from "consola";
4
4
  import { SdError } from "@simplysm/core-common";
5
+ import { pathx } from "@simplysm/core-node";
5
6
  import type {
6
7
  BuildTarget,
7
8
  SdBuildPackageConfig,
@@ -36,8 +37,8 @@ export function iteratePackages(
36
37
  */
37
38
  export function findPackageRoot(startDir: string): string {
38
39
  let dir = startDir;
39
- while (!fs.existsSync(path.join(dir, "package.json"))) {
40
- const parent = path.dirname(dir);
40
+ while (!fs.existsSync(pathx.posix(path.join(dir, "package.json")))) {
41
+ const parent = pathx.posix(path.dirname(dir));
41
42
  if (parent === dir) throw new Error("package.json not found");
42
43
  dir = parent;
43
44
  }
@@ -49,13 +50,14 @@ export function findPackageRoot(startDir: string): string {
49
50
  * Returns a map of directory name → relative path (e.g., "orm" → "tests/orm").
50
51
  */
51
52
  export function discoverWorkspacePackages(cwd: string): Map<string, string> {
53
+ logger.debug("워크스페이스 패키지 탐색 시작");
52
54
  const map = new Map<string, string>();
53
55
  for (const dir of ["packages", "tests"]) {
54
- const baseDir = path.join(cwd, dir);
56
+ const baseDir = pathx.posix(path.join(cwd, dir));
55
57
  if (!fs.existsSync(baseDir)) continue;
56
58
  for (const entry of fs.readdirSync(baseDir, { withFileTypes: true })) {
57
59
  if (!entry.isDirectory()) continue;
58
- if (!fs.existsSync(path.join(baseDir, entry.name, "package.json"))) continue;
60
+ if (!fs.existsSync(pathx.posix(path.join(baseDir, entry.name, "package.json")))) continue;
59
61
  if (map.has(entry.name)) {
60
62
  throw new SdError(
61
63
  `Duplicate workspace package name: ${entry.name} (${map.get(entry.name)} and ${dir}/${entry.name})`,
@@ -64,6 +66,7 @@ export function discoverWorkspacePackages(cwd: string): Map<string, string> {
64
66
  map.set(entry.name, `${dir}/${entry.name}`);
65
67
  }
66
68
  }
69
+ logger.debug(`워크스페이스 패키지 탐색 완료 (${map.size}개)`);
67
70
  return map;
68
71
  }
69
72
 
@@ -73,10 +76,24 @@ export function discoverWorkspacePackages(cwd: string): Map<string, string> {
73
76
  * Also builds a pathMap (name → relative path) for all packages.
74
77
  * Throws SdError if a tests package name collides with an sd.config.ts package name.
75
78
  */
79
+ /**
80
+ * Build pathMap from sd.config.ts packages only (without tests packages).
81
+ */
82
+ export function buildPathMapFromConfig(
83
+ configPackages: Record<string, SdPackageConfig | undefined>,
84
+ ): Map<string, string> {
85
+ const pathMap = new Map<string, string>();
86
+ for (const name of Object.keys(configPackages)) {
87
+ pathMap.set(name, `packages/${name}`);
88
+ }
89
+ return pathMap;
90
+ }
91
+
76
92
  export function mergeTestsPackagesIntoConfig(
77
93
  configPackages: Record<string, SdPackageConfig | undefined>,
78
94
  workspacePackages: Map<string, string>,
79
95
  ): { merged: Record<string, SdPackageConfig | undefined>; pathMap: Map<string, string> } {
96
+ logger.debug("tests 패키지 병합 시작");
80
97
  const pathMap = new Map<string, string>();
81
98
  const merged: Record<string, SdPackageConfig | undefined> = { ...configPackages };
82
99
 
@@ -99,6 +116,7 @@ export function mergeTestsPackagesIntoConfig(
99
116
  pathMap.set(name, relPath);
100
117
  }
101
118
 
119
+ logger.debug(`tests 패키지 병합 완료 (총 ${Object.keys(merged).length}개)`);
102
120
  return { merged, pathMap };
103
121
  }
104
122
 
@@ -114,7 +132,7 @@ export function collectDeps(
114
132
  ): DepsResult {
115
133
  const startTime = performance.now();
116
134
  logger.debug("의존성 수집 시작");
117
- const rootPkgJsonPath = path.join(cwd, "package.json");
135
+ const rootPkgJsonPath = pathx.posix(path.join(cwd, "package.json"));
118
136
  const rootPkgJson = JSON.parse(fs.readFileSync(rootPkgJsonPath, "utf-8")) as { name: string };
119
137
  const scopeMatch = rootPkgJson.name.match(/^(@[^/]+)\//);
120
138
  const workspaceScope = scopeMatch != null ? scopeMatch[1] : undefined;
@@ -132,7 +150,7 @@ export function collectDeps(
132
150
  const visited = new Set<string>();
133
151
 
134
152
  function traverse(dir: string): void {
135
- const pkgJsonPath = path.join(dir, "package.json");
153
+ const pkgJsonPath = pathx.posix(path.join(dir, "package.json"));
136
154
  if (!fs.existsSync(pkgJsonPath)) return;
137
155
 
138
156
  const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8")) as {
@@ -147,8 +165,8 @@ export function collectDeps(
147
165
  // Check for workspace package
148
166
  if (workspaceScope != null && dep.startsWith(workspaceScope + "/")) {
149
167
  const dirName = dep.slice(workspaceScope.length + 1);
150
- const depDir = path.join(cwd, "packages", dirName);
151
- if (fs.existsSync(path.join(depDir, "package.json"))) {
168
+ const depDir = pathx.posix(path.join(cwd, "packages", dirName));
169
+ if (fs.existsSync(pathx.posix(path.join(depDir, "package.json")))) {
152
170
  workspaceDeps.push(dirName);
153
171
  traverse(depDir);
154
172
  continue;
@@ -159,8 +177,8 @@ export function collectDeps(
159
177
  const matched = replaceDepsPatterns.find((p) => p.regex.test(dep));
160
178
  if (matched != null) {
161
179
  replaceDeps.push(dep);
162
- const depNodeModulesDir = path.join(cwd, "node_modules", ...dep.split("/"));
163
- if (fs.existsSync(path.join(depNodeModulesDir, "package.json"))) {
180
+ const depNodeModulesDir = pathx.posix(path.join(cwd, "node_modules", ...dep.split("/")));
181
+ if (fs.existsSync(pathx.posix(path.join(depNodeModulesDir, "package.json")))) {
164
182
  traverse(depNodeModulesDir);
165
183
  }
166
184
  continue;
@@ -180,7 +198,7 @@ export function collectDeps(
180
198
  * in dependencies or peerDependencies.
181
199
  */
182
200
  export function hasAngularCoreDependency(pkgDir: string): boolean {
183
- const pkgJsonPath = path.join(pkgDir, "package.json");
201
+ const pkgJsonPath = pathx.posix(path.join(pkgDir, "package.json"));
184
202
  if (!fs.existsSync(pkgJsonPath)) return false;
185
203
  try {
186
204
  const content = fs.readFileSync(pkgJsonPath, "utf-8");
@@ -226,6 +244,7 @@ export function filterPackagesByTargets(
226
244
  packages: Record<string, SdPackageConfig | undefined>,
227
245
  targets: string[],
228
246
  ): Record<string, SdPackageConfig> {
247
+ logger.debug(`패키지 필터링 시작 (targets: ${targets.length > 0 ? targets.join(", ") : "전체"})`);
229
248
  const result: Record<string, SdPackageConfig> = {};
230
249
 
231
250
  for (const [name, config] of Object.entries(packages)) {
@@ -246,6 +265,7 @@ export function filterPackagesByTargets(
246
265
  }
247
266
  }
248
267
 
268
+ logger.debug(`패키지 필터링 완료 (${Object.keys(result).length}개)`);
249
269
  return result;
250
270
  }
251
271
 
@@ -268,12 +288,13 @@ export function classifyWatchPackages(
268
288
  cwd: string,
269
289
  pathMap: Map<string, string>,
270
290
  ): WatchClassifiedPackages {
291
+ logger.debug("watch 패키지 분류 시작");
271
292
  const libraryPackages: WatchClassifiedPackages["libraryPackages"] = [];
272
293
  const watchHookPackages: WatchClassifiedPackages["watchHookPackages"] = [];
273
294
 
274
295
  for (const { name, config } of iteratePackages(allPackages, [])) {
275
296
  const relPath = pathMap.get(name) ?? `packages/${name}`;
276
- const pkgDir = path.join(cwd, relPath);
297
+ const pkgDir = pathx.posix(path.join(cwd, relPath));
277
298
  if (isLibraryTarget(config.target)) {
278
299
  const buildConfig = config as SdBuildPackageConfig;
279
300
  libraryPackages.push({ name, dir: pkgDir, config: buildConfig });
@@ -289,6 +310,7 @@ export function classifyWatchPackages(
289
310
  }
290
311
  }
291
312
 
313
+ logger.debug(`watch 패키지 분류 완료 (library: ${libraryPackages.length}, watchHook: ${watchHookPackages.length})`);
292
314
  return { libraryPackages, watchHookPackages };
293
315
  }
294
316
 
@@ -307,6 +329,7 @@ export function classifyDevPackages(
307
329
  cwd: string,
308
330
  pathMap: Map<string, string>,
309
331
  ): DevClassifiedPackages {
332
+ logger.debug("dev 패키지 분류 시작");
310
333
  const serverPackages: DevClassifiedPackages["serverPackages"] = [];
311
334
  const clientPackages: DevClassifiedPackages["clientPackages"] = [];
312
335
  const serverClientsMap = new Map<string, string[]>();
@@ -324,7 +347,7 @@ export function classifyDevPackages(
324
347
  // Second pass: classify all packages
325
348
  for (const { name, config } of entries) {
326
349
  const relPath = pathMap.get(name) ?? `packages/${name}`;
327
- const pkgDir = path.join(cwd, relPath);
350
+ const pkgDir = pathx.posix(path.join(cwd, relPath));
328
351
  if (config.target === "server") {
329
352
  serverPackages.push({
330
353
  name,
@@ -355,6 +378,7 @@ export function classifyDevPackages(
355
378
  // Library and scripts packages are excluded from dev mode
356
379
  }
357
380
 
381
+ logger.debug(`dev 패키지 분류 완료 (server: ${serverPackages.length}, client: ${clientPackages.length})`);
358
382
  return { serverPackages, clientPackages, serverClientsMap };
359
383
  }
360
384
 
@@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
2
2
  import { consola } from "consola";
3
3
 
4
4
  interface RebuildManagerEvents {
5
- batchComplete: [];
5
+ batchComplete: [completedKeys: string[]];
6
6
  }
7
7
 
8
8
  export class RebuildManager extends EventEmitter<RebuildManagerEvents> {
@@ -19,6 +19,7 @@ export class RebuildManager extends EventEmitter<RebuildManagerEvents> {
19
19
  }
20
20
 
21
21
  registerBuild(key: string, title: string): () => void {
22
+ this._logger.debug(`빌드 등록: ${key} (${title})`);
22
23
  let resolver!: () => void;
23
24
  const promise = new Promise<void>((resolve) => {
24
25
  resolver = resolve;
@@ -35,6 +36,7 @@ export class RebuildManager extends EventEmitter<RebuildManagerEvents> {
35
36
 
36
37
  private async _runBatch(): Promise<void> {
37
38
  if (this._isRunning || this._pendingBuilds.size === 0) {
39
+ this._logger.debug(`배치 건너뜀 (running: ${String(this._isRunning)}, pending: ${this._pendingBuilds.size})`);
38
40
  return;
39
41
  }
40
42
 
@@ -60,7 +62,7 @@ export class RebuildManager extends EventEmitter<RebuildManagerEvents> {
60
62
 
61
63
  this._logger.success(`리빌드 실행 완료 (${titles})`);
62
64
 
63
- this.emit("batchComplete");
65
+ this.emit("batchComplete", Array.from(batchBuilds.keys()));
64
66
 
65
67
  this._isRunning = false;
66
68
 
@@ -133,7 +133,7 @@ export interface WatchReplaceDepResult {
133
133
  async function collectSearchRoots(projectRoot: string): Promise<string[]> {
134
134
  const searchRoots = [projectRoot];
135
135
 
136
- const workspaceYamlPath = path.join(projectRoot, "pnpm-workspace.yaml");
136
+ const workspaceYamlPath = pathx.posix(path.join(projectRoot, "pnpm-workspace.yaml"));
137
137
  try {
138
138
  const yamlContent = await fs.promises.readFile(workspaceYamlPath, "utf-8");
139
139
  const workspaceGlobs = parseWorkspaceGlobs(yamlContent);
@@ -171,7 +171,7 @@ async function resolveAllReplaceDepEntries(
171
171
  const searchRoots = await collectSearchRoots(projectRoot);
172
172
 
173
173
  for (const searchRoot of searchRoots) {
174
- const nodeModulesDir = path.join(searchRoot, "node_modules");
174
+ const nodeModulesDir = pathx.posix(path.join(searchRoot, "node_modules"));
175
175
 
176
176
  try {
177
177
  await fs.promises.access(nodeModulesDir);
@@ -192,8 +192,8 @@ async function resolveAllReplaceDepEntries(
192
192
  const matchedEntries = resolveReplaceDepEntries(replaceDeps, targetNames);
193
193
 
194
194
  for (const { targetName, sourcePath } of matchedEntries) {
195
- const targetPath = path.join(nodeModulesDir, targetName);
196
- const resolvedSourcePath = path.resolve(projectRoot, sourcePath);
195
+ const targetPath = pathx.posix(path.join(nodeModulesDir, targetName));
196
+ const resolvedSourcePath = pathx.posixResolve(projectRoot, sourcePath);
197
197
 
198
198
  // Verify source path exists
199
199
  try {
@@ -208,7 +208,7 @@ async function resolveAllReplaceDepEntries(
208
208
  try {
209
209
  const stat = await fs.promises.lstat(targetPath);
210
210
  if (stat.isSymbolicLink()) {
211
- actualTargetPath = await fs.promises.realpath(targetPath);
211
+ actualTargetPath = pathx.posix(await fs.promises.realpath(targetPath));
212
212
  }
213
213
  } catch {
214
214
  // If targetPath doesn't exist, use as-is
@@ -263,7 +263,7 @@ export async function setupReplaceDeps(
263
263
 
264
264
  // Run postinstall scripts from replaced packages
265
265
  for (const { targetName, resolvedSourcePath, actualTargetPath } of entries) {
266
- const sourcePkgJsonPath = path.join(resolvedSourcePath, "package.json");
266
+ const sourcePkgJsonPath = pathx.posix(path.join(resolvedSourcePath, "package.json"));
267
267
  try {
268
268
  const pkgJson = JSON.parse(await fs.promises.readFile(sourcePkgJsonPath, "utf-8"));
269
269
  const postinstall = pkgJson.scripts?.postinstall as string | undefined;
@@ -312,7 +312,7 @@ export async function watchReplaceDeps(
312
312
  watchedSources.add(entry.resolvedSourcePath);
313
313
 
314
314
  const excludedPaths = [...EXCLUDED_NAMES].map((name) =>
315
- path.join(entry.resolvedSourcePath, name),
315
+ pathx.posix(path.join(entry.resolvedSourcePath, name)),
316
316
  );
317
317
 
318
318
  const watcher = await FsWatcher.watch([entry.resolvedSourcePath], { followSymlinks: false });
@@ -331,8 +331,8 @@ export async function watchReplaceDeps(
331
331
  if (e.resolvedSourcePath !== entry.resolvedSourcePath) continue;
332
332
 
333
333
  // Calculate relative path from source
334
- const relativePath = path.relative(e.resolvedSourcePath, changedPath);
335
- const destPath = path.join(e.actualTargetPath, relativePath);
334
+ const relativePath = pathx.posix(path.relative(e.resolvedSourcePath, changedPath));
335
+ const destPath = pathx.posix(path.join(e.actualTargetPath, relativePath));
336
336
 
337
337
  try {
338
338
  // Check if source exists
@@ -350,7 +350,7 @@ export async function watchReplaceDeps(
350
350
  if (stat.isDirectory()) {
351
351
  await fsx.mkdir(destPath);
352
352
  } else {
353
- await fsx.mkdir(path.dirname(destPath));
353
+ await fsx.mkdir(pathx.posix(path.dirname(destPath)));
354
354
  await fsx.copy(changedPath, destPath, replaceDepsCopyFilter);
355
355
  }
356
356
  } else {
@@ -1,7 +1,6 @@
1
- import path from "path";
2
1
  import { createJiti } from "jiti";
3
2
  import { SdError } from "@simplysm/core-common";
4
- import { fsx } from "@simplysm/core-node";
3
+ import { fsx, pathx } from "@simplysm/core-node";
5
4
  import { consola } from "consola";
6
5
  import type { SdConfig, SdConfigParams } from "../sd-config.types";
7
6
 
@@ -13,7 +12,7 @@ const logger = consola.withTag("sd:cli:sd-config");
13
12
  * @throws if sd.config.ts is missing or format is incorrect
14
13
  */
15
14
  export async function loadSdConfig(params: SdConfigParams): Promise<SdConfig> {
16
- const sdConfigPath = path.resolve(params.cwd, "sd.config.ts");
15
+ const sdConfigPath = pathx.posixResolve(params.cwd, "sd.config.ts");
17
16
  logger.debug(`sd.config.ts 로드 중: ${sdConfigPath}`);
18
17
 
19
18
  if (!(await fsx.exists(sdConfigPath))) {
@@ -4,6 +4,7 @@ import { err as errNs } from "@simplysm/core-common";
4
4
  import { consola } from "consola";
5
5
 
6
6
  const logger = consola.withTag("sd:cli:tsc-build");
7
+ import { pathx } from "@simplysm/core-node";
7
8
  import {
8
9
  parseTsconfig,
9
10
  getPackageSourceFiles,
@@ -27,6 +28,8 @@ export interface TscPackageBuildOptions {
27
28
  parsedConfig?: ts.ParsedCommandLine;
28
29
  /** Typecheck environment. When set, adjusts compilerOptions via getCompilerOptionsForEnv(). */
29
30
  env?: TypecheckEnv;
31
+ /** Include tests/ files in typecheck-only mode. Defaults to false. */
32
+ includeTests?: boolean;
30
33
  }
31
34
 
32
35
  /**
@@ -49,7 +52,7 @@ export interface TscPackageBuildResult {
49
52
  * Run TypeScript incremental build for a package.
50
53
  *
51
54
  * - output.js || output.dts: emit mode (src files only, generates output files)
52
- * - neither: typecheck only (src + test files, no output)
55
+ * - neither: typecheck only (src files only by default, src + test files when includeTests=true)
53
56
  *
54
57
  * Uses tsBuildInfoFile for incremental compilation across runs.
55
58
  */
@@ -68,7 +71,7 @@ export function runTscPackageBuild(options: TscPackageBuildOptions): TscPackageB
68
71
 
69
72
  let rootFiles: string[];
70
73
 
71
- if (needsEmit) {
74
+ if (needsEmit || !options.includeTests) {
72
75
  rootFiles = getPackageSourceFiles(pkgDir, parsedConfig);
73
76
  } else {
74
77
  rootFiles = getPackageFiles(pkgDir, parsedConfig);
@@ -79,7 +82,9 @@ export function runTscPackageBuild(options: TscPackageBuildOptions): TscPackageB
79
82
  const tsBuildInfoFile = path.join(
80
83
  pkgDir,
81
84
  ".cache",
82
- needsEmit ? "build.tsbuildinfo" : `typecheck${envSuffix}.tsbuildinfo`,
85
+ needsEmit
86
+ ? `build${output.dts ? "" : "-no-dts"}${envSuffix}.tsbuildinfo`
87
+ : `typecheck${envSuffix}.tsbuildinfo`,
83
88
  );
84
89
 
85
90
  const compilerOptions: ts.CompilerOptions = {
@@ -148,7 +153,7 @@ export function runTscPackageBuild(options: TscPackageBuildOptions): TscPackageB
148
153
  const result = builderProgram.getSemanticDiagnosticsOfNextAffectedFile();
149
154
  if (result == null) break;
150
155
  if ("fileName" in result.affected) {
151
- affectedFiles?.add(result.affected.fileName.replace(/\\/g, "/"));
156
+ affectedFiles?.add(pathx.posix(result.affected.fileName));
152
157
  } else {
153
158
  // ts.Program returned — global change, treat as full rebuild
154
159
  affectedFiles = undefined;
@@ -1,6 +1,7 @@
1
1
  import path from "path";
2
2
  import ts from "typescript";
3
3
  import { consola } from "consola";
4
+ import { pathx } from "@simplysm/core-node";
4
5
  import { parseTsconfig } from "./tsconfig";
5
6
  import { serializeDiagnostic, type SerializedDiagnostic } from "./typecheck-serialization";
6
7
 
@@ -28,15 +29,15 @@ export function typecheckNonPackageFiles(cwd: string): NonPackageTypecheckResult
28
29
  const packagesDir = path.join(cwd, "packages");
29
30
 
30
31
  const isNonPackageFile = (fileName: string): boolean => {
31
- const normalized = path.resolve(fileName);
32
- const normalizedPkgDir = path.resolve(packagesDir);
32
+ const normalized = pathx.posixResolve(fileName);
33
+ const normalizedPkgDir = pathx.posixResolve(packagesDir);
33
34
 
34
35
  // Files outside packages/ directory
35
- if (!normalized.startsWith(normalizedPkgDir + path.sep)) return true;
36
+ if (!normalized.startsWith(normalizedPkgDir + "/")) return true;
36
37
 
37
38
  // Files directly in package root (e.g., packages/{pkg}/file.ts — depth 2)
38
- const relative = path.relative(normalizedPkgDir, normalized);
39
- return relative.split(path.sep).length === 2;
39
+ const relative = pathx.posix(path.relative(normalizedPkgDir, normalized));
40
+ return relative.split("/").length === 2;
40
41
  };
41
42
 
42
43
  const rootFiles = parsedConfig.fileNames.filter(isNonPackageFile);
@@ -54,14 +55,18 @@ export function typecheckNonPackageFiles(cwd: string): NonPackageTypecheckResult
54
55
  declarationMap: false,
55
56
  };
56
57
 
58
+ logger.debug("incremental 프로그램 생성 시작");
57
59
  const host = ts.createIncrementalCompilerHost(options);
58
60
  const program = ts.createIncrementalProgram({
59
61
  rootNames: rootFiles,
60
62
  options,
61
63
  host,
62
64
  });
65
+ logger.debug("incremental 프로그램 생성 완료");
63
66
 
67
+ logger.debug("emit 시작");
64
68
  program.emit();
69
+ logger.debug("emit 완료");
65
70
 
66
71
  const allDiagnostics = [
67
72
  ...program.getConfigFileParsingDiagnostics(),
@@ -1,5 +1,6 @@
1
1
  import type { InlineConfig, PluginOption } from "vite";
2
2
  import path from "path";
3
+ import { pathx } from "@simplysm/core-node";
3
4
  import tsconfigPaths from "vite-tsconfig-paths";
4
5
  import browserslistToEsbuild from "browserslist-to-esbuild";
5
6
  import { sdAngularPlugin } from "../angular/vite-angular-plugin.js";
@@ -227,7 +228,7 @@ export async function createClientViteConfig(
227
228
  if (!code.includes("import.meta")) return;
228
229
 
229
230
  // id(파일 경로)를 Vite 서빙 URL로 변환
230
- const relative = path.relative(pkgDir, id).replace(/\\/g, "/");
231
+ const relative = pathx.posix(path.relative(pkgDir, id));
231
232
  const moduleUrl = id.startsWith("/") || id.startsWith("\0")
232
233
  ? id // 가상 모듈(/@vite/client 등)은 그대로 사용
233
234
  : base + relative;
@@ -51,7 +51,7 @@ export interface BaseWorkerInfo<TEvents extends Record<string, unknown> = Record
51
51
  export interface WorkerEventHandlerOptions {
52
52
  resultKey: string;
53
53
  listrTitle: string;
54
- resultType: "build" | "dts";
54
+ resultType: "build";
55
55
  }
56
56
 
57
57
  /**
@@ -77,6 +77,7 @@ export function registerWorkerEventHandlers(
77
77
  results: Map<string, BuildResult>,
78
78
  rebuildManager: RebuildManager,
79
79
  ): (result: BuildResult) => void {
80
+ workerEventsLogger.debug(`[${workerInfo.name}] 이벤트 핸들러 등록 (${opts.resultType})`);
80
81
  const completeTask = (result: BuildResult): void => {
81
82
  results.set(opts.resultKey, result);
82
83
  workerInfo.buildResolver?.();
@@ -1,13 +1,15 @@
1
1
  import consola, { type ConsolaInstance, LogLevels } from "consola";
2
+ import { SdCliReporter } from "./SdCliReporter";
2
3
 
3
4
  /**
4
- * Apply debug log level in worker threads
5
+ * Apply sd-cli reporter and debug log level in worker threads
5
6
  *
6
7
  * Checks the SD_DEBUG environment variable (set by --debug flag in main process)
7
8
  * and applies debug log level to consola in the current worker thread.
8
9
  * Must be called at worker module top level.
9
10
  */
10
11
  export function applyDebugLevel(): void {
12
+ consola.options.reporters = [new SdCliReporter()];
11
13
  if (process.env["SD_DEBUG"] === "true") {
12
14
  consola.level = LogLevels.debug;
13
15
  }
@@ -31,8 +31,13 @@ export function angularVitestPlugin(options: AngularVitestPluginOptions): Plugin
31
31
  (f) => f.includes("/src/") || f.includes(".fixture."),
32
32
  );
33
33
 
34
+ const angularCompilerOptions = configFile.config?.angularCompilerOptions as
35
+ | Record<string, unknown>
36
+ | undefined;
37
+
34
38
  const compilerOptions: ts.CompilerOptions = {
35
39
  ...parsedConfig.options,
40
+ ...(angularCompilerOptions as ts.CompilerOptions | undefined),
36
41
  noEmit: false,
37
42
  declaration: false,
38
43
  declarationMap: false,
@@ -104,8 +104,10 @@ async function startWatch(info: ClientBuildInfo): Promise<ClientBuildResult> {
104
104
  pwa: info.pwa,
105
105
  });
106
106
 
107
+ logger.debug(`[${info.name}] Vite server 생성 시작`);
107
108
  viteServer = await createServer(viteConfig);
108
109
  await viteServer.listen();
110
+ logger.debug(`[${info.name}] Vite server listen 완료`);
109
111
 
110
112
  // 실제 포트 감지
111
113
  const address = viteServer.httpServer?.address();
@@ -139,11 +141,13 @@ async function startWatch(info: ClientBuildInfo): Promise<ClientBuildResult> {
139
141
  * dev server 중지. Vite server를 정리한다.
140
142
  */
141
143
  async function stopWatch(): Promise<void> {
144
+ logger.debug("Vite server 정리 시작");
142
145
  const serverToClose = viteServer;
143
146
  viteServer = undefined;
144
147
  if (serverToClose != null) {
145
148
  await serverToClose.close();
146
149
  }
150
+ logger.debug("Vite server 정리 완료");
147
151
  }
148
152
 
149
153
  /**
@@ -1,5 +1,5 @@
1
- import path from "path";
2
- import { createWorker, FsWatcher } from "@simplysm/core-node";
1
+ import type ts from "typescript";
2
+ import { createWorker, FsWatcher, pathx } from "@simplysm/core-node";
3
3
  import { err as errNs } from "@simplysm/core-common";
4
4
  import { consola } from "consola";
5
5
  import type { SdBuildPackageConfig } from "../sd-config.types";
@@ -26,14 +26,12 @@ export interface LibraryBuildInfo {
26
26
  }
27
27
 
28
28
  export interface LibraryBuildResult {
29
- js: { success: boolean; errors?: string[]; warnings?: string[] };
30
- dts: { success: boolean; errors?: string[]; diagnostics: SerializedDiagnostic[] };
29
+ build: { success: boolean; errors?: string[]; warnings?: string[]; diagnostics: SerializedDiagnostic[] };
31
30
  lint?: LintWithProgramResult;
32
31
  }
33
32
 
34
33
  export interface CombinedBuildEvent {
35
- js: { success: boolean; errors?: string[]; warnings?: string[] };
36
- dts: { success: boolean; errors?: string[] };
34
+ build: { success: boolean; errors?: string[]; warnings?: string[] };
37
35
  lint?: LintWithProgramResult;
38
36
  }
39
37
 
@@ -54,6 +52,7 @@ let fsWatcher: FsWatcher | undefined;
54
52
  async function cleanup(): Promise<void> {
55
53
  const watcherToClose = fsWatcher;
56
54
  fsWatcher = undefined;
55
+ lastSourceFilePaths = undefined;
57
56
  await watcherToClose?.close();
58
57
  }
59
58
 
@@ -70,28 +69,27 @@ async function build(info: LibraryBuildInfo): Promise<LibraryBuildResult> {
70
69
  cwd: info.cwd,
71
70
  output: info.output,
72
71
  env: info.output.env,
72
+ includeTests: info.output.includeTests,
73
73
  });
74
74
  logger.debug(`[${info.name}] library worker build 완료 (success: ${tscResult.success})`);
75
75
 
76
76
  // Run lint if enabled and program is available
77
77
  let lint: LintWithProgramResult | undefined;
78
78
  if (info.output.lint === true && tscResult.program != null) {
79
+ logger.debug(`[${info.name}] lint 시작`);
79
80
  const lintRunner = new LintWithProgramRunner({
80
81
  cwd: info.cwd,
81
82
  pkgName: info.name,
82
83
  });
83
84
  lint = await lintRunner.lint({ program: tscResult.program });
85
+ logger.debug(`[${info.name}] lint 완료`);
84
86
  }
85
87
 
86
88
  return {
87
- js: {
89
+ build: {
88
90
  success: tscResult.success,
89
91
  errors: tscResult.errors,
90
92
  warnings: undefined,
91
- },
92
- dts: {
93
- success: tscResult.success,
94
- errors: tscResult.errors,
95
93
  diagnostics: tscResult.diagnostics,
96
94
  },
97
95
  lint,
@@ -107,20 +105,36 @@ const guardStartWatch = createOnceGuard("startWatch");
107
105
  // Mutable state for watch mode
108
106
  let watchInfo: LibraryBuildInfo | undefined;
109
107
  let watchLintRunner: LintWithProgramRunner | undefined;
108
+ let lastSourceFilePaths: Set<string> | undefined;
109
+
110
+ function extractSourceFilePaths(program: ts.Program | undefined): Set<string> | undefined {
111
+ if (program == null) return undefined;
112
+ const paths = new Set<string>();
113
+ for (const sf of program.getSourceFiles()) {
114
+ paths.add(pathx.posix(sf.fileName));
115
+ }
116
+ return paths;
117
+ }
110
118
 
111
119
  async function rebuildAll(): Promise<CombinedBuildEvent> {
112
120
  const info = watchInfo!;
121
+ logger.debug(`[${info.name}] rebuildAll 시작`);
113
122
 
114
123
  const tscResult = runTscPackageBuild({
115
124
  pkgDir: info.pkgDir,
116
125
  cwd: info.cwd,
117
126
  output: info.output,
118
127
  env: info.output.env,
128
+ includeTests: info.output.includeTests,
119
129
  });
120
130
 
131
+ // Update source file paths for dependency filtering
132
+ lastSourceFilePaths = extractSourceFilePaths(tscResult.program) ?? lastSourceFilePaths;
133
+
121
134
  // Run lint if enabled and program is available
122
135
  let lint: LintWithProgramResult | undefined;
123
136
  if (info.output.lint === true && tscResult.program != null) {
137
+ logger.debug(`[${info.name}] lint 시작`);
124
138
  if (watchLintRunner == null) {
125
139
  watchLintRunner = new LintWithProgramRunner({
126
140
  cwd: info.cwd,
@@ -131,11 +145,12 @@ async function rebuildAll(): Promise<CombinedBuildEvent> {
131
145
  program: tscResult.program,
132
146
  affectedFiles: tscResult.affectedFiles,
133
147
  });
148
+ logger.debug(`[${info.name}] lint 완료`);
134
149
  }
135
150
 
151
+ logger.debug(`[${info.name}] rebuildAll 완료`);
136
152
  return {
137
- js: { success: tscResult.success, errors: tscResult.errors },
138
- dts: { success: tscResult.success, errors: tscResult.errors },
153
+ build: { success: tscResult.success, errors: tscResult.errors },
139
154
  lint,
140
155
  };
141
156
  }
@@ -153,18 +168,33 @@ async function startWatch(info: LibraryBuildInfo): Promise<void> {
153
168
  const { workspaceDeps, replaceDeps } = collectDeps(info.pkgDir, info.cwd, info.replaceDeps);
154
169
 
155
170
  // Start FsWatcher — own src/ + workspace deps' src/ + replaceDeps dist/
171
+ logger.debug(`[${info.name}] FsWatcher 시작`);
156
172
  const watchPaths = [
157
- path.join(info.pkgDir, "src", "**", "*.ts"),
158
- ...workspaceDeps.map((d) => path.join(info.cwd, "packages", d, "src", "**", "*.ts")),
173
+ pathx.posixResolve(info.pkgDir, "src", "**", "*.ts"),
174
+ ...workspaceDeps.map((d) => pathx.posixResolve(info.cwd, "packages", d, "src", "**", "*.ts")),
159
175
  ...replaceDeps.flatMap((pkg) => [
160
- path.join(info.cwd, "node_modules", ...pkg.split("/"), "dist", "**", "*.{js,mjs,cjs}"),
161
- path.join(info.pkgDir, "node_modules", ...pkg.split("/"), "dist", "**", "*.{js,mjs,cjs}"),
176
+ pathx.posixResolve(info.cwd, "node_modules", ...pkg.split("/"), "dist", "**", "*.{js,mjs,cjs}"),
177
+ pathx.posixResolve(info.pkgDir, "node_modules", ...pkg.split("/"), "dist", "**", "*.{js,mjs,cjs}"),
162
178
  ]),
163
179
  ];
164
180
  fsWatcher = await FsWatcher.watch(watchPaths);
165
181
 
166
- fsWatcher.onChange({ delay: 300 }, async () => {
182
+ fsWatcher.onChange({ delay: 300 }, async (changes) => {
167
183
  try {
184
+ const hasFileAddOrRemove = changes.some(
185
+ (c) => c.event === "add" || c.event === "unlink",
186
+ );
187
+
188
+ if (!hasFileAddOrRemove && lastSourceFilePaths != null) {
189
+ const hasRelevantChange = changes.some((c) =>
190
+ lastSourceFilePaths!.has(c.path),
191
+ );
192
+ if (!hasRelevantChange) {
193
+ logger.debug("변경된 파일이 빌드에 포함되지 않아 리빌드 건너뜀");
194
+ return;
195
+ }
196
+ }
197
+
168
198
  sender.send("buildStart", {});
169
199
  const result = await rebuildAll();
170
200
  sender.send("build", result);