@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.
- package/README.md +58 -253
- package/dist/angular/client-transform-stylesheet.js +1 -1
- package/dist/angular/client-transform-stylesheet.js.map +1 -1
- package/dist/angular/vite-angular-plugin.d.ts +4 -2
- package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
- package/dist/angular/vite-angular-plugin.js +73 -36
- package/dist/angular/vite-angular-plugin.js.map +1 -1
- package/dist/angular/vite-postcss-inline-plugin.d.ts +1 -1
- package/dist/angular/vite-postcss-inline-plugin.js +1 -1
- package/dist/capacitor/capacitor.d.ts +20 -2
- package/dist/capacitor/capacitor.d.ts.map +1 -1
- package/dist/capacitor/capacitor.js +155 -28
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/build.d.ts +3 -10
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +3 -10
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/check.js +3 -3
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/dev.d.ts +3 -9
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +3 -9
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/device.d.ts +3 -3
- package/dist/commands/device.js +5 -5
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/publish.d.ts +1 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +24 -26
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/replace-deps.d.ts +3 -3
- package/dist/commands/replace-deps.d.ts.map +1 -1
- package/dist/commands/replace-deps.js +1 -1
- package/dist/commands/typecheck.d.ts +4 -3
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +5 -11
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/commands/watch.d.ts +9 -9
- package/dist/commands/watch.js +9 -9
- package/dist/electron/electron.d.ts.map +1 -1
- package/dist/electron/electron.js +42 -3
- package/dist/electron/electron.js.map +1 -1
- package/dist/engines/BaseEngine.d.ts +1 -1
- package/dist/engines/BaseEngine.d.ts.map +1 -1
- package/dist/engines/BaseEngine.js +3 -1
- package/dist/engines/BaseEngine.js.map +1 -1
- package/dist/engines/NgtscEngine.d.ts +7 -7
- package/dist/engines/NgtscEngine.d.ts.map +1 -1
- package/dist/engines/NgtscEngine.js +3 -3
- package/dist/engines/ServerEsbuildEngine.d.ts +7 -7
- package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -1
- package/dist/engines/ServerEsbuildEngine.js +3 -3
- package/dist/engines/TscEngine.d.ts +7 -7
- package/dist/engines/TscEngine.d.ts.map +1 -1
- package/dist/engines/TscEngine.js +3 -3
- package/dist/engines/ViteEngine.d.ts +7 -1
- package/dist/engines/ViteEngine.d.ts.map +1 -1
- package/dist/engines/ViteEngine.js +13 -12
- package/dist/engines/ViteEngine.js.map +1 -1
- package/dist/engines/index.d.ts +9 -5
- package/dist/engines/index.d.ts.map +1 -1
- package/dist/engines/index.js +7 -5
- package/dist/engines/index.js.map +1 -1
- package/dist/engines/types.d.ts +20 -20
- package/dist/engines/types.d.ts.map +1 -1
- package/dist/infra/ResultCollector.d.ts +9 -9
- package/dist/infra/ResultCollector.js +8 -8
- package/dist/infra/SignalHandler.d.ts +7 -7
- package/dist/infra/SignalHandler.js +7 -7
- package/dist/infra/WorkerManager.d.ts +14 -14
- package/dist/infra/WorkerManager.js +14 -14
- package/dist/orchestrators/BuildOrchestrator.d.ts +25 -25
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +34 -30
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.d.ts +7 -7
- package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.js +34 -34
- package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.d.ts +2 -2
- package/dist/sd-cli-entry.d.ts.map +1 -1
- package/dist/sd-cli-entry.js +15 -8
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.d.ts +3 -3
- package/dist/sd-cli.js +16 -16
- package/dist/sd-cli.js.map +1 -1
- package/dist/sd-config.types.d.ts +105 -105
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/utils/angular-compiler.js +5 -5
- package/dist/utils/angular-compiler.js.map +1 -1
- package/dist/utils/build-env.d.ts +1 -1
- package/dist/utils/build-env.js +1 -1
- package/dist/utils/concurrency.d.ts +7 -7
- package/dist/utils/concurrency.js +7 -7
- package/dist/utils/copy-public.d.ts +9 -9
- package/dist/utils/copy-public.js +17 -17
- package/dist/utils/copy-public.js.map +1 -1
- package/dist/utils/copy-src.d.ts +9 -9
- package/dist/utils/copy-src.js +11 -11
- package/dist/utils/copy-src.js.map +1 -1
- package/dist/utils/engine-stop.d.ts +8 -9
- package/dist/utils/engine-stop.d.ts.map +1 -1
- package/dist/utils/engine-stop.js +9 -10
- package/dist/utils/engine-stop.js.map +1 -1
- package/dist/utils/esbuild-config.d.ts +23 -23
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/esbuild-config.js +25 -25
- package/dist/utils/esbuild-config.js.map +1 -1
- package/dist/utils/lint-with-program.d.ts +15 -15
- package/dist/utils/lint-with-program.d.ts.map +1 -1
- package/dist/utils/lint-with-program.js +29 -29
- package/dist/utils/lint-with-program.js.map +1 -1
- package/dist/utils/ngtsc-build-core.d.ts +8 -8
- package/dist/utils/ngtsc-build-core.d.ts.map +1 -1
- package/dist/utils/ngtsc-build-core.js +14 -14
- package/dist/utils/ngtsc-build-core.js.map +1 -1
- package/dist/utils/output-path-rewriter.d.ts +14 -14
- package/dist/utils/output-path-rewriter.js +18 -18
- package/dist/utils/output-path-rewriter.js.map +1 -1
- package/dist/utils/output-utils.d.ts +6 -6
- package/dist/utils/output-utils.js +11 -11
- package/dist/utils/output-utils.js.map +1 -1
- package/dist/utils/package-utils.d.ts +21 -21
- package/dist/utils/package-utils.d.ts.map +1 -1
- package/dist/utils/package-utils.js +56 -45
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/utils/replace-deps.d.ts +25 -25
- package/dist/utils/replace-deps.d.ts.map +1 -1
- package/dist/utils/replace-deps.js +84 -65
- package/dist/utils/replace-deps.js.map +1 -1
- package/dist/utils/sd-config.d.ts +3 -3
- package/dist/utils/sd-config.js +3 -3
- package/dist/utils/tsc-build.d.ts +13 -13
- package/dist/utils/tsc-build.d.ts.map +1 -1
- package/dist/utils/tsc-build.js +9 -9
- package/dist/utils/tsc-build.js.map +1 -1
- package/dist/utils/tsconfig.d.ts +11 -9
- package/dist/utils/tsconfig.d.ts.map +1 -1
- package/dist/utils/tsconfig.js +11 -9
- package/dist/utils/tsconfig.js.map +1 -1
- package/dist/utils/typecheck-non-package.d.ts +5 -6
- package/dist/utils/typecheck-non-package.d.ts.map +1 -1
- package/dist/utils/typecheck-non-package.js +7 -8
- package/dist/utils/typecheck-non-package.js.map +1 -1
- package/dist/utils/typecheck-serialization.d.ts +8 -8
- package/dist/utils/typecheck-serialization.d.ts.map +1 -1
- package/dist/utils/typecheck-serialization.js +12 -16
- package/dist/utils/typecheck-serialization.js.map +1 -1
- package/dist/utils/vite-config.d.ts +12 -5
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +95 -41
- package/dist/utils/vite-config.js.map +1 -1
- package/dist/utils/vite-scope-watch-plugin.d.ts.map +1 -1
- package/dist/utils/vite-scope-watch-plugin.js +1 -1
- package/dist/utils/vite-scope-watch-plugin.js.map +1 -1
- package/dist/utils/worker-events.d.ts +12 -12
- package/dist/utils/worker-events.d.ts.map +1 -1
- package/dist/utils/worker-events.js +10 -10
- package/dist/utils/worker-events.js.map +1 -1
- package/dist/utils/worker-utils.d.ts +12 -13
- package/dist/utils/worker-utils.d.ts.map +1 -1
- package/dist/utils/worker-utils.js +12 -13
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/vitest-plugin.d.ts.map +1 -1
- package/dist/vitest-plugin.js +5 -7
- package/dist/vitest-plugin.js.map +1 -1
- package/dist/workers/client.worker.d.ts +8 -2
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +215 -6
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/library-build.worker.d.ts +1 -1
- package/dist/workers/library-build.worker.d.ts.map +1 -1
- package/dist/workers/library-build.worker.js +7 -7
- package/dist/workers/library-build.worker.js.map +1 -1
- package/dist/workers/lint.worker.d.ts +2 -2
- package/dist/workers/lint.worker.js +2 -2
- package/dist/workers/ngtsc-build.worker.js +30 -30
- package/dist/workers/ngtsc-build.worker.js.map +1 -1
- package/dist/workers/server-build.worker.d.ts +17 -17
- package/dist/workers/server-build.worker.d.ts.map +1 -1
- package/dist/workers/server-build.worker.js +46 -46
- package/dist/workers/server-build.worker.js.map +1 -1
- package/dist/workers/server-runtime.worker.d.ts +7 -7
- package/dist/workers/server-runtime.worker.d.ts.map +1 -1
- package/dist/workers/server-runtime.worker.js +17 -17
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/docs/config.md +340 -0
- package/docs/publish-configuration-types.md +87 -0
- package/docs/pwa-configuration-types.md +55 -0
- package/docs/vitest-plugin.md +47 -0
- package/package.json +9 -7
- package/src/angular/client-transform-stylesheet.ts +1 -1
- package/src/angular/vite-angular-plugin.ts +89 -39
- package/src/angular/vite-postcss-inline-plugin.ts +1 -1
- package/src/capacitor/capacitor.ts +185 -38
- package/src/commands/build.ts +3 -10
- package/src/commands/check.ts +3 -3
- package/src/commands/dev.ts +3 -9
- package/src/commands/device.ts +5 -5
- package/src/commands/publish.ts +30 -26
- package/src/commands/replace-deps.ts +3 -3
- package/src/commands/typecheck.ts +7 -13
- package/src/commands/watch.ts +9 -9
- package/src/electron/electron.ts +49 -4
- package/src/engines/BaseEngine.ts +4 -1
- package/src/engines/NgtscEngine.ts +7 -7
- package/src/engines/ServerEsbuildEngine.ts +7 -7
- package/src/engines/TscEngine.ts +7 -7
- package/src/engines/ViteEngine.ts +18 -13
- package/src/engines/index.ts +11 -5
- package/src/engines/types.ts +20 -20
- package/src/infra/ResultCollector.ts +9 -9
- package/src/infra/SignalHandler.ts +7 -7
- package/src/infra/WorkerManager.ts +14 -14
- package/src/orchestrators/BuildOrchestrator.ts +42 -38
- package/src/orchestrators/DevWatchOrchestrator.ts +36 -36
- package/src/sd-cli-entry.ts +15 -8
- package/src/sd-cli.ts +16 -16
- package/src/sd-config.types.ts +107 -107
- package/src/utils/angular-compiler.ts +5 -5
- package/src/utils/build-env.ts +1 -1
- package/src/utils/concurrency.ts +7 -7
- package/src/utils/copy-public.ts +17 -17
- package/src/utils/copy-src.ts +11 -11
- package/src/utils/engine-stop.ts +9 -10
- package/src/utils/esbuild-config.ts +29 -29
- package/src/utils/lint-with-program.ts +34 -34
- package/src/utils/ngtsc-build-core.ts +17 -17
- package/src/utils/output-path-rewriter.ts +18 -18
- package/src/utils/output-utils.ts +11 -11
- package/src/utils/package-utils.ts +57 -45
- package/src/utils/replace-deps.ts +92 -67
- package/src/utils/sd-config.ts +3 -3
- package/src/utils/tsc-build.ts +18 -18
- package/src/utils/tsconfig.ts +11 -9
- package/src/utils/typecheck-non-package.ts +7 -8
- package/src/utils/typecheck-serialization.ts +13 -15
- package/src/utils/vite-config.ts +108 -46
- package/src/utils/vite-scope-watch-plugin.ts +6 -1
- package/src/utils/worker-events.ts +16 -16
- package/src/utils/worker-utils.ts +12 -13
- package/src/vitest-plugin.ts +5 -8
- package/src/workers/client.worker.ts +246 -7
- package/src/workers/library-build.worker.ts +8 -8
- package/src/workers/lint.worker.ts +2 -2
- package/src/workers/ngtsc-build.worker.ts +31 -31
- package/src/workers/server-build.worker.ts +60 -60
- package/src/workers/server-runtime.worker.ts +22 -22
- package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +1 -0
- package/tests/angular/vite-angular-plugin-hmr.spec.ts +78 -0
- package/tests/angular/vite-angular-plugin.spec.ts +67 -0
- package/tests/capacitor/capacitor-build.spec.ts +93 -11
- package/tests/capacitor/capacitor-icon.spec.ts +7 -5
- package/tests/capacitor/capacitor-init.spec.ts +124 -10
- package/tests/capacitor/capacitor-run.spec.ts +14 -17
- package/tests/capacitor/capacitor-workspace.spec.ts +5 -3
- package/tests/commands/check.spec.ts +2 -2
- package/tests/commands/publish.spec.ts +2 -2
- package/tests/commands/typecheck.spec.ts +8 -0
- package/tests/electron/electron.spec.ts +12 -10
- package/tests/engines/base-engine.spec.ts +37 -0
- package/tests/engines/vite-engine.spec.ts +115 -3
- package/tests/utils/vite-config.spec.ts +162 -90
- package/tests/workers/client-worker.spec.ts +690 -0
- 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
|
-
*
|
|
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
|
-
/**
|
|
35
|
+
/** 빌드 시 치환할 환경변수 */
|
|
36
36
|
env?: Record<string, string>;
|
|
37
|
-
/**
|
|
37
|
+
/** 런타임 설정 (dist/.config.json에 기록) */
|
|
38
38
|
configs?: Record<string, unknown>;
|
|
39
|
-
/**
|
|
39
|
+
/** sd.config.ts에서 수동 지정한 외부 모듈 */
|
|
40
40
|
externals?: string[];
|
|
41
|
-
/** PM2
|
|
41
|
+
/** PM2 설정 (지정 시 dist/pm2.config.cjs 생성) */
|
|
42
42
|
pm2?: {
|
|
43
43
|
name?: string;
|
|
44
44
|
ignoreWatchPaths?: string[];
|
|
45
45
|
};
|
|
46
|
-
/**
|
|
46
|
+
/** 사용할 패키지 매니저 (mise.toml 또는 volta 설정 생성에 영향) */
|
|
47
47
|
packageManager?: "volta" | "mise";
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
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
|
-
/**
|
|
58
|
+
/** 빌드 시 치환할 환경변수 */
|
|
59
59
|
env?: Record<string, string>;
|
|
60
|
-
/**
|
|
60
|
+
/** 런타임 설정 (dist/.config.json에 기록) */
|
|
61
61
|
configs?: Record<string, unknown>;
|
|
62
|
-
/**
|
|
62
|
+
/** sd.config.ts에서 수동 지정한 외부 모듈 */
|
|
63
63
|
externals?: string[];
|
|
64
|
-
/**
|
|
64
|
+
/** sd.config.ts의 replaceDeps 설정 */
|
|
65
65
|
replaceDeps?: Record<string, string>;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
103
|
+
/** esbuild 빌드 컨텍스트 (정리 대상) */
|
|
104
104
|
let esbuildContext: esbuild.BuildContext | undefined;
|
|
105
105
|
|
|
106
|
-
/**
|
|
106
|
+
/** 마지막 빌드 metafile (리빌드 시 변경 파일 필터링용) */
|
|
107
107
|
let lastMetafile: esbuild.Metafile | undefined;
|
|
108
108
|
|
|
109
|
-
/**
|
|
109
|
+
/** public 파일 감시자 (정리 대상) */
|
|
110
110
|
let publicWatcher: FsWatcher | undefined;
|
|
111
111
|
|
|
112
|
-
/**
|
|
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
|
-
*
|
|
139
|
-
*
|
|
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
|
-
*
|
|
151
|
-
* Lockfile v9
|
|
152
|
-
*
|
|
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
|
-
//
|
|
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; //
|
|
172
|
+
break; // 다음 최상위 섹션
|
|
173
173
|
}
|
|
174
174
|
if (!inPackages) continue;
|
|
175
175
|
|
|
176
|
-
//
|
|
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
|
-
//
|
|
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
|
-
*
|
|
193
|
-
*
|
|
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
|
-
*
|
|
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.
|
|
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
|
-
*
|
|
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
|
-
//
|
|
329
|
+
// tsconfig 파싱
|
|
330
330
|
const parsedConfig = parseTsconfig(info.pkgDir);
|
|
331
331
|
const entryPoints = getPackageSourceFiles(info.pkgDir, parsedConfig);
|
|
332
332
|
|
|
333
|
-
//
|
|
333
|
+
// 외부 모듈 수집
|
|
334
334
|
const external = collectAllExternals(info.pkgDir, info.externals);
|
|
335
335
|
|
|
336
|
-
// esbuild (
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
427
|
+
// watch 모드용 가변 상태
|
|
428
428
|
let watchInfo: ServerWatchInfo | undefined;
|
|
429
429
|
let watchLintRunner: LintWithProgramRunner | undefined;
|
|
430
430
|
|
|
431
431
|
/**
|
|
432
|
-
*
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
//
|
|
541
|
+
// 외부 모듈 수집 (watch 모드용 캐시)
|
|
542
542
|
let cachedExternal = collectAllExternals(info.pkgDir, info.externals);
|
|
543
543
|
|
|
544
|
-
//
|
|
544
|
+
// esbuild 컨텍스트 생성 (JS 출력 필요 시)
|
|
545
545
|
if (info.output.js) {
|
|
546
546
|
esbuildContext = await createEsbuildWatchContext(info, entryPoints, cachedExternal);
|
|
547
547
|
}
|
|
548
548
|
|
|
549
|
-
//
|
|
549
|
+
// 초기 빌드: esbuild + tsc 병렬
|
|
550
550
|
sender.send("buildStart", {});
|
|
551
551
|
const initialResult = await rebuildAll();
|
|
552
552
|
|
|
553
|
-
//
|
|
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
|
-
//
|
|
559
|
+
// public/ + public-dev/ 감시
|
|
560
560
|
publicWatcher = await watchPublicFiles(info.pkgDir, true);
|
|
561
561
|
|
|
562
|
-
//
|
|
562
|
+
// 의존성 기반 감시 경로 수집
|
|
563
563
|
const { workspaceDeps, replaceDeps } = collectDeps(info.pkgDir, info.cwd, info.replaceDeps);
|
|
564
564
|
|
|
565
565
|
const watchPaths: string[] = [];
|
|
566
566
|
|
|
567
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
594
|
+
// 파일 추가/삭제 시 컨텍스트 재생성
|
|
595
595
|
const newParsedConfig = parseTsconfig(info.pkgDir);
|
|
596
596
|
const newEntryPoints = getPackageSourceFiles(info.pkgDir, newParsedConfig);
|
|
597
597
|
|
|
598
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
*
|
|
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
|
-
*
|
|
12
|
+
* 서버 런타임 시작 정보
|
|
13
13
|
*/
|
|
14
14
|
export interface ServerRuntimeStartInfo {
|
|
15
15
|
mainJsPath: string;
|
|
16
|
-
/** Client Vite dev server
|
|
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
|
-
*
|
|
22
|
+
* 서버 준비 완료 이벤트
|
|
23
23
|
*/
|
|
24
24
|
export interface ServerRuntimeReadyEvent {
|
|
25
25
|
port: number;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* 에러 이벤트
|
|
30
30
|
*/
|
|
31
31
|
export interface ServerRuntimeErrorEvent {
|
|
32
32
|
message: string;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
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
|
-
/**
|
|
49
|
+
/** 서버 인스턴스 (정리 대상) */
|
|
50
50
|
let serverInstance: { close: () => Promise<void> } | undefined;
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
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
|
-
//
|
|
64
|
-
// (
|
|
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
|
-
//
|
|
71
|
-
setTimeout(() => process.exit(1),
|
|
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
|
-
//
|
|
80
|
-
setTimeout(() => process.exit(1),
|
|
79
|
+
// 이벤트 전송 후 종료할 수 있도록 대기
|
|
80
|
+
setTimeout(() => process.exit(1), 500);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
registerCleanupHandlers(cleanup, logger);
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
116
|
-
*
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
140
|
+
// 서버 인스턴스 저장 (정리용)
|
|
141
141
|
serverInstance = server;
|
|
142
142
|
|
|
143
|
-
//
|
|
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
|
-
//
|
|
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
|
});
|