@simplysm/sd-cli 14.0.11 → 14.0.13

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/README.md +58 -253
  2. package/dist/angular/client-transform-stylesheet.js +1 -1
  3. package/dist/angular/client-transform-stylesheet.js.map +1 -1
  4. package/dist/angular/vite-angular-plugin.d.ts +4 -2
  5. package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
  6. package/dist/angular/vite-angular-plugin.js +73 -36
  7. package/dist/angular/vite-angular-plugin.js.map +1 -1
  8. package/dist/angular/vite-postcss-inline-plugin.d.ts +1 -1
  9. package/dist/angular/vite-postcss-inline-plugin.js +1 -1
  10. package/dist/capacitor/capacitor.d.ts +20 -2
  11. package/dist/capacitor/capacitor.d.ts.map +1 -1
  12. package/dist/capacitor/capacitor.js +155 -28
  13. package/dist/capacitor/capacitor.js.map +1 -1
  14. package/dist/commands/build.d.ts +3 -10
  15. package/dist/commands/build.d.ts.map +1 -1
  16. package/dist/commands/build.js +3 -10
  17. package/dist/commands/build.js.map +1 -1
  18. package/dist/commands/check.js +3 -3
  19. package/dist/commands/check.js.map +1 -1
  20. package/dist/commands/dev.d.ts +3 -9
  21. package/dist/commands/dev.d.ts.map +1 -1
  22. package/dist/commands/dev.js +3 -9
  23. package/dist/commands/dev.js.map +1 -1
  24. package/dist/commands/device.d.ts +3 -3
  25. package/dist/commands/device.js +5 -5
  26. package/dist/commands/device.js.map +1 -1
  27. package/dist/commands/publish.d.ts +1 -1
  28. package/dist/commands/publish.d.ts.map +1 -1
  29. package/dist/commands/publish.js +24 -26
  30. package/dist/commands/publish.js.map +1 -1
  31. package/dist/commands/replace-deps.d.ts +3 -3
  32. package/dist/commands/replace-deps.d.ts.map +1 -1
  33. package/dist/commands/replace-deps.js +1 -1
  34. package/dist/commands/typecheck.d.ts +4 -3
  35. package/dist/commands/typecheck.d.ts.map +1 -1
  36. package/dist/commands/typecheck.js +5 -11
  37. package/dist/commands/typecheck.js.map +1 -1
  38. package/dist/commands/watch.d.ts +9 -9
  39. package/dist/commands/watch.js +9 -9
  40. package/dist/electron/electron.d.ts.map +1 -1
  41. package/dist/electron/electron.js +42 -3
  42. package/dist/electron/electron.js.map +1 -1
  43. package/dist/engines/BaseEngine.d.ts +1 -1
  44. package/dist/engines/BaseEngine.d.ts.map +1 -1
  45. package/dist/engines/BaseEngine.js +3 -1
  46. package/dist/engines/BaseEngine.js.map +1 -1
  47. package/dist/engines/NgtscEngine.d.ts +7 -7
  48. package/dist/engines/NgtscEngine.d.ts.map +1 -1
  49. package/dist/engines/NgtscEngine.js +3 -3
  50. package/dist/engines/ServerEsbuildEngine.d.ts +7 -7
  51. package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -1
  52. package/dist/engines/ServerEsbuildEngine.js +3 -3
  53. package/dist/engines/TscEngine.d.ts +7 -7
  54. package/dist/engines/TscEngine.d.ts.map +1 -1
  55. package/dist/engines/TscEngine.js +3 -3
  56. package/dist/engines/ViteEngine.d.ts +7 -1
  57. package/dist/engines/ViteEngine.d.ts.map +1 -1
  58. package/dist/engines/ViteEngine.js +13 -12
  59. package/dist/engines/ViteEngine.js.map +1 -1
  60. package/dist/engines/index.d.ts +9 -5
  61. package/dist/engines/index.d.ts.map +1 -1
  62. package/dist/engines/index.js +7 -5
  63. package/dist/engines/index.js.map +1 -1
  64. package/dist/engines/types.d.ts +20 -20
  65. package/dist/engines/types.d.ts.map +1 -1
  66. package/dist/infra/ResultCollector.d.ts +9 -9
  67. package/dist/infra/ResultCollector.js +8 -8
  68. package/dist/infra/SignalHandler.d.ts +7 -7
  69. package/dist/infra/SignalHandler.js +7 -7
  70. package/dist/infra/WorkerManager.d.ts +14 -14
  71. package/dist/infra/WorkerManager.js +14 -14
  72. package/dist/orchestrators/BuildOrchestrator.d.ts +25 -25
  73. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  74. package/dist/orchestrators/BuildOrchestrator.js +34 -30
  75. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  76. package/dist/orchestrators/DevWatchOrchestrator.d.ts +7 -7
  77. package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
  78. package/dist/orchestrators/DevWatchOrchestrator.js +34 -34
  79. package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
  80. package/dist/sd-cli-entry.d.ts +2 -2
  81. package/dist/sd-cli-entry.d.ts.map +1 -1
  82. package/dist/sd-cli-entry.js +15 -8
  83. package/dist/sd-cli-entry.js.map +1 -1
  84. package/dist/sd-cli.d.ts +3 -3
  85. package/dist/sd-cli.js +16 -16
  86. package/dist/sd-cli.js.map +1 -1
  87. package/dist/sd-config.types.d.ts +105 -105
  88. package/dist/sd-config.types.d.ts.map +1 -1
  89. package/dist/utils/angular-compiler.js +5 -5
  90. package/dist/utils/angular-compiler.js.map +1 -1
  91. package/dist/utils/build-env.d.ts +1 -1
  92. package/dist/utils/build-env.js +1 -1
  93. package/dist/utils/concurrency.d.ts +7 -7
  94. package/dist/utils/concurrency.js +7 -7
  95. package/dist/utils/copy-public.d.ts +9 -9
  96. package/dist/utils/copy-public.js +17 -17
  97. package/dist/utils/copy-public.js.map +1 -1
  98. package/dist/utils/copy-src.d.ts +9 -9
  99. package/dist/utils/copy-src.js +11 -11
  100. package/dist/utils/copy-src.js.map +1 -1
  101. package/dist/utils/engine-stop.d.ts +8 -9
  102. package/dist/utils/engine-stop.d.ts.map +1 -1
  103. package/dist/utils/engine-stop.js +9 -10
  104. package/dist/utils/engine-stop.js.map +1 -1
  105. package/dist/utils/esbuild-config.d.ts +23 -23
  106. package/dist/utils/esbuild-config.d.ts.map +1 -1
  107. package/dist/utils/esbuild-config.js +25 -25
  108. package/dist/utils/esbuild-config.js.map +1 -1
  109. package/dist/utils/lint-with-program.d.ts +15 -15
  110. package/dist/utils/lint-with-program.d.ts.map +1 -1
  111. package/dist/utils/lint-with-program.js +29 -29
  112. package/dist/utils/lint-with-program.js.map +1 -1
  113. package/dist/utils/ngtsc-build-core.d.ts +8 -8
  114. package/dist/utils/ngtsc-build-core.d.ts.map +1 -1
  115. package/dist/utils/ngtsc-build-core.js +14 -14
  116. package/dist/utils/ngtsc-build-core.js.map +1 -1
  117. package/dist/utils/output-path-rewriter.d.ts +14 -14
  118. package/dist/utils/output-path-rewriter.js +18 -18
  119. package/dist/utils/output-path-rewriter.js.map +1 -1
  120. package/dist/utils/output-utils.d.ts +6 -6
  121. package/dist/utils/output-utils.js +11 -11
  122. package/dist/utils/output-utils.js.map +1 -1
  123. package/dist/utils/package-utils.d.ts +21 -21
  124. package/dist/utils/package-utils.d.ts.map +1 -1
  125. package/dist/utils/package-utils.js +56 -45
  126. package/dist/utils/package-utils.js.map +1 -1
  127. package/dist/utils/replace-deps.d.ts +25 -25
  128. package/dist/utils/replace-deps.d.ts.map +1 -1
  129. package/dist/utils/replace-deps.js +84 -65
  130. package/dist/utils/replace-deps.js.map +1 -1
  131. package/dist/utils/sd-config.d.ts +3 -3
  132. package/dist/utils/sd-config.js +3 -3
  133. package/dist/utils/tsc-build.d.ts +13 -13
  134. package/dist/utils/tsc-build.d.ts.map +1 -1
  135. package/dist/utils/tsc-build.js +9 -9
  136. package/dist/utils/tsc-build.js.map +1 -1
  137. package/dist/utils/tsconfig.d.ts +11 -9
  138. package/dist/utils/tsconfig.d.ts.map +1 -1
  139. package/dist/utils/tsconfig.js +11 -9
  140. package/dist/utils/tsconfig.js.map +1 -1
  141. package/dist/utils/typecheck-non-package.d.ts +5 -6
  142. package/dist/utils/typecheck-non-package.d.ts.map +1 -1
  143. package/dist/utils/typecheck-non-package.js +7 -8
  144. package/dist/utils/typecheck-non-package.js.map +1 -1
  145. package/dist/utils/typecheck-serialization.d.ts +8 -8
  146. package/dist/utils/typecheck-serialization.d.ts.map +1 -1
  147. package/dist/utils/typecheck-serialization.js +12 -16
  148. package/dist/utils/typecheck-serialization.js.map +1 -1
  149. package/dist/utils/vite-config.d.ts +12 -5
  150. package/dist/utils/vite-config.d.ts.map +1 -1
  151. package/dist/utils/vite-config.js +95 -41
  152. package/dist/utils/vite-config.js.map +1 -1
  153. package/dist/utils/vite-scope-watch-plugin.d.ts.map +1 -1
  154. package/dist/utils/vite-scope-watch-plugin.js +1 -1
  155. package/dist/utils/vite-scope-watch-plugin.js.map +1 -1
  156. package/dist/utils/worker-events.d.ts +12 -12
  157. package/dist/utils/worker-events.d.ts.map +1 -1
  158. package/dist/utils/worker-events.js +10 -10
  159. package/dist/utils/worker-events.js.map +1 -1
  160. package/dist/utils/worker-utils.d.ts +12 -13
  161. package/dist/utils/worker-utils.d.ts.map +1 -1
  162. package/dist/utils/worker-utils.js +12 -13
  163. package/dist/utils/worker-utils.js.map +1 -1
  164. package/dist/vitest-plugin.d.ts.map +1 -1
  165. package/dist/vitest-plugin.js +5 -7
  166. package/dist/vitest-plugin.js.map +1 -1
  167. package/dist/workers/client.worker.d.ts +8 -2
  168. package/dist/workers/client.worker.d.ts.map +1 -1
  169. package/dist/workers/client.worker.js +215 -6
  170. package/dist/workers/client.worker.js.map +1 -1
  171. package/dist/workers/library-build.worker.d.ts +1 -1
  172. package/dist/workers/library-build.worker.d.ts.map +1 -1
  173. package/dist/workers/library-build.worker.js +7 -7
  174. package/dist/workers/library-build.worker.js.map +1 -1
  175. package/dist/workers/lint.worker.d.ts +2 -2
  176. package/dist/workers/lint.worker.js +2 -2
  177. package/dist/workers/ngtsc-build.worker.js +30 -30
  178. package/dist/workers/ngtsc-build.worker.js.map +1 -1
  179. package/dist/workers/server-build.worker.d.ts +17 -17
  180. package/dist/workers/server-build.worker.d.ts.map +1 -1
  181. package/dist/workers/server-build.worker.js +46 -46
  182. package/dist/workers/server-build.worker.js.map +1 -1
  183. package/dist/workers/server-runtime.worker.d.ts +7 -7
  184. package/dist/workers/server-runtime.worker.d.ts.map +1 -1
  185. package/dist/workers/server-runtime.worker.js +17 -17
  186. package/dist/workers/server-runtime.worker.js.map +1 -1
  187. package/docs/config.md +340 -0
  188. package/docs/publish-configuration-types.md +87 -0
  189. package/docs/pwa-configuration-types.md +55 -0
  190. package/docs/vitest-plugin.md +47 -0
  191. package/package.json +9 -7
  192. package/src/angular/client-transform-stylesheet.ts +1 -1
  193. package/src/angular/vite-angular-plugin.ts +89 -39
  194. package/src/angular/vite-postcss-inline-plugin.ts +1 -1
  195. package/src/capacitor/capacitor.ts +185 -38
  196. package/src/commands/build.ts +3 -10
  197. package/src/commands/check.ts +3 -3
  198. package/src/commands/dev.ts +3 -9
  199. package/src/commands/device.ts +5 -5
  200. package/src/commands/publish.ts +30 -26
  201. package/src/commands/replace-deps.ts +3 -3
  202. package/src/commands/typecheck.ts +7 -13
  203. package/src/commands/watch.ts +9 -9
  204. package/src/electron/electron.ts +49 -4
  205. package/src/engines/BaseEngine.ts +4 -1
  206. package/src/engines/NgtscEngine.ts +7 -7
  207. package/src/engines/ServerEsbuildEngine.ts +7 -7
  208. package/src/engines/TscEngine.ts +7 -7
  209. package/src/engines/ViteEngine.ts +18 -13
  210. package/src/engines/index.ts +11 -5
  211. package/src/engines/types.ts +20 -20
  212. package/src/infra/ResultCollector.ts +9 -9
  213. package/src/infra/SignalHandler.ts +7 -7
  214. package/src/infra/WorkerManager.ts +14 -14
  215. package/src/orchestrators/BuildOrchestrator.ts +42 -38
  216. package/src/orchestrators/DevWatchOrchestrator.ts +36 -36
  217. package/src/sd-cli-entry.ts +15 -8
  218. package/src/sd-cli.ts +16 -16
  219. package/src/sd-config.types.ts +107 -107
  220. package/src/utils/angular-compiler.ts +5 -5
  221. package/src/utils/build-env.ts +1 -1
  222. package/src/utils/concurrency.ts +7 -7
  223. package/src/utils/copy-public.ts +17 -17
  224. package/src/utils/copy-src.ts +11 -11
  225. package/src/utils/engine-stop.ts +9 -10
  226. package/src/utils/esbuild-config.ts +29 -29
  227. package/src/utils/lint-with-program.ts +34 -34
  228. package/src/utils/ngtsc-build-core.ts +17 -17
  229. package/src/utils/output-path-rewriter.ts +18 -18
  230. package/src/utils/output-utils.ts +11 -11
  231. package/src/utils/package-utils.ts +57 -45
  232. package/src/utils/replace-deps.ts +92 -67
  233. package/src/utils/sd-config.ts +3 -3
  234. package/src/utils/tsc-build.ts +18 -18
  235. package/src/utils/tsconfig.ts +11 -9
  236. package/src/utils/typecheck-non-package.ts +7 -8
  237. package/src/utils/typecheck-serialization.ts +13 -15
  238. package/src/utils/vite-config.ts +108 -46
  239. package/src/utils/vite-scope-watch-plugin.ts +6 -1
  240. package/src/utils/worker-events.ts +16 -16
  241. package/src/utils/worker-utils.ts +12 -13
  242. package/src/vitest-plugin.ts +5 -8
  243. package/src/workers/client.worker.ts +246 -7
  244. package/src/workers/library-build.worker.ts +8 -8
  245. package/src/workers/lint.worker.ts +2 -2
  246. package/src/workers/ngtsc-build.worker.ts +31 -31
  247. package/src/workers/server-build.worker.ts +60 -60
  248. package/src/workers/server-runtime.worker.ts +22 -22
  249. package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +1 -0
  250. package/tests/angular/vite-angular-plugin-hmr.spec.ts +78 -0
  251. package/tests/angular/vite-angular-plugin.spec.ts +67 -0
  252. package/tests/capacitor/capacitor-build.spec.ts +93 -11
  253. package/tests/capacitor/capacitor-icon.spec.ts +7 -5
  254. package/tests/capacitor/capacitor-init.spec.ts +124 -10
  255. package/tests/capacitor/capacitor-run.spec.ts +14 -17
  256. package/tests/capacitor/capacitor-workspace.spec.ts +5 -3
  257. package/tests/commands/check.spec.ts +2 -2
  258. package/tests/commands/publish.spec.ts +2 -2
  259. package/tests/commands/typecheck.spec.ts +8 -0
  260. package/tests/electron/electron.spec.ts +12 -10
  261. package/tests/engines/base-engine.spec.ts +37 -0
  262. package/tests/engines/vite-engine.spec.ts +115 -3
  263. package/tests/utils/vite-config.spec.ts +162 -90
  264. package/tests/workers/client-worker.spec.ts +690 -0
  265. package/tests/workers/server-build-worker.spec.ts +3 -3
@@ -25,48 +25,48 @@ import { copyPublicFiles, watchPublicFiles } from "../utils/copy-public";
25
25
  //#region Types
26
26
 
27
27
  /**
28
- * Server build information (one-time build)
28
+ * 서버 빌드 정보 (일회성 빌드)
29
29
  */
30
30
  export interface ServerBuildInfo {
31
31
  name: string;
32
32
  cwd: string;
33
33
  pkgDir: string;
34
34
  output: BuildOutput;
35
- /** Environment variables to substitute during build */
35
+ /** 빌드 치환할 환경변수 */
36
36
  env?: Record<string, string>;
37
- /** Runtime configuration (recorded in dist/.config.json) */
37
+ /** 런타임 설정 (dist/.config.json에 기록) */
38
38
  configs?: Record<string, unknown>;
39
- /** External modules manually specified in sd.config.ts */
39
+ /** sd.config.ts에서 수동 지정한 외부 모듈 */
40
40
  externals?: string[];
41
- /** PM2 configuration (generates dist/pm2.config.cjs when specified) */
41
+ /** PM2 설정 (지정 dist/pm2.config.cjs 생성) */
42
42
  pm2?: {
43
43
  name?: string;
44
44
  ignoreWatchPaths?: string[];
45
45
  };
46
- /** Package manager to use (affects mise.toml or volta settings generation) */
46
+ /** 사용할 패키지 매니저 (mise.toml 또는 volta 설정 생성에 영향) */
47
47
  packageManager?: "volta" | "mise";
48
48
  }
49
49
 
50
50
  /**
51
- * Server watch information
51
+ * 서버 watch 정보
52
52
  */
53
53
  export interface ServerWatchInfo {
54
54
  name: string;
55
55
  cwd: string;
56
56
  pkgDir: string;
57
57
  output: BuildOutput;
58
- /** Environment variables to substitute during build */
58
+ /** 빌드 치환할 환경변수 */
59
59
  env?: Record<string, string>;
60
- /** Runtime configuration (recorded in dist/.config.json) */
60
+ /** 런타임 설정 (dist/.config.json에 기록) */
61
61
  configs?: Record<string, unknown>;
62
- /** External modules manually specified in sd.config.ts */
62
+ /** sd.config.ts에서 수동 지정한 외부 모듈 */
63
63
  externals?: string[];
64
- /** replaceDeps configuration from sd.config.ts */
64
+ /** sd.config.ts replaceDeps 설정 */
65
65
  replaceDeps?: Record<string, string>;
66
66
  }
67
67
 
68
68
  /**
69
- * Server build result (aligned with LibraryBuildResult + mainJsPath)
69
+ * 서버 빌드 결과 (LibraryBuildResult + mainJsPath 형태)
70
70
  */
71
71
  export interface ServerBuildResult {
72
72
  build: { success: boolean; errors?: string[]; warnings?: string[]; diagnostics: SerializedDiagnostic[] };
@@ -75,7 +75,7 @@ export interface ServerBuildResult {
75
75
  }
76
76
 
77
77
  /**
78
- * Combined build event for watch mode
78
+ * watch 모드용 통합 빌드 이벤트
79
79
  */
80
80
  export interface ServerCombinedBuildEvent {
81
81
  build: { success: boolean; errors?: string[]; warnings?: string[] };
@@ -84,7 +84,7 @@ export interface ServerCombinedBuildEvent {
84
84
  }
85
85
 
86
86
  /**
87
- * Worker event types
87
+ * 워커 이벤트 타입
88
88
  */
89
89
  export interface ServerBuildWorkerEvents extends Record<string, unknown> {
90
90
  buildStart: Record<string, never>;
@@ -100,16 +100,16 @@ applyDebugLevel();
100
100
 
101
101
  const logger = consola.withTag("sd:cli:server-build:worker");
102
102
 
103
- /** esbuild build context (to be cleaned up) */
103
+ /** esbuild 빌드 컨텍스트 (정리 대상) */
104
104
  let esbuildContext: esbuild.BuildContext | undefined;
105
105
 
106
- /** Last build metafile (for filtering changed files on rebuild) */
106
+ /** 마지막 빌드 metafile (리빌드 변경 파일 필터링용) */
107
107
  let lastMetafile: esbuild.Metafile | undefined;
108
108
 
109
- /** Public files watcher (to be cleaned up) */
109
+ /** public 파일 감시자 (정리 대상) */
110
110
  let publicWatcher: FsWatcher | undefined;
111
111
 
112
- /** Source + scope packages watcher (to be cleaned up) */
112
+ /** 소스 + 스코프 패키지 감시자 (정리 대상) */
113
113
  let srcWatcher: FsWatcher | undefined;
114
114
 
115
115
  async function cleanup(): Promise<void> {
@@ -135,8 +135,8 @@ async function cleanup(): Promise<void> {
135
135
  }
136
136
 
137
137
  /**
138
- * Collect external modules from three sources and merge them.
139
- * Uses single-pass dependency tree traversal via collectAllDependencyExternals.
138
+ * 가지 소스에서 외부 모듈을 수집하고 병합한다.
139
+ * collectAllDependencyExternals를 통한 단일 패스 의존성 트리 순회를 사용한다.
140
140
  */
141
141
  function collectAllExternals(pkgDir: string, manualExternals?: string[]): string[] {
142
142
  logger.debug("의존성 트리 스캔 중...");
@@ -147,9 +147,9 @@ function collectAllExternals(pkgDir: string, manualExternals?: string[]): string
147
147
  }
148
148
 
149
149
  /**
150
- * Parse pnpm-lock.yaml packages section to build a name→version map.
151
- * Lockfile v9 format: `packages:` section with `'name@version':` keys.
152
- * Uses simple line-based parsing to avoid YAML parser dependency.
150
+ * pnpm-lock.yaml packages 섹션을 파싱하여 name→version 맵을 생성한다.
151
+ * Lockfile v9 형식: `packages:` 섹션의 `'name@version':` 키를 파싱한다.
152
+ * YAML 파서 의존성을 피하기 위해 단순 라인 기반 파싱을 사용한다.
153
153
  */
154
154
  function parseLockfileVersions(cwd: string): Map<string, string> {
155
155
  const lockfilePath = path.join(cwd, "pnpm-lock.yaml");
@@ -160,7 +160,7 @@ function parseLockfileVersions(cwd: string): Map<string, string> {
160
160
  const content = fs.readFileSync(lockfilePath, "utf-8");
161
161
  const map = new Map<string, string>();
162
162
 
163
- // Find "packages:" section and parse entries like "'@scope/name@1.2.3':" or "'name@1.2.3':"
163
+ // "packages:" 섹션을 찾고 "'@scope/name@1.2.3':" 또는 "'name@1.2.3':" 형태의 항목을 파싱
164
164
  const lines = content.split("\n");
165
165
  let inPackages = false;
166
166
  for (const line of lines) {
@@ -169,16 +169,16 @@ function parseLockfileVersions(cwd: string): Map<string, string> {
169
169
  continue;
170
170
  }
171
171
  if (inPackages && line.length > 0 && !line.startsWith(" ") && !line.startsWith("'")) {
172
- break; // Next top-level section
172
+ break; // 다음 최상위 섹션
173
173
  }
174
174
  if (!inPackages) continue;
175
175
 
176
- // Match "'@scope/name@version':" or "'name@version':"
176
+ // "'@scope/name@version':" 또는 "'name@version':" 매칭
177
177
  const match = /^\s{2}'(.+)@(\d[^']*)':\s*$/.exec(line);
178
178
  if (match != null) {
179
179
  const name = match[1];
180
180
  const version = match[2];
181
- // Keep first occurrence (lockfile lists each version once)
181
+ // 번째 항목 유지 (lockfile 버전을 번만 기록)
182
182
  if (!map.has(name)) {
183
183
  map.set(name, version);
184
184
  }
@@ -189,8 +189,8 @@ function parseLockfileVersions(cwd: string): Map<string, string> {
189
189
  }
190
190
 
191
191
  /**
192
- * Resolve locked versions of all given packages from pnpm-lock.yaml.
193
- * Throws if any package is not found in the lockfile.
192
+ * pnpm-lock.yaml에서 주어진 모든 패키지의 잠긴 버전을 확인한다.
193
+ * lockfile에서 패키지를 찾을 없으면 에러를 던진다.
194
194
  */
195
195
  function resolveLockedVersions(cwd: string, pkgNames: string[]): Record<string, string> {
196
196
  const versionMap = parseLockfileVersions(cwd);
@@ -209,7 +209,7 @@ function resolveLockedVersions(cwd: string, pkgNames: string[]): Record<string,
209
209
  }
210
210
 
211
211
  /**
212
- * Generate files for production deployment
212
+ * 프로덕션 배포용 파일을 생성한다
213
213
  */
214
214
  function generateProductionFiles(
215
215
  info: ServerBuildInfo,
@@ -228,7 +228,7 @@ function generateProductionFiles(
228
228
  distPkgJson["dependencies"] = resolveLockedVersions(info.cwd, externals);
229
229
  }
230
230
  if (info.packageManager === "volta") {
231
- const nodeVersion = cpx.execSync("node", ["-v"]).stdout.trim();
231
+ const nodeVersion = cpx.spawnSync("node", ["-v"]).stdout.trim();
232
232
  distPkgJson["volta"] = { node: nodeVersion };
233
233
  }
234
234
  fs.writeFileSync(path.join(distDir, "package.json"), JSON.stringify(distPkgJson, undefined, 2));
@@ -319,21 +319,21 @@ registerCleanupHandlers(cleanup, logger);
319
319
  //#region Worker
320
320
 
321
321
  /**
322
- * One-time build (production)
322
+ * 일회성 빌드 (프로덕션)
323
323
  */
324
324
  async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
325
325
  const mainJsPath = pathx.posixResolve(info.pkgDir, "dist", "main.js");
326
326
  logger.debug(`[${info.name}] server worker build 시작 (js: ${info.output.js}, dts: ${info.output.dts})`);
327
327
 
328
328
  try {
329
- // Parse tsconfig
329
+ // tsconfig 파싱
330
330
  const parsedConfig = parseTsconfig(info.pkgDir);
331
331
  const entryPoints = getPackageSourceFiles(info.pkgDir, parsedConfig);
332
332
 
333
- // Collect externals
333
+ // 외부 모듈 수집
334
334
  const external = collectAllExternals(info.pkgDir, info.externals);
335
335
 
336
- // esbuild (async) ‖ tsc (sync) in parallel
336
+ // esbuild (비동기) ‖ tsc (동기) 병렬 실행
337
337
  const esbuildOptions = createServerEsbuildOptions({
338
338
  pkgDir: info.pkgDir,
339
339
  entryPoints,
@@ -360,7 +360,7 @@ async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
360
360
  }))
361
361
  : null;
362
362
 
363
- // tsc typecheck (always runs, emit controlled by output.dts)
363
+ // tsc 타입체크 (항상 실행, emit output.dts로 제어)
364
364
  const tscResult = runTscPackageBuild({
365
365
  pkgDir: info.pkgDir,
366
366
  cwd: info.cwd,
@@ -374,7 +374,7 @@ async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
374
374
  ? await esbuildPromise
375
375
  : { success: true, errors: undefined, warnings: undefined };
376
376
 
377
- // Run lint if enabled and program is available
377
+ // lint 실행 (활성화 + program 사용 가능 시)
378
378
  let lint: LintWithProgramResult | undefined;
379
379
  if (info.output.lint === true && tscResult.program != null) {
380
380
  logger.debug(`[${info.name}] lint 시작`);
@@ -386,7 +386,7 @@ async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
386
386
  logger.debug(`[${info.name}] lint 완료`);
387
387
  }
388
388
 
389
- // Generate production artifacts only when JS output is requested
389
+ // JS 출력이 요청된 경우에만 프로덕션 아티팩트 생성
390
390
  if (info.output.js) {
391
391
  const confDistPath = path.join(info.pkgDir, "dist", ".config.json");
392
392
  fs.writeFileSync(confDistPath, JSON.stringify(info.configs ?? {}, undefined, 2));
@@ -424,12 +424,12 @@ async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
424
424
 
425
425
  const guardStartWatch = createOnceGuard("startWatch");
426
426
 
427
- // Mutable state for watch mode
427
+ // watch 모드용 가변 상태
428
428
  let watchInfo: ServerWatchInfo | undefined;
429
429
  let watchLintRunner: LintWithProgramRunner | undefined;
430
430
 
431
431
  /**
432
- * Rebuild esbuild + tsc in parallel (watch mode)
432
+ * esbuild + tsc 병렬 리빌드 (watch 모드)
433
433
  */
434
434
  async function rebuildAll(): Promise<ServerCombinedBuildEvent> {
435
435
  const info = watchInfo!;
@@ -437,11 +437,11 @@ async function rebuildAll(): Promise<ServerCombinedBuildEvent> {
437
437
  const mainJsPath = pathx.posixResolve(info.pkgDir, "dist", "main.js");
438
438
  const parsedConfig = parseTsconfig(info.pkgDir);
439
439
 
440
- // esbuild rebuild (async)
440
+ // esbuild 리빌드 (비동기)
441
441
  let esbuildPromise: Promise<{ success: boolean; errors?: string[]; warnings?: string[] }> | null = null;
442
442
  if (info.output.js && esbuildContext != null) {
443
443
  esbuildPromise = esbuildContext.rebuild().then(async (result) => {
444
- // Save metafile
444
+ // metafile 저장
445
445
  if (result.metafile != null) {
446
446
  lastMetafile = result.metafile;
447
447
  }
@@ -459,7 +459,7 @@ async function rebuildAll(): Promise<ServerCombinedBuildEvent> {
459
459
  });
460
460
  }
461
461
 
462
- // tsc rebuild (sync, incremental)
462
+ // tsc 리빌드 (동기, 증분)
463
463
  const tscResult = runTscPackageBuild({
464
464
  pkgDir: info.pkgDir,
465
465
  cwd: info.cwd,
@@ -469,7 +469,7 @@ async function rebuildAll(): Promise<ServerCombinedBuildEvent> {
469
469
  includeTests: info.output.includeTests,
470
470
  });
471
471
 
472
- // Run lint if enabled and program is available
472
+ // lint 실행 (활성화 + program 사용 가능 시)
473
473
  let lint: LintWithProgramResult | undefined;
474
474
  if (info.output.lint === true && tscResult.program != null) {
475
475
  logger.debug(`[${info.name}] lint 시작`);
@@ -504,7 +504,7 @@ async function rebuildAll(): Promise<ServerCombinedBuildEvent> {
504
504
  }
505
505
 
506
506
  /**
507
- * Create esbuild context for watch mode
507
+ * watch 모드용 esbuild 컨텍스트를 생성한다
508
508
  */
509
509
  async function createEsbuildWatchContext(
510
510
  info: ServerWatchInfo,
@@ -527,7 +527,7 @@ async function createEsbuildWatchContext(
527
527
  }
528
528
 
529
529
  /**
530
- * Start watch mode
530
+ * watch 모드 시작
531
531
  */
532
532
  async function startWatch(info: ServerWatchInfo): Promise<void> {
533
533
  guardStartWatch();
@@ -538,33 +538,33 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
538
538
  const parsedConfig = parseTsconfig(info.pkgDir);
539
539
  const entryPoints = getPackageSourceFiles(info.pkgDir, parsedConfig);
540
540
 
541
- // Collect externals (cached for watch mode)
541
+ // 외부 모듈 수집 (watch 모드용 캐시)
542
542
  let cachedExternal = collectAllExternals(info.pkgDir, info.externals);
543
543
 
544
- // Create esbuild context (if JS output needed)
544
+ // esbuild 컨텍스트 생성 (JS 출력 필요 시)
545
545
  if (info.output.js) {
546
546
  esbuildContext = await createEsbuildWatchContext(info, entryPoints, cachedExternal);
547
547
  }
548
548
 
549
- // Initial build: esbuild + tsc parallel
549
+ // 초기 빌드: esbuild + tsc 병렬
550
550
  sender.send("buildStart", {});
551
551
  const initialResult = await rebuildAll();
552
552
 
553
- // Write .config.json on first build
553
+ // 빌드 시 .config.json 작성
554
554
  const confDistPath = path.join(info.pkgDir, "dist", ".config.json");
555
555
  fs.writeFileSync(confDistPath, JSON.stringify(info.configs ?? {}, undefined, 2));
556
556
 
557
557
  sender.send("build", initialResult);
558
558
 
559
- // Watch public/ + public-dev/
559
+ // public/ + public-dev/ 감시
560
560
  publicWatcher = await watchPublicFiles(info.pkgDir, true);
561
561
 
562
- // Collect watch paths based on dependencies
562
+ // 의존성 기반 감시 경로 수집
563
563
  const { workspaceDeps, replaceDeps } = collectDeps(info.pkgDir, info.cwd, info.replaceDeps);
564
564
 
565
565
  const watchPaths: string[] = [];
566
566
 
567
- // Server package itself + workspace dependency packages source
567
+ // 서버 패키지 자체 + workspace 의존성 패키지 소스
568
568
  const watchDirs = [
569
569
  info.pkgDir,
570
570
  ...workspaceDeps.map((d) => pathx.posixResolve(info.cwd, "packages", d)),
@@ -573,7 +573,7 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
573
573
  watchPaths.push(pathx.posixResolve(dir, "src", "**", "*"));
574
574
  }
575
575
 
576
- // ReplaceDeps dependency packages dist
576
+ // replaceDeps 의존성 패키지 dist
577
577
  for (const pkg of replaceDeps) {
578
578
  watchPaths.push(pathx.posixResolve(info.cwd, "node_modules", ...pkg.split("/"), "dist", "**", "*.{js,mjs,cjs}"));
579
579
  watchPaths.push(
@@ -581,7 +581,7 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
581
581
  );
582
582
  }
583
583
 
584
- // Start FsWatcher
584
+ // FsWatcher 시작
585
585
  srcWatcher = await FsWatcher.watch(watchPaths);
586
586
 
587
587
  srcWatcher.onChange({ delay: 300 }, async (changes) => {
@@ -591,11 +591,11 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
591
591
  if (hasFileAddOrRemove) {
592
592
  sender.send("buildStart", {});
593
593
 
594
- // Recreate context on file add/remove
594
+ // 파일 추가/삭제 컨텍스트 재생성
595
595
  const newParsedConfig = parseTsconfig(info.pkgDir);
596
596
  const newEntryPoints = getPackageSourceFiles(info.pkgDir, newParsedConfig);
597
597
 
598
- // Only re-collect externals when package.json changed
598
+ // package.json이 변경된 경우에만 외부 모듈 재수집
599
599
  const hasPackageJsonChange = changes.some((c) =>
600
600
  c.path.endsWith("package.json"),
601
601
  );
@@ -617,7 +617,7 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
617
617
  return;
618
618
  }
619
619
 
620
- // Only file changes: filter by metafile
620
+ // 파일 변경만 있는 경우: metafile로 필터링
621
621
  if (esbuildContext == null) {
622
622
  sender.send("buildStart", {});
623
623
  const result = await rebuildAll();
@@ -632,7 +632,7 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
632
632
  return;
633
633
  }
634
634
 
635
- // Filter by metafile inputs
635
+ // metafile 입력 기반 필터링
636
636
  const metafileAbsPaths = new Set(
637
637
  Object.keys(lastMetafile.inputs).map((key) => pathx.posixResolve(info.cwd, key)),
638
638
  );
@@ -656,7 +656,7 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
656
656
  }
657
657
 
658
658
  /**
659
- * Stop watch
659
+ * watch 중지
660
660
  */
661
661
  async function stopWatch(): Promise<void> {
662
662
  await cleanup();
@@ -9,31 +9,31 @@ import { registerCleanupHandlers, applyDebugLevel } from "../utils/worker-utils"
9
9
  //#region Types
10
10
 
11
11
  /**
12
- * Server runtime start info
12
+ * 서버 런타임 시작 정보
13
13
  */
14
14
  export interface ServerRuntimeStartInfo {
15
15
  mainJsPath: string;
16
- /** Client Vite dev server ports for @fastify/http-proxy registration */
16
+ /** @fastify/http-proxy 등록을 위한 Client Vite dev server 포트 */
17
17
  clientPorts?: Record<string, number>;
18
18
  env?: Record<string, string>;
19
19
  }
20
20
 
21
21
  /**
22
- * Server ready event
22
+ * 서버 준비 완료 이벤트
23
23
  */
24
24
  export interface ServerRuntimeReadyEvent {
25
25
  port: number;
26
26
  }
27
27
 
28
28
  /**
29
- * Error event
29
+ * 에러 이벤트
30
30
  */
31
31
  export interface ServerRuntimeErrorEvent {
32
32
  message: string;
33
33
  }
34
34
 
35
35
  /**
36
- * Worker event types
36
+ * 워커 이벤트 타입
37
37
  */
38
38
  export interface ServerRuntimeWorkerEvents extends Record<string, unknown> {
39
39
  serverReady: ServerRuntimeReadyEvent;
@@ -46,11 +46,11 @@ applyDebugLevel();
46
46
 
47
47
  const logger = consola.withTag("sd:cli:server-runtime:worker");
48
48
 
49
- /** Server instance (to be cleaned up) */
49
+ /** 서버 인스턴스 (정리 대상) */
50
50
  let serverInstance: { close: () => Promise<void> } | undefined;
51
51
 
52
52
  /**
53
- * Clean up resources
53
+ * 리소스 정리
54
54
  */
55
55
  async function cleanup(): Promise<void> {
56
56
  const server = serverInstance;
@@ -60,15 +60,15 @@ async function cleanup(): Promise<void> {
60
60
  }
61
61
  }
62
62
 
63
- // Catch runtime errors that occur after server listen() and send them as a custom "error" event
64
- // (Without this handler, the worker will crash but dev.ts's buildResolver won't be called, causing listr to hang)
63
+ // 서버 listen() 이후 발생하는 런타임 에러를 잡아 커스텀 "error" 이벤트로 전송
64
+ // ( 핸들러 없이는 워커가 크래시해도 빌드 Promise가 resolve되지 않아 프로세스가 중단될 있다)
65
65
  process.on("uncaughtException", (err) => {
66
66
  logger.error("서버 런타임 미처리 에러", err);
67
67
  sender.send("error", {
68
68
  message: errNs.message(err),
69
69
  });
70
- // Allow event to be sent before exit
71
- setTimeout(() => process.exit(1), 100);
70
+ // 이벤트 전송 종료할 있도록 대기
71
+ setTimeout(() => process.exit(1), 500);
72
72
  });
73
73
 
74
74
  process.on("unhandledRejection", (reason) => {
@@ -76,14 +76,14 @@ process.on("unhandledRejection", (reason) => {
76
76
  sender.send("error", {
77
77
  message: errNs.message(reason),
78
78
  });
79
- // Allow event to be sent before exit
80
- setTimeout(() => process.exit(1), 100);
79
+ // 이벤트 전송 종료할 있도록 대기
80
+ setTimeout(() => process.exit(1), 500);
81
81
  });
82
82
 
83
83
  registerCleanupHandlers(cleanup, logger);
84
84
 
85
85
  /**
86
- * Check if a port is available for use
86
+ * 포트가 사용 가능한지 확인한다
87
87
  */
88
88
  function isPortAvailable(port: number): Promise<boolean> {
89
89
  return new Promise((resolve) => {
@@ -97,7 +97,7 @@ function isPortAvailable(port: number): Promise<boolean> {
97
97
  }
98
98
 
99
99
  /**
100
- * Find and return an available port starting from the specified port
100
+ * 지정된 포트부터 사용 가능한 포트를 찾아 반환한다
101
101
  */
102
102
  async function findAvailablePort(startPort: number, maxRetries = 20): Promise<number> {
103
103
  for (let i = 0; i < maxRetries; i++) {
@@ -112,21 +112,21 @@ async function findAvailablePort(startPort: number, maxRetries = 20): Promise<nu
112
112
  }
113
113
 
114
114
  /**
115
- * Start Server Runtime
116
- * Import main.js, then listen
115
+ * 서버 런타임 시작
116
+ * main.js import한 listen 수행
117
117
  */
118
118
  async function start(info: ServerRuntimeStartInfo): Promise<void> {
119
119
  try {
120
120
  const startTime = performance.now();
121
121
 
122
- // Inject environment variables into process.env before importing main.js
122
+ // main.js import 전에 환경변수를 process.env 주입
123
123
  if (info.env != null) {
124
124
  for (const [key, value] of Object.entries(info.env)) {
125
125
  process.env[key] = value;
126
126
  }
127
127
  }
128
128
 
129
- // Import main.js (must export a server instance)
129
+ // main.js import (서버 인스턴스를 export해야 )
130
130
  logger.debug("main.js 임포트 중...");
131
131
  let stepStart = performance.now();
132
132
  const module = await import(pathToFileURL(info.mainJsPath).href);
@@ -137,10 +137,10 @@ async function start(info: ServerRuntimeStartInfo): Promise<void> {
137
137
  throw new Error("main.js must export a server instance.");
138
138
  }
139
139
 
140
- // Save server instance (for cleanup)
140
+ // 서버 인스턴스 저장 (정리용)
141
141
  serverInstance = server;
142
142
 
143
- // Register client proxies (before listen)
143
+ // 클라이언트 프록시 등록 (listen )
144
144
  if (info.clientPorts != null && Object.keys(info.clientPorts).length > 0) {
145
145
  for (const [name, port] of Object.entries(info.clientPorts)) {
146
146
  logger.debug(`프록시 등록: /${name} → http://127.0.0.1:${String(port)}`);
@@ -153,7 +153,7 @@ async function start(info: ServerRuntimeStartInfo): Promise<void> {
153
153
  }
154
154
  }
155
155
 
156
- // Find available port (auto-increment on port conflict)
156
+ // 사용 가능한 포트 탐색 (포트 충돌 자동 증가)
157
157
  logger.debug("사용 가능한 포트 탐색 중...");
158
158
  stepStart = performance.now();
159
159
  const originalPort = server.options.port;
@@ -195,6 +195,7 @@ describe("sdAngularPlugin HMR fallback", () => {
195
195
  const middlewares: Array<(req: IncomingMessage, res: ServerResponse, next: () => void) => void> =
196
196
  [];
197
197
  const mockServer = {
198
+ config: { base: "/" },
198
199
  middlewares: {
199
200
  use: (fn: (req: IncomingMessage, res: ServerResponse, next: () => void) => void) => {
200
201
  middlewares.push(fn);
@@ -17,6 +17,7 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
17
17
  const middlewares: Array<(req: IncomingMessage, res: ServerResponse, next: () => void) => void> =
18
18
  [];
19
19
  const mockServer = {
20
+ config: { base: "/" },
20
21
  middlewares: {
21
22
  use: (fn: (req: IncomingMessage, res: ServerResponse, next: () => void) => void) => {
22
23
  middlewares.push(fn);
@@ -72,6 +73,7 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
72
73
  const middlewares: Array<(req: IncomingMessage, res: ServerResponse, next: () => void) => void> =
73
74
  [];
74
75
  const mockServer = {
76
+ config: { base: "/" },
75
77
  middlewares: {
76
78
  use: (fn: (req: IncomingMessage, res: ServerResponse, next: () => void) => void) => {
77
79
  middlewares.push(fn);
@@ -101,6 +103,7 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
101
103
  const middlewares: Array<(req: IncomingMessage, res: ServerResponse, next: () => void) => void> =
102
104
  [];
103
105
  const mockServer = {
106
+ config: { base: "/" },
104
107
  middlewares: {
105
108
  use: (fn: (req: IncomingMessage, res: ServerResponse, next: () => void) => void) => {
106
109
  middlewares.push(fn);
@@ -138,6 +141,7 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
138
141
  const middlewares: Array<(req: IncomingMessage, res: ServerResponse, next: () => void) => void> =
139
142
  [];
140
143
  const mockServer = {
144
+ config: { base: "/" },
141
145
  middlewares: {
142
146
  use: (fn: (req: IncomingMessage, res: ServerResponse, next: () => void) => void) => {
143
147
  middlewares.push(fn);
@@ -177,6 +181,7 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
177
181
  const middlewares: Array<(req: IncomingMessage, res: ServerResponse, next: () => void) => void> =
178
182
  [];
179
183
  const mockServer = {
184
+ config: { base: "/" },
180
185
  middlewares: {
181
186
  use: (fn: (req: IncomingMessage, res: ServerResponse, next: () => void) => void) => {
182
187
  middlewares.push(fn);
@@ -223,4 +228,77 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
223
228
 
224
229
  await (plugin as any).buildEnd?.call({});
225
230
  });
231
+
232
+ // Acceptance: base path가 포함된 /@ng/component 요청도 정상 응답한다
233
+ it("serves /@ng/component requests with base path prefix", async () => {
234
+ const plugin = sdAngularPlugin({ tsconfig: TSCONFIG_PATH, dev: true });
235
+ await (plugin as any).buildStart?.call({});
236
+
237
+ const middlewares: Array<(req: IncomingMessage, res: ServerResponse, next: () => void) => void> =
238
+ [];
239
+ const mockServer = {
240
+ config: { base: "/client-pda/" },
241
+ middlewares: {
242
+ use: (fn: (req: IncomingMessage, res: ServerResponse, next: () => void) => void) => {
243
+ middlewares.push(fn);
244
+ },
245
+ },
246
+ httpServer: { on: vi.fn() },
247
+ };
248
+ (plugin as any).configureServer?.(mockServer);
249
+
250
+ const middleware = middlewares[0];
251
+ // 실제 브라우저에서는 /client-pda/src/services/@ng/component 형태로 요청됨
252
+ const mockReq = {
253
+ url: "/client-pda/src/services/@ng/component?c=testId",
254
+ } as IncomingMessage;
255
+
256
+ let statusCode: number | undefined;
257
+ let headers: Record<string, string> = {};
258
+ const mockRes = {
259
+ writeHead(code: number, hdrs: Record<string, string>) {
260
+ statusCode = code;
261
+ headers = hdrs;
262
+ },
263
+ end: vi.fn(),
264
+ } as unknown as ServerResponse;
265
+
266
+ const next = vi.fn();
267
+ middleware(mockReq, mockRes, next);
268
+
269
+ expect(statusCode).toBe(200);
270
+ expect(headers["Content-Type"]).toBe("text/javascript");
271
+ expect(next).not.toHaveBeenCalled();
272
+
273
+ await (plugin as any).buildEnd?.call({});
274
+ });
275
+
276
+ // Acceptance: base path가 있지만 @ng/component가 아닌 요청은 next()로 통과
277
+ it("passes through non-/@ng/component requests with base path", async () => {
278
+ const plugin = sdAngularPlugin({ tsconfig: TSCONFIG_PATH, dev: true });
279
+ await (plugin as any).buildStart?.call({});
280
+
281
+ const middlewares: Array<(req: IncomingMessage, res: ServerResponse, next: () => void) => void> =
282
+ [];
283
+ const mockServer = {
284
+ config: { base: "/client-pda/" },
285
+ middlewares: {
286
+ use: (fn: (req: IncomingMessage, res: ServerResponse, next: () => void) => void) => {
287
+ middlewares.push(fn);
288
+ },
289
+ },
290
+ httpServer: { on: vi.fn() },
291
+ };
292
+ (plugin as any).configureServer?.(mockServer);
293
+
294
+ const middleware = middlewares[0];
295
+ const mockReq = { url: "/client-pda/other-path" } as IncomingMessage;
296
+ const mockRes = {} as ServerResponse;
297
+ const next = vi.fn();
298
+
299
+ middleware(mockReq, mockRes, next);
300
+ expect(next).toHaveBeenCalled();
301
+
302
+ await (plugin as any).buildEnd?.call({});
303
+ });
226
304
  });