@simplysm/sd-cli 14.0.48 → 14.0.51

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 (133) hide show
  1. package/README.md +22 -679
  2. package/dist/engines/EsbuildClientEngine.d.ts.map +1 -1
  3. package/dist/engines/EsbuildClientEngine.js +1 -2
  4. package/dist/engines/EsbuildClientEngine.js.map +1 -1
  5. package/dist/esbuild/esbuild-angular-compiler-plugin.js.map +1 -1
  6. package/dist/lint/lint-core.js.map +1 -1
  7. package/dist/orchestrators/DevOrchestrator.js.map +1 -1
  8. package/dist/orchestrators/ServerRuntimeManager.d.ts.map +1 -1
  9. package/dist/orchestrators/ServerRuntimeManager.js +2 -4
  10. package/dist/orchestrators/ServerRuntimeManager.js.map +1 -1
  11. package/dist/ts-compiler/SdTsCompiler.d.ts +1 -7
  12. package/dist/ts-compiler/SdTsCompiler.d.ts.map +1 -1
  13. package/dist/ts-compiler/SdTsCompiler.js +109 -116
  14. package/dist/ts-compiler/SdTsCompiler.js.map +1 -1
  15. package/dist/utils/package-utils.d.ts.map +1 -1
  16. package/dist/utils/package-utils.js.map +1 -1
  17. package/docs/angular-vite-plugin/sd-angular-plugin.md +54 -0
  18. package/docs/config/build-target.md +31 -0
  19. package/docs/config/npm-config.md +27 -0
  20. package/docs/config/sd-browser-support-config.md +19 -0
  21. package/docs/config/sd-build-package-config.md +21 -0
  22. package/docs/config/sd-capacitor-config.md +109 -0
  23. package/docs/config/sd-client-package-config.md +33 -0
  24. package/docs/config/sd-config.md +73 -0
  25. package/docs/config/sd-electron-config.md +27 -0
  26. package/docs/config/sd-package-config.md +18 -0
  27. package/docs/config/sd-post-publish-script-config.md +19 -0
  28. package/docs/config/sd-publish-config.md +72 -0
  29. package/docs/config/sd-pwa-config.md +41 -0
  30. package/docs/config/sd-scripts-package-config.md +19 -0
  31. package/docs/config/sd-server-package-config.md +32 -0
  32. package/docs/config/sd-watch-hook-config.md +19 -0
  33. package/docs/ts-compiler/sd-ts-compiler.md +152 -0
  34. package/package.json +7 -6
  35. package/src/engines/EsbuildClientEngine.ts +1 -2
  36. package/src/esbuild/esbuild-angular-compiler-plugin.ts +1 -1
  37. package/src/lint/lint-core.ts +1 -1
  38. package/src/orchestrators/DevOrchestrator.ts +3 -3
  39. package/src/orchestrators/ServerRuntimeManager.ts +2 -5
  40. package/src/ts-compiler/SdTsCompiler.ts +136 -154
  41. package/src/utils/package-utils.ts +1 -2
  42. package/tests/angular/angular-compiler-hmr-removal.verify.md +12 -12
  43. package/tests/angular/onbuild-lint-removal.verify.md +4 -4
  44. package/tests/angular/vite-angular-plugin-sdtscompiler.verify.md +9 -9
  45. package/tests/angular/vite-angular-plugin-vitest.verify.md +16 -16
  46. package/tests/capacitor/capacitor-android-exports.verify.md +7 -7
  47. package/tests/commands/publish-npm-local-split.verify.md +5 -5
  48. package/tests/commands/publish-responsibility-split.verify.md +9 -9
  49. package/tests/commands/publish-set.verify.md +3 -3
  50. package/tests/commands/publish-storage-split.verify.md +4 -4
  51. package/tests/commands/slice3-severity-cleanup.verify.md +8 -8
  52. package/tests/deps/deps-directory-separation.verify.md +11 -11
  53. package/tests/deps/replace-deps/replace-deps-perf.verify.md +6 -6
  54. package/tests/deps/server-externals/mise-toml-parse-intent.verify.md +8 -8
  55. package/tests/electron/electron-symlink-cleanup.verify.md +4 -4
  56. package/tests/engines/engine-duplicate-output-removal.verify.md +6 -6
  57. package/tests/engines/engine-typecheck-selection.verify.md +4 -4
  58. package/tests/engines/esbuild-client-engine.verify.md +11 -11
  59. package/tests/engines/normalize-result.verify.md +5 -5
  60. package/tests/engines/vite-dependency-cleanup.verify.md +11 -11
  61. package/tests/esbuild/esbuild-angular-compiler-plugin-hmr.verify.md +10 -10
  62. package/tests/esbuild/esbuild-angular-compiler-plugin-onload.verify.md +17 -17
  63. package/tests/esbuild/esbuild-angular-compiler-plugin-onstart-extraction.verify.md +12 -12
  64. package/tests/esbuild/esbuild-angular-compiler-plugin-sdtscompiler.verify.md +11 -11
  65. package/tests/esbuild/esbuild-angular-compiler-plugin-stylesheet.verify.md +13 -13
  66. package/tests/esbuild/esbuild-angular-compiler-plugin-worker.verify.md +32 -32
  67. package/tests/esbuild/esbuild-angular-compiler-plugin.spec.ts +1 -1
  68. package/tests/esbuild/esbuild-angular-compiler-plugin.verify.md +9 -9
  69. package/tests/esbuild/esbuild-postcss-plugin-chunking.verify.md +3 -3
  70. package/tests/esbuild/esbuild-postcss-plugin.acc.spec.ts +1 -1
  71. package/tests/esbuild/esbuild-tsc-plugin-imports.verify.md +9 -9
  72. package/tests/esbuild/esbuild-tsc-plugin.acc.spec.ts +2 -2
  73. package/tests/esbuild/esbuild-tsc-plugin.spec.ts +3 -3
  74. package/tests/esbuild/esbuild-worker-plugin-node.verify.md +7 -7
  75. package/tests/esbuild/esbuild-worker-plugin.spec.ts +1 -1
  76. package/tests/esbuild/esbuild-worker-plugin.verify.md +3 -3
  77. package/tests/orchestrators/build-orchestrator.spec.ts +9 -9
  78. package/tests/orchestrators/dev-orchestrator.spec.ts +4 -4
  79. package/tests/orchestrators/dist-delete-watcher.verify.md +6 -6
  80. package/tests/orchestrators/orchestrator-baseenv.verify.md +6 -6
  81. package/tests/orchestrators/orchestrator-diagnostic-formatting.verify.md +6 -6
  82. package/tests/orchestrators/orchestrator-initializemode-signature.verify.md +5 -5
  83. package/tests/orchestrators/slice1-stdout-to-consola.verify.md +6 -6
  84. package/tests/orchestrators/watch-orchestrator.spec.ts +2 -2
  85. package/tests/sd-cli-catch-all.verify.md +3 -3
  86. package/tests/sd-cli-log-tag.verify.md +7 -7
  87. package/tests/ts-compiler/SdTsCompiler-affected-files.verify.md +4 -4
  88. package/tests/ts-compiler/SdTsCompiler-crash-handling.verify.md +24 -0
  89. package/tests/ts-compiler/SdTsCompiler-diagnostics.verify.md +8 -8
  90. package/tests/ts-compiler/SdTsCompiler-emit.verify.md +5 -5
  91. package/tests/ts-compiler/SdTsCompiler.verify.md +20 -20
  92. package/tests/ts-compiler/scss-lint-integration.verify.md +10 -10
  93. package/tests/utils/copy-public-outdir.verify.md +4 -4
  94. package/tests/utils/copy-src.spec.ts +5 -5
  95. package/tests/utils/dev-http-server.verify.md +4 -4
  96. package/tests/utils/engine-watch-events.verify.md +8 -8
  97. package/tests/utils/esbuild-client-config-integration.verify.md +5 -5
  98. package/tests/utils/esbuild-client-config-postcss.verify.md +2 -2
  99. package/tests/utils/esbuild-client-config.acc.spec.ts +1 -1
  100. package/tests/utils/esbuild-client-config.spec.ts +4 -4
  101. package/tests/utils/esbuild-client-config.verify.md +16 -16
  102. package/tests/utils/esbuild-config.spec.ts +3 -3
  103. package/tests/utils/esbuild-index-html.verify.md +6 -6
  104. package/tests/utils/esbuild-postcss-plugin.spec.ts +1 -1
  105. package/tests/utils/esbuild-pwa.verify.md +5 -5
  106. package/tests/utils/esbuild-scss-plugin.verify.md +4 -4
  107. package/tests/utils/hmr-client-script.acc.spec.ts +1 -1
  108. package/tests/utils/hmr-service.verify.md +10 -10
  109. package/tests/utils/lint-core-import-paths.verify.md +6 -6
  110. package/tests/utils/ngtsc-build-core.spec.ts +1 -1
  111. package/tests/utils/package-utils.spec.ts +6 -6
  112. package/tests/utils/replace-deps-split.verify.md +11 -11
  113. package/tests/utils/replace-deps-watch.verify.md +5 -5
  114. package/tests/utils/server-production-files-import-paths.verify.md +10 -10
  115. package/tests/utils/vite-config-cleanup.verify.md +3 -3
  116. package/tests/workers/build-watch-paths-library.verify.md +6 -6
  117. package/tests/workers/build-watch-paths-ngtsc-server.verify.md +8 -8
  118. package/tests/workers/client-worker-browser-support.verify.md +3 -3
  119. package/tests/workers/client-worker-cleanup.verify.md +4 -4
  120. package/tests/workers/client-worker-initial-build-error.verify.md +3 -3
  121. package/tests/workers/client-worker-initial-build-warnings.verify.md +3 -3
  122. package/tests/workers/client-worker-mtime-incremental.verify.md +6 -6
  123. package/tests/workers/client-worker-onend-sync.verify.md +3 -3
  124. package/tests/workers/client-worker-refactor.verify.md +18 -18
  125. package/tests/workers/client-worker-ts-cache-invalidation.verify.md +8 -8
  126. package/tests/workers/dev-port-file.verify.md +3 -3
  127. package/tests/workers/ngtsc-build-rootnames-refresh.verify.md +4 -4
  128. package/tests/workers/server-build-context-dispose.verify.md +4 -4
  129. package/tests/workers/server-build-worker-plugin.verify.md +5 -5
  130. package/tests/workers/server-build-worker-refactoring.verify.md +10 -10
  131. package/tests/workers/server-build-worker.spec.ts +1 -1
  132. package/tests/workers/server-esbuild-context-integration.verify.md +6 -6
  133. package/tests/workers/server-esbuild-context-tsc.verify.md +3 -3
@@ -2,8 +2,8 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] `@angular/build/private`에서 `createCompilerPlugin`, `SourceFileCache`, `CompilerPluginOptions`, `BundleStylesheetOptions` import가 없다: grep 결과 `@angular/build/private` 매치 없음
6
- - [x] `createAngularCompilerPlugin`이 `./esbuild-angular-compiler-plugin.js`에서 import되어 있다: line 10에서 import, line 119에서 호출 확인
7
- - [x] `ClientSourceFileCache`가 `AngularSourceFileCache`를 확장하고 `typeScriptFileCache`, `loadResultCache` 프로퍼티를 가진다: line 46-49에서 `extends AngularSourceFileCache` + 두 프로퍼티 확인
8
- - [x] `ClientEsbuildResult.sourceFileCache` 타입이 `ClientSourceFileCache`이다: line 53 확인
9
- - [x] `client.worker.ts`의 `sourceFileCache.loadResultCache.watchFiles`, `sourceFileCache.typeScriptFileCache.keys()`, `sourceFileCache.invalidate()` 접근이 새 구조와 타입 호환된다: typecheck 0 에러로 타입 호환성 확인됨
5
+ - `@angular/build/private`에서 `createCompilerPlugin`, `SourceFileCache`, `CompilerPluginOptions`, `BundleStylesheetOptions` import가 없다: grep 결과 `@angular/build/private` 매치 없음
6
+ - `createAngularCompilerPlugin`이 `./esbuild-angular-compiler-plugin.js`에서 import되어 있다: line 10에서 import, line 119에서 호출 확인
7
+ - `ClientSourceFileCache`가 `AngularSourceFileCache`를 확장하고 `typeScriptFileCache`, `loadResultCache` 프로퍼티를 가진다: line 46-49에서 `extends AngularSourceFileCache` + 두 프로퍼티 확인
8
+ - `ClientEsbuildResult.sourceFileCache` 타입이 `ClientSourceFileCache`이다: line 53 확인
9
+ - `client.worker.ts`의 `sourceFileCache.loadResultCache.watchFiles`, `sourceFileCache.typeScriptFileCache.keys()`, `sourceFileCache.invalidate()` 접근이 새 구조와 타입 호환된다: typecheck 0 에러로 타입 호환성 확인됨
@@ -2,5 +2,5 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] postcssConfigPath가 CreateClientEsbuildOptions 인터페이스에서 제거됨: `esbuild-client-config.ts`에서 `postcssConfigPath` 검색 결과 없음 (No matches found). `client.worker.ts`에서도 2곳 모두 제거 확인.
6
- - [x] createScssPlugin에 로딩된 PostCSS 인스턴스가 전달됨: `esbuild-client-config.ts:118`에서 `postcssPlugins: loadedPostcssPlugins`로 `createScssPlugin`에 전달 확인.
5
+ - postcssConfigPath가 CreateClientEsbuildOptions 인터페이스에서 제거됨: `esbuild-client-config.ts`에서 `postcssConfigPath` 검색 결과 없음 (No matches found). `client.worker.ts`에서도 2곳 모두 제거 확인.
6
+ - createScssPlugin에 로딩된 PostCSS 인스턴스가 전달됨: `esbuild-client-config.ts:118`에서 `postcssPlugins: loadedPostcssPlugins`로 `createScssPlugin`에 전달 확인.
@@ -423,7 +423,7 @@ describe("createClientEsbuildContext — Acceptance", () => {
423
423
  pkgDir: "/workspace/packages/my-app",
424
424
  cwd: "/workspace",
425
425
  mode: "dev",
426
- plugins: [customPlugin as any],
426
+ plugins: [customPlugin],
427
427
  onEnd: vi.fn(),
428
428
  });
429
429
 
@@ -215,7 +215,7 @@ describe("createClientEsbuildContext — PostCSS 플러그인 통합", () => {
215
215
  await createClientEsbuildContext({
216
216
  ...baseBuild,
217
217
  postcssPlugins: [["autoprefixer"]],
218
- plugins: [customPlugin as any],
218
+ plugins: [customPlugin],
219
219
  legacyModule: true,
220
220
  onEnd: vi.fn(),
221
221
  });
@@ -294,7 +294,7 @@ describe("createClientEsbuildContext — 추가 옵션", () => {
294
294
  const customPlugin = { name: "custom", setup: vi.fn() };
295
295
  await createClientEsbuildContext({
296
296
  ...baseDev,
297
- plugins: [customPlugin as any],
297
+ plugins: [customPlugin],
298
298
  });
299
299
  const opts = vi.mocked(esbuild.context).mock.calls[0][0];
300
300
  expect(opts.plugins).toContainEqual(customPlugin);
@@ -460,7 +460,7 @@ describe("createClientEsbuildContext — onEnd 플러그인", () => {
460
460
  it("customPlugins가 angularPlugin 이전에 위치 (onStart에서 sourceFileCache 무효화 선행)", async () => {
461
461
  await createClientEsbuildContext({
462
462
  ...baseDev,
463
- plugins: [{ name: "custom", setup: vi.fn() } as any],
463
+ plugins: [{ name: "custom", setup: vi.fn() }],
464
464
  onEnd: vi.fn(),
465
465
  });
466
466
  const opts = vi.mocked(esbuild.context).mock.calls[0][0];
@@ -506,7 +506,7 @@ describe("createClientEsbuildContext — SCSS 플러그인 통합", () => {
506
506
  const customPlugin = { name: "custom", setup: vi.fn() };
507
507
  await createClientEsbuildContext({
508
508
  ...baseDev,
509
- plugins: [customPlugin as any],
509
+ plugins: [customPlugin],
510
510
  });
511
511
  const opts = vi.mocked(esbuild.context).mock.calls[0][0];
512
512
  const pluginNames = opts.plugins!.map((p: any) => p.name);
@@ -2,25 +2,25 @@
2
2
 
3
3
  ## Feature 1.1a 검증 항목
4
4
 
5
- - [x] import 경로: `@angular/build/private`에서 `createCompilerPlugin`, `SourceFileCache`, `CompilerPluginOptions`, `BundleStylesheetOptions`를 정상 import — `private.d.ts` line 31-37에 모두 export 확인
6
- - [x] `createCompilerPlugin`의 2-param 시그니처(private export wrapper)와 일치하는 호출 — `createCompilerPlugin(pluginOptions, styleOptions)` 2개 인자로 호출 (private.d.ts의 wrapper 시그니처와 일치)
7
- - [x] `CompilerPluginOptions.sourcemap` 타입이 `boolean | 'external'` 범위 내 값 사용 — `isDev` (boolean)으로 전달, `true`/`false` 범위 내
8
- - [x] `BundleStylesheetOptions.sourcemap` 타입이 `boolean | 'external' | 'inline' | 'linked'` 범위 내 값 사용 — `isDev ? "linked" : false` 범위 내
9
- - [x] `sd-config.types.ts`의 `postCss.plugins` 타입이 Angular의 `PostcssConfiguration` 포맷과 일치 — `[string, (object | string)?][]` = Angular의 `[name: string, options?: object | string][]`
10
- - [x] `onEnd` 플러그인이 esbuild Plugin 규격(`name` + `setup`)을 준수 — `{ name: "sd-on-end", setup(build) { build.onEnd(...) } }` 규격 충족
11
- - [x] 패키지 루트 `index.ts`에서 신규 모듈 export 필요 여부 — 불필요. 기존 `esbuild-config.ts`도 내부 유틸로 export 안 함. `sd-config.types.ts`만 export 중
5
+ - import 경로: `@angular/build/private`에서 `createCompilerPlugin`, `SourceFileCache`, `CompilerPluginOptions`, `BundleStylesheetOptions`를 정상 import — `private.d.ts` line 31-37에 모두 export 확인
6
+ - `createCompilerPlugin`의 2-param 시그니처(private export wrapper)와 일치하는 호출 — `createCompilerPlugin(pluginOptions, styleOptions)` 2개 인자로 호출 (private.d.ts의 wrapper 시그니처와 일치)
7
+ - `CompilerPluginOptions.sourcemap` 타입이 `boolean | 'external'` 범위 내 값 사용 — `isDev` (boolean)으로 전달, `true`/`false` 범위 내
8
+ - `BundleStylesheetOptions.sourcemap` 타입이 `boolean | 'external' | 'inline' | 'linked'` 범위 내 값 사용 — `isDev ? "linked" : false` 범위 내
9
+ - `sd-config.types.ts`의 `postCss.plugins` 타입이 Angular의 `PostcssConfiguration` 포맷과 일치 — `[string, (object | string)?][]` = Angular의 `[name: string, options?: object | string][]`
10
+ - `onEnd` 플러그인이 esbuild Plugin 규격(`name` + `setup`)을 준수 — `{ name: "sd-on-end", setup(build) { build.onEnd(...) } }` 규격 충족
11
+ - 패키지 루트 `index.ts`에서 신규 모듈 export 필요 여부 — 불필요. 기존 `esbuild-config.ts`도 내부 유틸로 export 안 함. `sd-config.types.ts`만 export 중
12
12
 
13
13
  ## Feature 1.1b-2 검증 항목
14
14
 
15
- - [x] `browserslist-to-esbuild` import 정상 — `import browserslistToEsbuild from "browserslist-to-esbuild"` 추가. `vite-config.ts:5`와 동일한 패턴
16
- - [x] `browserslistToEsbuild` 반환값이 esbuild target 및 BundleStylesheetOptions.target에 동시 적용 — `esbuildTarget` 변수로 통합하여 두 곳에 사용 (line 51-56, 82, 122)
17
- - [x] 에셋 복사: `copyPublicFiles(pkgDir, false)` 시그니처가 build 모드 호출 가능 — `copy-public.ts:15` 시그니처 `(pkgDir: string, includeDev: boolean): Promise<void>` 확인. build 모드에서 `false` 전달 적합
18
- - [x] 에셋 감시: `watchPublicFiles(pkgDir, true)` 시그니처가 dev 모드 호출 가능 — `copy-public.ts:58` 시그니처 `(pkgDir: string, includeDev: boolean): Promise<FsWatcher | undefined>` 확인. dev 모드에서 `true` 전달 적합
19
- - [x] 에셋 복사 함수는 `createClientEsbuildContext` 외부 호출자 담당 (설계 결정 D4) — `createClientEsbuildContext` 내부에 `copyPublicFiles`/`watchPublicFiles` 호출 없음 확인. 실제 호출 통합은 Feature 3.1에서 수행
15
+ - `browserslist-to-esbuild` import 정상 — `import browserslistToEsbuild from "browserslist-to-esbuild"` 추가. `vite-config.ts:5`와 동일한 패턴
16
+ - `browserslistToEsbuild` 반환값이 esbuild target 및 BundleStylesheetOptions.target에 동시 적용 — `esbuildTarget` 변수로 통합하여 두 곳에 사용 (line 51-56, 82, 122)
17
+ - 에셋 복사: `copyPublicFiles(pkgDir, false)` 시그니처가 build 모드 호출 가능 — `copy-public.ts:15` 시그니처 `(pkgDir: string, includeDev: boolean): Promise<void>` 확인. build 모드에서 `false` 전달 적합
18
+ - 에셋 감시: `watchPublicFiles(pkgDir, true)` 시그니처가 dev 모드 호출 가능 — `copy-public.ts:58` 시그니처 `(pkgDir: string, includeDev: boolean): Promise<FsWatcher | undefined>` 확인. dev 모드에서 `true` 전달 적합
19
+ - 에셋 복사 함수는 `createClientEsbuildContext` 외부 호출자 담당 (설계 결정 D4) — `createClientEsbuildContext` 내부에 `copyPublicFiles`/`watchPublicFiles` 호출 없음 확인. 실제 호출 통합은 Feature 3.1에서 수행
20
20
 
21
21
  ## Feature 1.1b-1 검증 항목
22
22
 
23
- - [x] tsconfig 옵션이 CompilerPluginOptions와 esbuild context에 동일 기본값 사용 — 둘 다 `options.tsconfig ?? path.join(options.pkgDir, "tsconfig.json")`. CompilerPluginOptions(line 65)와 esbuild context(line 151)에서 동일 패턴
24
- - [x] esbuild tsconfig 옵션이 서버 빌드 패턴(D1)과 일치 — `esbuild-config.ts:94` `tsconfig: path.join(options.pkgDir, "tsconfig.json")`과 동일. 클라이언트는 추가로 `options.tsconfig` 커스텀 경로 지원 (기존 인터페이스에 이미 존재)
25
- - [x] scssPlugin loadPaths가 buildScssLoadPaths 패턴과 일치 — `ngtsc-build-core.ts:91-96`의 `[pkgDir/scss, cwd/node_modules]`와 `esbuild-client-config.ts:105-108`의 `[options.pkgDir/scss, options.cwd/node_modules]` 동일
26
- - [x] plugins 배열 순서가 구현계획과 일치 — `[angularPlugin, scssPlugin, ...customPlugins, ...onEndPlugin]` (line 153-169)
23
+ - tsconfig 옵션이 CompilerPluginOptions와 esbuild context에 동일 기본값 사용 — 둘 다 `options.tsconfig ?? path.join(options.pkgDir, "tsconfig.json")`. CompilerPluginOptions(line 65)와 esbuild context(line 151)에서 동일 패턴
24
+ - esbuild tsconfig 옵션이 서버 빌드 패턴(D1)과 일치 — `esbuild-config.ts:94` `tsconfig: path.join(options.pkgDir, "tsconfig.json")`과 동일. 클라이언트는 추가로 `options.tsconfig` 커스텀 경로 지원 (기존 인터페이스에 이미 존재)
25
+ - scssPlugin loadPaths가 buildScssLoadPaths 패턴과 일치 — `ngtsc-build-core.ts:91-96`의 `[pkgDir/scss, cwd/node_modules]`와 `esbuild-client-config.ts:105-108`의 `[options.pkgDir/scss, options.cwd/node_modules]` 동일
26
+ - plugins 배열 순서가 구현계획과 일치 — `[angularPlugin, scssPlugin, ...customPlugins, ...onEndPlugin]` (line 153-169)
@@ -123,7 +123,7 @@ describe("createEnvBanner", () => {
123
123
  describe("writeChangedOutputFiles", () => {
124
124
  beforeEach(() => {
125
125
  vi.clearAllMocks();
126
- vi.mocked(mockFs.mkdir).mockResolvedValue(undefined as any);
126
+ vi.mocked(mockFs.mkdir).mockResolvedValue(undefined);
127
127
  vi.mocked(mockFs.writeFile).mockResolvedValue();
128
128
  });
129
129
 
@@ -160,7 +160,7 @@ describe("writeChangedOutputFiles", () => {
160
160
  });
161
161
 
162
162
  it("skips writing when transformed content matches existing file", async () => {
163
- vi.mocked(mockFs.readFile).mockResolvedValue('import { bar } from "./bar.js";' as any);
163
+ vi.mocked(mockFs.readFile).mockResolvedValue('import { bar } from "./bar.js";');
164
164
 
165
165
  await writeChangedOutputFiles([
166
166
  {
@@ -173,7 +173,7 @@ describe("writeChangedOutputFiles", () => {
173
173
  });
174
174
 
175
175
  it("writes file when content changed", async () => {
176
- vi.mocked(mockFs.readFile).mockResolvedValue('import { old } from "./old.js";' as any);
176
+ vi.mocked(mockFs.readFile).mockResolvedValue('import { old } from "./old.js";');
177
177
 
178
178
  await writeChangedOutputFiles([
179
179
  {
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] FileInfo 타입이 IndexHtmlGenerator의 FileInfo와 호환: IndexHtmlGeneratorProcessOptions.files는 `{file: string, name?: string, extension: string}` — 동일 구조 확인
6
- - [x] entrypoints 타입이 IndexHtmlGenerator의 Entrypoint와 호환: `[string, boolean][]` — `Entrypoint = [name: string, isModule: boolean]`과 동일
7
- - [x] SRI 옵션이 mode에 따라 정확히 설정: `sri: options.mode === "build"` — build일 때만 true
8
- - [x] cssBundle 연결 로직의 정확성: JS entry의 `output.cssBundle` 경로를 normalize하여 CSS output의 경로와 매칭. esbuild metafile 구조상 cssBundle은 output key와 동일 형식
9
- - [x] postTransform이 IndexHtmlGeneratorOptions에 올바르게 전달: `IndexHtmlTransform = (content: string) => Promise<string>` — GenerateIndexHtmlOptions.postTransform과 동일 타입
10
- - [x] Windows 경로 호환성: `replace(/\\/g, "/")` 로 모든 경로를 POSIX 형식으로 정규화
5
+ - FileInfo 타입이 IndexHtmlGenerator의 FileInfo와 호환: IndexHtmlGeneratorProcessOptions.files는 `{file: string, name?: string, extension: string}` — 동일 구조 확인
6
+ - entrypoints 타입이 IndexHtmlGenerator의 Entrypoint와 호환: `[string, boolean][]` — `Entrypoint = [name: string, isModule: boolean]`과 동일
7
+ - SRI 옵션이 mode에 따라 정확히 설정: `sri: options.mode === "build"` — build일 때만 true
8
+ - cssBundle 연결 로직의 정확성: JS entry의 `output.cssBundle` 경로를 normalize하여 CSS output의 경로와 매칭. esbuild metafile 구조상 cssBundle은 output key와 동일 형식
9
+ - postTransform이 IndexHtmlGeneratorOptions에 올바르게 전달: `IndexHtmlTransform = (content: string) => Promise<string>` — GenerateIndexHtmlOptions.postTransform과 동일 타입
10
+ - Windows 경로 호환성: `replace(/\\/g, "/")` 로 모든 경로를 POSIX 형식으로 정규화
@@ -86,7 +86,7 @@ describe("createPostcssPlugin — metafile 가드", () => {
86
86
  metafile: undefined,
87
87
  outputFiles: [],
88
88
  mangleCache: {},
89
- } as unknown as esbuild.BuildResult);
89
+ });
90
90
 
91
91
  expect(fs.readFileSync(cssFile, "utf-8")).toBe(original);
92
92
  });
@@ -2,8 +2,8 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] augmentAppWithServiceWorker 호출 인자가 정확한가: `esbuild-pwa.ts:108-113` — `(options.pkgDir, options.cwd, options.outdir, options.baseHref)` 순서로 호출 확인
6
- - [x] 기본 ngsw-config.json에 $schema, index, assetGroups(app, assets)가 포함되는가: `esbuild-pwa.ts:83-104` — `$schema`, `index: "/index.html"`, `assetGroups: [{name: "app"}, {name: "assets"}]` 확인
7
- - [x] 등록 스크립트에서 ngsw-worker.js를 등록하는가: `esbuild-pwa.ts:119` — `register("ngsw-worker.js")` 확인. `sw.js` 참조 없음
8
- - [x] SdPwaConfig.workbox 필드를 참조하지 않는가: grep 결과 "workbox" 0건 확인. D4 결정 준수
9
- - [x] generatePwaIcons 호출 결과를 dist/icons/로 복사하는 로직이 있는가: `esbuild-pwa.ts:51-58` — `srcIconsDir(public/icons/)` → `dstIconsDir(outdir/icons/)` copyFileSync 로직 확인
5
+ - augmentAppWithServiceWorker 호출 인자가 정확한가: `esbuild-pwa.ts:108-113` — `(options.pkgDir, options.cwd, options.outdir, options.baseHref)` 순서로 호출 확인
6
+ - 기본 ngsw-config.json에 $schema, index, assetGroups(app, assets)가 포함되는가: `esbuild-pwa.ts:83-104` — `$schema`, `index: "/index.html"`, `assetGroups: [{name: "app"}, {name: "assets"}]` 확인
7
+ - 등록 스크립트에서 ngsw-worker.js를 등록하는가: `esbuild-pwa.ts:119` — `register("ngsw-worker.js")` 확인. `sw.js` 참조 없음
8
+ - SdPwaConfig.workbox 필드를 참조하지 않는가: grep 결과 "workbox" 0건 확인. D4 결정 준수
9
+ - generatePwaIcons 호출 결과를 dist/icons/로 복사하는 로직이 있는가: `esbuild-pwa.ts:51-58` — `srcIconsDir(public/icons/)` → `dstIconsDir(outdir/icons/)` copyFileSync 로직 확인
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] createCompilerPlugin 컴포넌트 스타일과 비충돌: `createCompilerPlugin`은 `BundleStylesheetOptions`를 받아 컴포넌트 `styleUrls`를 TypeScript 변환 과정에서 내부 처리한다. 이들은 esbuild의 일반 `onLoad` 경로를 타지 않으므로, `sd-scss` 플러그인의 `onLoad({ filter: /\.scss$/ })` 콜백은 side-effect `import "./global.scss"` 같은 일반 import만 인터셉트한다. 충돌 없음 확인.
6
- - [x] onLoad filter 정확성: `/\.scss$/` 정규식이 `.scss` 파일만 매칭 — `.css`, `.scss.ts` 등은 매칭하지 않음. 단위 테스트(`esbuild-scss-plugin.spec.ts`)에서 검증 완료.
7
- - [x] sass 라인 번호 변환: sass `span.start.line`은 0-based, esbuild `location.line`은 1-based. `line + 1` 변환 적용 확인 (`esbuild-scss-plugin.ts:31`).
8
- - [x] fileURLToPath import 정상: `url` 모듈에서 import. `scss-compiler.ts`의 `pathToFileURL`과 동일 모듈.
5
+ - createCompilerPlugin 컴포넌트 스타일과 비충돌: `createCompilerPlugin`은 `BundleStylesheetOptions`를 받아 컴포넌트 `styleUrls`를 TypeScript 변환 과정에서 내부 처리한다. 이들은 esbuild의 일반 `onLoad` 경로를 타지 않으므로, `sd-scss` 플러그인의 `onLoad({ filter: /\.scss$/ })` 콜백은 side-effect `import "./global.scss"` 같은 일반 import만 인터셉트한다. 충돌 없음 확인.
6
+ - onLoad filter 정확성: `/\.scss$/` 정규식이 `.scss` 파일만 매칭 — `.css`, `.scss.ts` 등은 매칭하지 않음. 단위 테스트(`esbuild-scss-plugin.spec.ts`)에서 검증 완료.
7
+ - sass 라인 번호 변환: sass `span.start.line`은 0-based, esbuild `location.line`은 1-based. `line + 1` 변환 적용 확인 (`esbuild-scss-plugin.ts:31`).
8
+ - fileURLToPath import 정상: `url` 모듈에서 import. `scss-compiler.ts`의 `pathToFileURL`과 동일 모듈.
@@ -64,7 +64,7 @@ describe("HMR 클라이언트 스크립트 통합", () => {
64
64
  globalThis: {} as Record<string, unknown>,
65
65
  JSON,
66
66
  };
67
- sandbox.globalThis = sandbox as Record<string, unknown>;
67
+ sandbox.globalThis = sandbox;
68
68
 
69
69
  return {
70
70
  sandbox,
@@ -2,16 +2,16 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] ws 의존성이 sd-cli package.json dependencies에 추가되었는가: `"ws": "^8.20.0"` 확인 (package.json:51)
6
- - [x] @types/ws가 sd-cli package.json devDependencies에 추가되었는가: `"@types/ws": "^8.18.1"` 확인 (package.json:57)
7
- - [x] dev-http-server.ts의 onRequest 훅 타입 시그니처가 올바른가: `onRequest?: (req: http.IncomingMessage, res: http.ServerResponse) => boolean` — Node.js 표준 타입 사용, boolean 반환으로 처리 완료 여부 표시
8
- - [x] hmr-service.ts의 handleRequest가 angularComponentMiddleware와 동일한 encodeURIComponent 패턴을 사용하는가: `templateUpdates.get(encodeURIComponent(componentId))` (hmr-service.ts:65) — vite-angular-plugin.ts:553과 동일 패턴
9
- - [x] WebSocketServer가 httpServer의 upgrade 이벤트를 통해 연결되는가: `new WebSocketServer({ server: httpServer })` (hmr-service.ts:27) — ws 라이브러리가 httpServer의 upgrade 이벤트를 자동 처리
5
+ - ws 의존성이 sd-cli package.json dependencies에 추가되었는가: `"ws": "^8.20.0"` 확인 (package.json:51)
6
+ - @types/ws가 sd-cli package.json devDependencies에 추가되었는가: `"@types/ws": "^8.18.1"` 확인 (package.json:57)
7
+ - dev-http-server.ts의 onRequest 훅 타입 시그니처가 올바른가: `onRequest?: (req: http.IncomingMessage, res: http.ServerResponse) => boolean` — Node.js 표준 타입 사용, boolean 반환으로 처리 완료 여부 표시
8
+ - hmr-service.ts의 handleRequest가 angularComponentMiddleware와 동일한 encodeURIComponent 패턴을 사용하는가: `templateUpdates.get(encodeURIComponent(componentId))` (hmr-service.ts:65) — vite-angular-plugin.ts:553과 동일 패턴
9
+ - WebSocketServer가 httpServer의 upgrade 이벤트를 통해 연결되는가: `new WebSocketServer({ server: httpServer })` (hmr-service.ts:27) — ws 라이브러리가 httpServer의 upgrade 이벤트를 자동 처리
10
10
 
11
11
  ## Slice 2 검증 항목
12
12
 
13
- - [x] import.meta.hot 폴리필이 esbuild banner로 주입되는가: `esbuild-client-config.ts`에서 `templateUpdates != null && legacyModule !== true` 조건으로 banner 생성. `import.meta.hot.on()`, `import.meta.hot.off()`, `globalThis.__hmr_dispatch()` 제공.
14
- - [x] legacyModule일 때 폴리필이 주입되지 않는가: `options.legacyModule !== true` 조건으로 legacyModule에서 banner 미생성.
15
- - [x] ngHmrMode 가드가 있어 prod에서 실행되지 않는가: banner 코드가 `if(typeof ngHmrMode!=="undefined"&&ngHmrMode)` 가드로 래핑. prod에서 `ngHmrMode="false"`로 정의되므로 실행 안됨.
16
- - [x] sd-hmr-reset 플러그인이 onStart에서 templateUpdates.clear()를 호출하는가: `esbuild-client-config.ts`에 `sd-hmr-reset` 플러그인 추가. `build.onStart(() => { options.templateUpdates!.clear(); })` 확인.
17
- - [x] sd-hmr-reset이 angularPlugin보다 먼저 등록되는가: plugins 배열에서 `sd-hmr-reset`이 첫 번째 위치 (angularPlugin, scssPlugin보다 앞). createCompilerPlugin이 templateUpdates에 쓰기 전에 clear됨.
13
+ - import.meta.hot 폴리필이 esbuild banner로 주입되는가: `esbuild-client-config.ts`에서 `templateUpdates != null && legacyModule !== true` 조건으로 banner 생성. `import.meta.hot.on()`, `import.meta.hot.off()`, `globalThis.__hmr_dispatch()` 제공.
14
+ - legacyModule일 때 폴리필이 주입되지 않는가: `options.legacyModule !== true` 조건으로 legacyModule에서 banner 미생성.
15
+ - ngHmrMode 가드가 있어 prod에서 실행되지 않는가: banner 코드가 `if(typeof ngHmrMode!=="undefined"&&ngHmrMode)` 가드로 래핑. prod에서 `ngHmrMode="false"`로 정의되므로 실행 안됨.
16
+ - sd-hmr-reset 플러그인이 onStart에서 templateUpdates.clear()를 호출하는가: `esbuild-client-config.ts`에 `sd-hmr-reset` 플러그인 추가. `build.onStart(() => { options.templateUpdates!.clear(); })` 확인.
17
+ - sd-hmr-reset이 angularPlugin보다 먼저 등록되는가: plugins 배열에서 `sd-hmr-reset`이 첫 번째 위치 (angularPlugin, scssPlugin보다 앞). createCompilerPlugin이 templateUpdates에 쓰기 전에 clear됨.
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] lint.worker.ts가 `../utils/lint-core`에서 import: `import { executeLint, type LintOptions, type LintResult } from "../utils/lint-core";` (line 2) 확인
6
- - [x] lint-utils.ts가 `./lint-core`에서 import: `import type { LintOptions, LintResult } from "./lint-core";` (line 2) 확인
7
- - [x] check.ts가 `../utils/lint-core`에서 import: `import { executeLint, type LintResult } from "../utils/lint-core";` (line 4) 확인
8
- - [x] commands/lint.ts�� `../commands/lint` import가 없음: `import { executeLint, type LintOptions } from "../utils/lint-core";` (line 1)만 존재
9
- - [x] lint-core.ts에 runLint가 없음: `executeLint`, `loadIgnorePatterns`, `LintOptions`, `LintResult`만 export
10
- - [x] commands/lint.ts에 runLint만 존재: 13줄의 thin wrapper
5
+ - lint.worker.ts가 `../utils/lint-core`에서 import: `import { executeLint, type LintOptions, type LintResult } from "../utils/lint-core";` (line 2) 확인
6
+ - lint-utils.ts가 `./lint-core`에서 import: `import type { LintOptions, LintResult } from "./lint-core";` (line 2) 확인
7
+ - check.ts가 `../utils/lint-core`에서 import: `import { executeLint, type LintResult } from "../utils/lint-core";` (line 4) 확인
8
+ - commands/lint.ts�� `../commands/lint` import가 없음: `import { executeLint, type LintOptions } from "../utils/lint-core";` (line 1)만 존재
9
+ - lint-core.ts에 runLint가 없음: `executeLint`, `loadIgnorePatterns`, `LintOptions`, `LintResult`만 export
10
+ - commands/lint.ts에 runLint만 존재: 13줄의 thin wrapper
@@ -8,7 +8,7 @@ const mockCollectDiagnostics = vi.fn().mockReturnValue([]);
8
8
  const mockEmitAffectedFiles = vi.fn().mockReturnValue([]);
9
9
  const mockGetTsProgram = vi.fn().mockReturnValue({
10
10
  getSourceFiles: () => [],
11
- } as unknown as ts.Program);
11
+ });
12
12
 
13
13
  const angularCompilerConstructorSpy = vi.fn();
14
14
 
@@ -97,8 +97,8 @@ describe("mergeTestsPackagesIntoConfig", () => {
97
97
  // Acceptance: targets 없이 watch 실행 시 tests 패키지가 포함된다
98
98
  it("merges tests packages into config packages with target node", () => {
99
99
  const configPackages: Record<string, SdPackageConfig | undefined> = {
100
- "core-common": { target: "neutral" } as SdBuildPackageConfig,
101
- "core-node": { target: "node" } as SdBuildPackageConfig,
100
+ "core-common": { target: "neutral" },
101
+ "core-node": { target: "node" },
102
102
  };
103
103
  const workspacePackages = new Map([
104
104
  ["core-common", "packages/core-common"],
@@ -124,7 +124,7 @@ describe("mergeTestsPackagesIntoConfig", () => {
124
124
  // Acceptance: tests 패키지를 target으로 지정하여 watch 실행 (validateTargets에서 통합 맵 사용)
125
125
  it("makes tests packages available for validateTargets", () => {
126
126
  const configPackages: Record<string, SdPackageConfig | undefined> = {
127
- "core-common": { target: "neutral" } as SdBuildPackageConfig,
127
+ "core-common": { target: "neutral" },
128
128
  };
129
129
  const workspacePackages = new Map([
130
130
  ["core-common", "packages/core-common"],
@@ -140,7 +140,7 @@ describe("mergeTestsPackagesIntoConfig", () => {
140
140
  // Unit: packages/ entries in workspacePackages are not added to merged (already in config)
141
141
  it("does not duplicate packages/ entries from workspacePackages", () => {
142
142
  const configPackages: Record<string, SdPackageConfig | undefined> = {
143
- "core-common": { target: "neutral" } as SdBuildPackageConfig,
143
+ "core-common": { target: "neutral" },
144
144
  };
145
145
  const workspacePackages = new Map([
146
146
  ["core-common", "packages/core-common"],
@@ -156,7 +156,7 @@ describe("mergeTestsPackagesIntoConfig", () => {
156
156
  // Unit: empty workspacePackages returns config as-is
157
157
  it("returns config unchanged when no workspace packages", () => {
158
158
  const configPackages: Record<string, SdPackageConfig | undefined> = {
159
- "core-common": { target: "neutral" } as SdBuildPackageConfig,
159
+ "core-common": { target: "neutral" },
160
160
  };
161
161
  const workspacePackages = new Map<string, string>();
162
162
 
@@ -169,7 +169,7 @@ describe("mergeTestsPackagesIntoConfig", () => {
169
169
  // Acceptance: 이름 충돌 시 에러 발생
170
170
  it("throws when config package name collides with tests package name", () => {
171
171
  const configPackages: Record<string, SdPackageConfig | undefined> = {
172
- "orm": { target: "node" } as SdBuildPackageConfig,
172
+ "orm": { target: "node" },
173
173
  };
174
174
  const workspacePackages = new Map([
175
175
  ["orm", "tests/orm"],
@@ -2,14 +2,14 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] 파일 구조: `replace-deps-resolve.ts` 신규 생성, `replace-deps.ts` 기존 유지 확인
6
- - [x] 해석 함수 이동: `resolveReplaceDepEntries`, `parseWorkspaceGlobs`, `collectSearchRoots`, `resolveAllReplaceDepEntries`가 `replace-deps-resolve.ts`에 위치
7
- - [x] 타입 위치: `ReplaceDepEntry`가 `replace-deps-resolve.ts`에서 export, `replace-deps.ts`에서 `export type` re-export
8
- - [x] `WatchReplaceDepResult`가 `replace-deps.ts`에서 export
9
- - [x] `replace-deps.ts`에서 `resolveAllReplaceDepEntries`를 import하여 사용
10
- - [x] `commands/publish.ts` import 경로가 `../utils/replace-deps-resolve`로 변경
11
- - [x] `sd-cli.ts`, `BaseOrchestrator.ts`, `commands/replace-deps.ts`의 import 경로는 변경 없음 (실행 함수만 사용)
12
- - [x] `tests/utils/replace-deps.spec.ts` import 경로가 `../../src/deps/replace-deps-resolve`로 변경
13
- - [x] `tests/utils/replace-deps-watch.spec.ts`, `replace-deps-watch.acc.spec.ts`의 import 경로는 변경 없음
14
- - [x] `replace-deps.ts`에서 `glob` import가 제거됨 (해석 로직 전용)
15
- - [x] `replace-deps-resolve.ts`에서 `fsx`, `FsWatcher`, `exec` import가 없음 (실행 로직 전용)
5
+ - 파일 구조: `replace-deps-resolve.ts` 신규 생성, `replace-deps.ts` 기존 유지 확인
6
+ - 해석 함수 이동: `resolveReplaceDepEntries`, `parseWorkspaceGlobs`, `collectSearchRoots`, `resolveAllReplaceDepEntries`가 `replace-deps-resolve.ts`에 위치
7
+ - 타입 위치: `ReplaceDepEntry`가 `replace-deps-resolve.ts`에서 export, `replace-deps.ts`에서 `export type` re-export
8
+ - `WatchReplaceDepResult`가 `replace-deps.ts`에서 export
9
+ - `replace-deps.ts`에서 `resolveAllReplaceDepEntries`를 import하여 사용
10
+ - `commands/publish.ts` import 경로가 `../utils/replace-deps-resolve`로 변경
11
+ - `sd-cli.ts`, `BaseOrchestrator.ts`, `commands/replace-deps.ts`의 import 경로는 변경 없음 (실행 함수만 사용)
12
+ - `tests/utils/replace-deps.spec.ts` import 경로가 `../../src/deps/replace-deps-resolve`로 변경
13
+ - `tests/utils/replace-deps-watch.spec.ts`, `replace-deps-watch.acc.spec.ts`의 import 경로는 변경 없음
14
+ - `replace-deps.ts`에서 `glob` import가 제거됨 (해석 로직 전용)
15
+ - `replace-deps-resolve.ts`에서 `fsx`, `FsWatcher`, `exec` import가 없음 (실행 로직 전용)
@@ -2,8 +2,8 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] watchReplaceDeps 시그니처에 optional `options` 파라미터가 추가되었는가: `replace-deps.ts:321`에 `options?: { onChanged?: () => void }` 확인
6
- - [x] onChanged 콜백이 파일 복사 루프 완료 후(for 루프 바깥) 호출되는가: `replace-deps.ts:393`에서 for 루프(346-392) 완료 후 `options?.onChanged?.()` 호출 확인
7
- - [x] 기존 watchReplaceDeps 호출자가 새 시그니처와 호환되는가: `DevWatchOrchestrator.ts:121`에서 2개 인자로 호출 — 3번째 optional 파라미터 생략으로 호환
8
- - [x] esbuild onEnd 플러그인(esbuild-client-config.ts)이 빌드 결과를 외부로 전달하는 경로가 존재하는가: `esbuild-client-config.ts:215-218`에 `sd-on-end` 플러그인이 `options.onEnd(result)` 호출
9
- - [x] hmrService.onBuildEnd()가 metafile 기반으로 HMR 메시지를 디스패치하는 경로가 존재하는가: `hmr-service.ts:49-133`에 metafile 기반 변경 판별 → WS broadcast 로직 존재
5
+ - watchReplaceDeps 시그니처에 optional `options` 파라미터가 추가되었는가: `replace-deps.ts:321`에 `options?: { onChanged?: () => void }` 확인
6
+ - onChanged 콜백이 파일 복사 루프 완료 후(for 루프 바깥) 호출되는가: `replace-deps.ts:393`에서 for 루프(346-392) 완료 후 `options?.onChanged?.()` 호출 확인
7
+ - 기존 watchReplaceDeps 호출자가 새 시그니처와 호환되는가: `DevWatchOrchestrator.ts:121`에서 2개 인자로 호출 — 3번째 optional 파라미터 생략으로 호환
8
+ - esbuild onEnd 플러그인(esbuild-client-config.ts)이 빌드 결과를 외부로 전달하는 경로가 존재하는가: `esbuild-client-config.ts:215-218`에 `sd-on-end` 플러그인이 `options.onEnd(result)` 호출
9
+ - hmrService.onBuildEnd()가 metafile 기반으로 HMR 메시지를 디스패치하는 경로가 존재하는가: `hmr-service.ts:49-133`에 metafile 기반 변경 판별 → WS broadcast 로직 존재
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] 4개 함수가 utils/server-production-files.ts에 export되어 있다: line 14, 27, 68, 87에 각각 export function 확인
6
- - [x] worker에서 4개 함수 정의가 제거되었다: worker에 함수 정의 없음, import와 호출만 존재
7
- - [x] worker에서 collectAllExternals, generateProductionFiles를 새 모듈에서 import한다: line 19 `import { collectAllExternals, generateProductionFiles } from "../utils/server-production-files"`
8
- - [x] collectAllExternals 시그니처: `(pkgDir: string, manualExternals?: string[]) => string[]` — line 14 확인
9
- - [x] parseLockfileVersions 시그니처: `(cwd: string) => Map<string, string>` — line 27 확인
10
- - [x] resolveLockedVersions 시그니처: `(cwd: string, pkgNames: string[]) => Record<string, string>` — line 68 확인
11
- - [x] generateProductionFiles 시그니처: `(info: ServerBuildInfo, externals: string[]) => void` — line 87 확인
12
- - [x] worker에 build, rebuildAll, startWatch, stopWatch, cleanup, createEsbuildWatchContext가 존재한다: line 116, 148, 259, 336, 359, 488 확인
13
- - [x] worker에서 cpx import가 제거되었다: grep 결과 매칭 없음
14
- - [x] worker에서 collectAllDependencyExternals import가 제거되었다: grep 결과 매칭 없음
5
+ - 4개 함수가 utils/server-production-files.ts에 export되어 있다: line 14, 27, 68, 87에 각각 export function 확인
6
+ - worker에서 4개 함수 정의가 제거되었다: worker에 함수 정의 없음, import와 호출만 존재
7
+ - worker에서 collectAllExternals, generateProductionFiles를 새 모듈에서 import한다: line 19 `import { collectAllExternals, generateProductionFiles } from "../utils/server-production-files"`
8
+ - collectAllExternals 시그니처: `(pkgDir: string, manualExternals?: string[]) => string[]` — line 14 확인
9
+ - parseLockfileVersions 시그니처: `(cwd: string) => Map<string, string>` — line 27 확인
10
+ - resolveLockedVersions 시그니처: `(cwd: string, pkgNames: string[]) => Record<string, string>` — line 68 확인
11
+ - generateProductionFiles 시그니처: `(info: ServerBuildInfo, externals: string[]) => void` — line 87 확인
12
+ - worker에 build, rebuildAll, startWatch, stopWatch, cleanup, createEsbuildWatchContext가 존재한다: line 116, 148, 259, 336, 359, 488 확인
13
+ - worker에서 cpx import가 제거되었다: grep 결과 매칭 없음
14
+ - worker에서 collectAllDependencyExternals import가 제거되었다: grep 결과 매칭 없음
@@ -2,6 +2,6 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] enableLint 옵션 제거: `CreateClientViteConfigOptions` 인터페이스에서 `enableLint` 필드가 완전히 제거됨. `vite-config.ts` 전체에서 `enableLint` 문자열 검색 결과 0건.
6
- - [x] replaceDepDistPaths 계산 제거: `vite-config.ts` 전체에서 `replaceDepDistPaths` 문자열 검색 결과 0건. 기존 line 98-114의 계산 로직(fs.realpathSync, pathx.posix 사용)이 완전히 제거됨. `fs`와 `pathx` import도 함께 제거됨.
7
- - [x] tsconfigPath, browserslist, postCssPlugins, legacyModule은 인터페이스 옵션이 아닌 함수 내부 로컬 변수로만 존재: 모두 `loadSdConfig()` 결과에서 추출하거나 `pkgDir`에서 derive됨.
5
+ - enableLint 옵션 제거: `CreateClientViteConfigOptions` 인터페이스에서 `enableLint` 필드가 완전히 제거됨. `vite-config.ts` 전체에서 `enableLint` 문자열 검색 결과 0건.
6
+ - replaceDepDistPaths 계산 제거: `vite-config.ts` 전체에서 `replaceDepDistPaths` 문자열 검색 결과 0건. 기존 line 98-114의 계산 로직(fs.realpathSync, pathx.posix 사용)이 완전히 제거됨. `fs`와 `pathx` import도 함께 제거됨.
7
+ - tsconfigPath, browserslist, postCssPlugins, legacyModule은 인터페이스 옵션이 아닌 함수 내부 로컬 변수로만 존재: 모두 `loadSdConfig()` 결과에서 추출하거나 `pkgDir`에서 derive됨.
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] buildWatchPaths 호출 인자가 기존 코드와 동등: `srcGlobs: ["*.ts"]`, `replaceDeps: info.replaceDeps` — 기존 `pathx.posixResolve(info.pkgDir, "src", "**", "*.ts")` 패턴과 동일한 경로 생성 확인
6
- - [x] workspace deps 경로 생성이 동일: 기존 `workspaceDeps.map((d) => pathx.posixResolve(info.cwd, "packages", d, "src", "**", "*.ts"))` → buildWatchPaths 내부에서 동일하게 `pathx.posixResolve(cwd, "packages", d)` + `src/**/*.ts` 생성
7
- - [x] replaceDeps dist 경로가 cwd와 pkgDir 양쪽에서 생성됨: buildWatchPaths 내부에서 `pathx.posixResolve(cwd, "node_modules", ...pkg.split("/"), "dist", "**", "*.{js,mjs,cjs}")` 및 pkgDir 경로 동일하게 생성
8
- - [x] collectDeps import 제거됨: library-build.worker.ts에서 collectDeps가 더 이상 직접 import되지 않음 — buildWatchPaths가 내부에서 호출
9
- - [x] 변경 필터링 동작 동일: 기존 `hasFileAddOrRemove` 인라인 로직 + `lastSourceFilePaths` 교차가 `shouldSkipRebuild(changes.map(c => c.path), hasFileAddOrRemove(changes), lastSourceFilePaths, logger)`로 동일하게 대체됨
10
- - [x] 기존 테스트(library-build-worker.spec.ts) 16건 모두 통과 확인
5
+ - buildWatchPaths 호출 인자가 기존 코드와 동등: `srcGlobs: ["*.ts"]`, `replaceDeps: info.replaceDeps` — 기존 `pathx.posixResolve(info.pkgDir, "src", "**", "*.ts")` 패턴과 동일한 경로 생성 확인
6
+ - workspace deps 경로 생성이 동일: 기존 `workspaceDeps.map((d) => pathx.posixResolve(info.cwd, "packages", d, "src", "**", "*.ts"))` → buildWatchPaths 내부에서 동일하게 `pathx.posixResolve(cwd, "packages", d)` + `src/**/*.ts` 생성
7
+ - replaceDeps dist 경로가 cwd와 pkgDir 양쪽에서 생성됨: buildWatchPaths 내부에서 `pathx.posixResolve(cwd, "node_modules", ...pkg.split("/"), "dist", "**", "*.{js,mjs,cjs}")` 및 pkgDir 경로 동일하게 생성
8
+ - collectDeps import 제거됨: library-build.worker.ts에서 collectDeps가 더 이상 직접 import되지 않음 — buildWatchPaths가 내부에서 호출
9
+ - 변경 필터링 동작 동일: 기존 `hasFileAddOrRemove` 인라인 로직 + `lastSourceFilePaths` 교차가 `shouldSkipRebuild(changes.map(c => c.path), hasFileAddOrRemove(changes), lastSourceFilePaths, logger)`로 동일하게 대체됨
10
+ - 기존 테스트(library-build-worker.spec.ts) 16건 모두 통과 확인
@@ -2,11 +2,11 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] ngtsc-build watchPaths 동일성: `srcGlobs: ["*.{ts,scss,css}"]`, `extraDirs: [{ dir: "scss", globs: ["*.{scss,css}"] }]` — 기존 코드의 `src/**/*.{ts,scss,css}` + `scss/**/*.{scss,css}` + deps 동일 패턴과 매칭 확인
6
- - [x] ngtsc-build SCSS 역방향 탐색 유지: `hasFileAddOrRemove` → `hasFileAddOrRemoveFn`으로 변경 후에도 `modifiedFiles` 구성 로직(pipeline.findAffectedByScss)이 그대로 유지됨
7
- - [x] ngtsc-build `shouldSkipRebuild(modifiedFiles, addOrRemove, ...)`: SCSS 역방향 탐색 결과가 포함된 `modifiedFiles` Set을 Iterable로 전달하여 정확한 필터링 수행
8
- - [x] server-build watchPaths 동일성: `srcGlobs: ["*"]` — 기존 `src/**/*` 패턴과 동일
9
- - [x] server-build metafile 기반 필터링 유지: `hasFileAddOrRemoveFn(changes)` → `addOrRemove` 변수명으로 변경 외에 metafile 기반 로직(L444-470) 무변경
10
- - [x] server-build esbuild context 재생성 로직 유지: `if (addOrRemove)` 블록 내 context 재생성 로직이 기존과 동일
11
- - [x] collectDeps import 제거: ngtsc-build, server-build 모두에서 collectDeps 직접 import 제거됨 — buildWatchPaths가 내부에서 호출
12
- - [x] 기존 테스트 94건 모두 통과 확인
5
+ - ngtsc-build watchPaths 동일성: `srcGlobs: ["*.{ts,scss,css}"]`, `extraDirs: [{ dir: "scss", globs: ["*.{scss,css}"] }]` — 기존 코드의 `src/**/*.{ts,scss,css}` + `scss/**/*.{scss,css}` + deps 동일 패턴과 매칭 확인
6
+ - ngtsc-build SCSS 역방향 탐색 유지: `hasFileAddOrRemove` → `hasFileAddOrRemoveFn`으로 변경 후에도 `modifiedFiles` 구성 로직(pipeline.findAffectedByScss)이 그대로 유지됨
7
+ - ngtsc-build `shouldSkipRebuild(modifiedFiles, addOrRemove, ...)`: SCSS 역방향 탐색 결과가 포함된 `modifiedFiles` Set을 Iterable로 전달하여 정확한 필터링 수행
8
+ - server-build watchPaths 동일성: `srcGlobs: ["*"]` — 기존 `src/**/*` 패턴과 동일
9
+ - server-build metafile 기반 필터링 유지: `hasFileAddOrRemoveFn(changes)` → `addOrRemove` 변수명으로 변경 외에 metafile 기반 로직(L444-470) 무변경
10
+ - server-build esbuild context 재생성 로직 유지: `if (addOrRemove)` 블록 내 context 재생성 로직이 기존과 동일
11
+ - collectDeps import 제거: ngtsc-build, server-build 모두에서 collectDeps 직접 import 제거됨 — buildWatchPaths가 내부에서 호출
12
+ - 기존 테스트 94건 모두 통과 확인
@@ -2,6 +2,6 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] loadSdConfig import 제거: `client.worker.ts` — `import { loadSdConfig }` 라인이 제거됨. `sd-config` 모듈 import 없음
6
- - [x] resolvePackageInfo가 동기 함수: `client.worker.ts:73` — `function resolvePackageInfo(info: ClientBuildInfo): {` (async 제거, Promise 반환 없음)
7
- - [x] resolvePackageInfo가 info.browserSupport 사용: `client.worker.ts:83-86` — `info.browserSupport?.legacyModule`, `info.browserSupport?.browserslist`, `info.browserSupport?.postCss?.plugins`
5
+ - loadSdConfig import 제거: `client.worker.ts` — `import { loadSdConfig }` 라인이 제거됨. `sd-config` 모듈 import 없음
6
+ - resolvePackageInfo가 동기 함수: `client.worker.ts:73` — `function resolvePackageInfo(info: ClientBuildInfo): {` (async 제거, Promise 반환 없음)
7
+ - resolvePackageInfo가 info.browserSupport 사용: `client.worker.ts:83-86` — `info.browserSupport?.legacyModule`, `info.browserSupport?.browserslist`, `info.browserSupport?.postCss?.plugins`
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] resolvePackageInfo에서 tsconfigPath 반환 제거: 반환 타입이 `{ pkgName: string }`으로 변경됨. 함수 내부에서 `tsconfigPath` 변수 선언 및 계산이 제거됨. 모든 호출부(startWatch:199, startLegacyWatch:266, build:396)에서 `{ pkgName }`으로만 구조분해.
6
- - [x] startWatch에서 제거된 옵션 미전달: `createClientViteConfig` 호출에서 `tsconfigPath`, `browserslist`, `postCssPlugins`, `legacyModule`, `enableLint` 모두 제거됨.
7
- - [x] startLegacyWatch에서 제거된 옵션 미전달: 동일하게 5개 옵션 모두 제거됨.
8
- - [x] build에서 제거된 옵션 미전달: 동일하게 5개 옵션 모두 제거됨.
5
+ - resolvePackageInfo에서 tsconfigPath 반환 제거: 반환 타입이 `{ pkgName: string }`으로 변경됨. 함수 내부에서 `tsconfigPath` 변수 선언 및 계산이 제거됨. 모든 호출부(startWatch:199, startLegacyWatch:266, build:396)에서 `{ pkgName }`으로만 구조분해.
6
+ - startWatch에서 제거된 옵션 미전달: `createClientViteConfig` 호출에서 `tsconfigPath`, `browserslist`, `postCssPlugins`, `legacyModule`, `enableLint` 모두 제거됨.
7
+ - startLegacyWatch에서 제거된 옵션 미전달: 동일하게 5개 옵션 모두 제거됨.
8
+ - build에서 제거된 옵션 미전달: 동일하게 5개 옵션 모두 제거됨.
@@ -2,6 +2,6 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] client.worker.ts onEnd에서 초기 빌드 시 errors 필드가 포함되는지: `client.worker.ts:285-291` — `initialBuildResolve`에 `errors: result.errors.map((e) => e.text)` 포함 확인
6
- - [x] EsbuildClientEngine.startWatch에서 반환값의 success 확인 후 ResultCollector에 보고하는지: `EsbuildClientEngine.ts:129-138` — `!result.success` 조건으로 `resultCollector.add()` 호출 확인. logger.error는 Feature 1.2에서 제거됨 (중복 출력 방지)
7
- - [x] esbuild-client-config.ts에서 logLevel이 "silent"인지: `esbuild-client-config.ts:191` — `logLevel: "silent"` 확인. Feature 1.2에서 dev/build 모두 "silent"로 통일됨
5
+ - client.worker.ts onEnd에서 초기 빌드 시 errors 필드가 포함되는지: `client.worker.ts:285-291` — `initialBuildResolve`에 `errors: result.errors.map((e) => e.text)` 포함 확인
6
+ - EsbuildClientEngine.startWatch에서 반환값의 success 확인 후 ResultCollector에 보고하는지: `EsbuildClientEngine.ts:129-138` — `!result.success` 조건으로 `resultCollector.add()` 호출 확인. logger.error는 Feature 1.2에서 제거됨 (중복 출력 방지)
7
+ - esbuild-client-config.ts에서 logLevel이 "silent"인지: `esbuild-client-config.ts:191` — `logLevel: "silent"` 확인. Feature 1.2에서 dev/build 모두 "silent"로 통일됨
@@ -2,6 +2,6 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] client.worker.ts의 initialBuildResolve에 warnings 필드가 포함되는지: `client.worker.ts:305-315` — `initialBuildResolve`에 `warnings: result.warnings.length > 0 ? result.warnings.map(formatEsbuildMessage) : undefined` 포함 확인. 후속 빌드(line 295-298)와 동일 패턴
6
- - [x] warnings가 없을 때 undefined인지: `client.worker.ts:312-314` — `result.warnings.length > 0` 조건으로 빈 배열일 때 undefined 반환 확인
7
- - [x] ClientBuildResult 인터페이스에 warnings 필드가 있는지: `client.worker.ts:47` — `warnings?: string[]` 확인. 변경 불필요
5
+ - client.worker.ts의 initialBuildResolve에 warnings 필드가 포함되는지: `client.worker.ts:305-315` — `initialBuildResolve`에 `warnings: result.warnings.length > 0 ? result.warnings.map(formatEsbuildMessage) : undefined` 포함 확인. 후속 빌드(line 295-298)와 동일 패턴
6
+ - warnings가 없을 때 undefined인지: `client.worker.ts:312-314` — `result.warnings.length > 0` 조건으로 빈 배열일 때 undefined 반환 확인
7
+ - ClientBuildResult 인터페이스에 warnings 필드가 있는지: `client.worker.ts:47` — `warnings?: string[]` 확인. 변경 불필요
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] createSourceFileCachePlugin이 IncrementalMtimeTracker를 사용하는지 확인: `client.worker.ts:198` — `const mtimeTracker = new IncrementalMtimeTracker();` 확인됨
6
- - [x] onStart에서 detectChanges 결과로 invalidate가 호출되는지 확인: `client.worker.ts:210-213` — `mtimeTracker.detectChanges(watchTargets)` 결과를 `sourceFileCache.invalidate(changedFiles)`에 전달. 기존 로직과 동작 동일
7
- - [x] onEnd에서 updateMtimes가 호출되는지 확인: `client.worker.ts:228` — `mtimeTracker.updateMtimes(watchTargets)` 호출 확인. `prevMtimes.clear()` + 전체 순회 제거됨
8
- - [x] esbuildResult == null 가드가 유지되는지 확인: onStart의 `if (esbuildResult != null)` (라인 202), onEnd의 `if (esbuildResult == null) return;` (라인 222) 모두 유지됨
9
- - [x] isInitialBuild 가드와 sender.send("buildStart")가 유지되는지 확인: `client.worker.ts:216-218` — `if (!isInitialBuild) { sender.send("buildStart", {}); }` 유지됨
10
- - [x] import 경로에 .js 확장자가 없는지 확인: `client.worker.ts:20` — `from "./incremental-mtime-tracker"` (확장자 없음)
5
+ - createSourceFileCachePlugin이 IncrementalMtimeTracker를 사용하는지 확인: `client.worker.ts:198` — `const mtimeTracker = new IncrementalMtimeTracker();` 확인됨
6
+ - onStart에서 detectChanges 결과로 invalidate가 호출되는지 확인: `client.worker.ts:210-213` — `mtimeTracker.detectChanges(watchTargets)` 결과를 `sourceFileCache.invalidate(changedFiles)`에 전달. 기존 로직과 동작 동일
7
+ - onEnd에서 updateMtimes가 호출되는지 확인: `client.worker.ts:228` — `mtimeTracker.updateMtimes(watchTargets)` 호출 확인. `prevMtimes.clear()` + 전체 순회 제거됨
8
+ - esbuildResult == null 가드가 유지되는지 확인: onStart의 `if (esbuildResult != null)` (라인 202), onEnd의 `if (esbuildResult == null) return;` (라인 222) 모두 유지됨
9
+ - isInitialBuild 가드와 sender.send("buildStart")가 유지되는지 확인: `client.worker.ts:216-218` — `if (!isInitialBuild) { sender.send("buildStart", {}); }` 유지됨
10
+ - import 경로에 .js 확장자가 없는지 확인: `client.worker.ts:20` — `from "./incremental-mtime-tracker"` (확장자 없음)
@@ -2,6 +2,6 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] onEnd 타입이 `void | Promise<void>`를 허용: `esbuild-client-config.ts:29` — `onEnd?: (result: esbuild.BuildResult) => void | Promise<void>;`
6
- - [x] sd-on-end 플러그인이 onEnd 반환값을 return: `esbuild-client-config.ts:220` — `return options.onEnd!(result);`
7
- - [x] client.worker.ts onEnd가 async 직접 전달 (fire-and-forget 제거): `client.worker.ts:240` — `onEnd: async (result: esbuild.BuildResult) => {` (이전의 `void (async () => { ... })()` 패턴 제거됨)
5
+ - onEnd 타입이 `void | Promise<void>`를 허용: `esbuild-client-config.ts:29` — `onEnd?: (result: esbuild.BuildResult) => void | Promise<void>;`
6
+ - sd-on-end 플러그인이 onEnd 반환값을 return: `esbuild-client-config.ts:220` — `return options.onEnd!(result);`
7
+ - client.worker.ts onEnd가 async 직접 전달 (fire-and-forget 제거): `client.worker.ts:240` — `onEnd: async (result: esbuild.BuildResult) => {` (이전의 `void (async () => { ... })()` 패턴 제거됨)
@@ -2,21 +2,21 @@
2
2
 
3
3
  ## 검증 항목
4
4
 
5
- - [x] createSourceFileCachePlugin이 모듈 스코프에 존재하고 esbuild.Plugin을 반환한다: lines 192-250, `function createSourceFileCachePlugin(): esbuild.Plugin` 반환 타입 명시, `name: "sd-build-start"` 유지
6
- - [x] createDevBuildEndHandler가 모듈 스코프에 존재하고 onEnd 콜백을 반환한다: lines 255-322, 반환 타입 `(result: esbuild.BuildResult) => Promise<void>`
7
- - [x] startWatch에서 추출 함수를 호출한다: line 385 `plugins: [createSourceFileCachePlugin()]`, line 386 `onEnd: createDevBuildEndHandler(basePath, actualPort, outdir, entryNames, info.pkgDir)`
8
- - [x] 파일 변경 감지 로직이 동일하다: createSourceFileCachePlugin onStart (lines 198-230) — watchFiles + typeScriptFileCache 순회, mtime 비교, invalidate 호출, buildStart 이벤트 전송 모두 원본과 동일
9
- - [x] mtime 기록 로직이 동일하다: createSourceFileCachePlugin onEnd (lines 232-247) — prevMtimes.clear() + mtime 기록 원본과 동일
10
- - [x] index.html 재생성 로직이 동일하다: createDevBuildEndHandler (lines 264-279) — lastMetafile 보관 + generateIndexHtml + writeFileSync 원본과 동일
11
- - [x] HMR 디스패치 로직이 동일하다: lines 281-284, `hmrService.onBuildEnd(result.metafile)` 조건부 호출 원본과 동일
12
- - [x] build 이벤트 전송 로직이 동일하다: lines 286-300, `sender.send("build", ...)` 조건부 호출 원본과 동일
13
- - [x] 초기 빌드 resolve 로직이 동일하다: lines 302-312, `isInitialBuild = false` + `initialBuildResolve?.()` 원본과 동일
14
- - [x] 에러 핸들링 로직이 동일하다: lines 313-320, catch 블록 원본과 동일
15
- - [x] 외부 인터페이스가 변경되지 않았다: export는 `export default sender`만 존재 (line 501), ClientBuildInfo/ClientBuildResult/ClientWorkerEvents 타입 변경 없음
16
- - [x] build 함수가 변경되지 않았다: lines 96-187, 원본과 동일
17
- - [x] stopWatch에 리셋 로직이 추가되었다: lines 472-475, `lastMetafile = undefined`, `isInitialBuild = true`, `initialBuildResolve = undefined`
18
- - [x] stopWatch 기존 정리 로직이 변경되지 않았다: lines 439-470, 5단계 정리(esbuild dispose, HMR 종료, HTTP 종료, public 감시 종료, index.html 감시 종료) 원본과 동일
19
- - [x] 모듈 스코프 변수가 올바르게 선언되었다: lines 67-69, 기존 변수(62-66) 바로 아래 배치
20
- - [x] startWatch가 ~110줄로 축소되었다: lines 327-434 = 107줄 (목표 ~110줄 달성)
21
- - [x] index.html watcher에서 lastMetafile 접근이 정상 동작한다: line 400, `if (lastMetafile == null) return;` — 모듈 스코프 변수로 접근
22
- - [x] 기존 테스트 6개 전부 통과: client-worker.spec.ts (3), client-worker.acc.spec.ts (3) — 회귀 없음
5
+ - createSourceFileCachePlugin이 모듈 스코프에 존재하고 esbuild.Plugin을 반환한다: lines 192-250, `function createSourceFileCachePlugin(): esbuild.Plugin` 반환 타입 명시, `name: "sd-build-start"` 유지
6
+ - createDevBuildEndHandler가 모듈 스코프에 존재하고 onEnd 콜백을 반환한다: lines 255-322, 반환 타입 `(result: esbuild.BuildResult) => Promise<void>`
7
+ - startWatch에서 추출 함수를 호출한다: line 385 `plugins: [createSourceFileCachePlugin()]`, line 386 `onEnd: createDevBuildEndHandler(basePath, actualPort, outdir, entryNames, info.pkgDir)`
8
+ - 파일 변경 감지 로직이 동일하다: createSourceFileCachePlugin onStart (lines 198-230) — watchFiles + typeScriptFileCache 순회, mtime 비교, invalidate 호출, buildStart 이벤트 전송 모두 원본과 동일
9
+ - mtime 기록 로직이 동일하다: createSourceFileCachePlugin onEnd (lines 232-247) — prevMtimes.clear() + mtime 기록 원본과 동일
10
+ - index.html 재생성 로직이 동일하다: createDevBuildEndHandler (lines 264-279) — lastMetafile 보관 + generateIndexHtml + writeFileSync 원본과 동일
11
+ - HMR 디스패치 로직이 동일하다: lines 281-284, `hmrService.onBuildEnd(result.metafile)` 조건부 호출 원본과 동일
12
+ - build 이벤트 전송 로직이 동일하다: lines 286-300, `sender.send("build", ...)` 조건부 호출 원본과 동일
13
+ - 초기 빌드 resolve 로직이 동일하다: lines 302-312, `isInitialBuild = false` + `initialBuildResolve?.()` 원본과 동일
14
+ - 에러 핸들링 로직이 동일하다: lines 313-320, catch 블록 원본과 동일
15
+ - 외부 인터페이스가 변경되지 않았다: export는 `export default sender`만 존재 (line 501), ClientBuildInfo/ClientBuildResult/ClientWorkerEvents 타입 변경 없음
16
+ - build 함수가 변경되지 않았다: lines 96-187, 원본과 동일
17
+ - stopWatch에 리셋 로직이 추가되었다: lines 472-475, `lastMetafile = undefined`, `isInitialBuild = true`, `initialBuildResolve = undefined`
18
+ - stopWatch 기존 정리 로직이 변경되지 않았다: lines 439-470, 5단계 정리(esbuild dispose, HMR 종료, HTTP 종료, public 감시 종료, index.html 감시 종료) 원본과 동일
19
+ - 모듈 스코프 변수가 올바르게 선언되었다: lines 67-69, 기존 변수(62-66) 바로 아래 배치
20
+ - startWatch가 ~110줄로 축소되었다: lines 327-434 = 107줄 (목표 ~110줄 달성)
21
+ - index.html watcher에서 lastMetafile 접근이 정상 동작한다: line 400, `if (lastMetafile == null) return;` — 모듈 스코프 변수로 접근
22
+ - 기존 테스트 6개 전부 통과: client-worker.spec.ts (3), client-worker.acc.spec.ts (3) — 회귀 없음