@simplysm/sd-cli 13.0.11 → 13.0.12
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/dist/builders/BaseBuilder.d.ts +15 -4
- package/dist/builders/BaseBuilder.d.ts.map +1 -1
- package/dist/builders/BaseBuilder.js +50 -0
- package/dist/builders/BaseBuilder.js.map +1 -1
- package/dist/builders/DtsBuilder.d.ts.map +1 -1
- package/dist/builders/DtsBuilder.js +2 -39
- package/dist/builders/DtsBuilder.js.map +1 -1
- package/dist/builders/LibraryBuilder.d.ts.map +1 -1
- package/dist/builders/LibraryBuilder.js +2 -39
- package/dist/builders/LibraryBuilder.js.map +1 -1
- package/dist/builders/types.d.ts +2 -2
- package/dist/builders/types.d.ts.map +1 -1
- package/dist/capacitor/capacitor.d.ts.map +1 -1
- package/dist/capacitor/capacitor.js +2 -1
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/add-client.d.ts.map +1 -1
- package/dist/commands/add-client.js +1 -9
- package/dist/commands/add-client.js.map +1 -1
- package/dist/commands/add-server.d.ts.map +1 -1
- package/dist/commands/add-server.js +1 -9
- package/dist/commands/add-server.js.map +1 -1
- package/dist/commands/build.d.ts +1 -2
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +12 -311
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.d.ts +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +11 -432
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +17 -32
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -9
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +1 -1
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +71 -118
- package/dist/commands/lint.js.map +2 -2
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +47 -69
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/typecheck.d.ts +1 -1
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +11 -24
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/infra/ResultCollector.d.ts +1 -1
- package/dist/infra/ResultCollector.d.ts.map +1 -1
- package/dist/infra/ResultCollector.js +1 -1
- package/dist/infra/ResultCollector.js.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.d.ts +53 -0
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -0
- package/dist/orchestrators/BuildOrchestrator.js +338 -0
- package/dist/orchestrators/BuildOrchestrator.js.map +6 -0
- package/dist/orchestrators/DevOrchestrator.d.ts +64 -0
- package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -0
- package/dist/orchestrators/DevOrchestrator.js +524 -0
- package/dist/orchestrators/DevOrchestrator.js.map +6 -0
- package/dist/orchestrators/WatchOrchestrator.d.ts +1 -1
- package/dist/orchestrators/WatchOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/WatchOrchestrator.js +30 -21
- package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
- package/dist/orchestrators/index.d.ts +2 -0
- package/dist/orchestrators/index.d.ts.map +1 -1
- package/dist/orchestrators/index.js +4 -0
- package/dist/orchestrators/index.js.map +1 -1
- package/dist/sd-cli-entry.js +14 -14
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.js +6 -4
- package/dist/sd-cli.js.map +1 -1
- package/dist/utils/output-utils.d.ts +0 -1
- package/dist/utils/output-utils.d.ts.map +1 -1
- package/dist/utils/output-utils.js +1 -1
- package/dist/utils/output-utils.js.map +1 -1
- package/dist/utils/package-utils.d.ts +5 -1
- package/dist/utils/package-utils.d.ts.map +1 -1
- package/dist/utils/package-utils.js +12 -0
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/utils/rebuild-manager.d.ts +15 -0
- package/dist/utils/rebuild-manager.d.ts.map +1 -0
- package/dist/utils/rebuild-manager.js +50 -0
- package/dist/utils/rebuild-manager.js.map +6 -0
- package/dist/utils/replace-deps.d.ts.map +1 -1
- package/dist/utils/replace-deps.js +7 -13
- package/dist/utils/replace-deps.js.map +1 -1
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +43 -5
- package/dist/utils/vite-config.js.map +1 -1
- package/dist/utils/worker-events.d.ts +5 -4
- package/dist/utils/worker-events.d.ts.map +1 -1
- package/dist/utils/worker-events.js +4 -0
- package/dist/utils/worker-events.js.map +1 -1
- package/dist/utils/worker-utils.d.ts +13 -0
- package/dist/utils/worker-utils.d.ts.map +1 -0
- package/dist/utils/worker-utils.js +15 -0
- package/dist/utils/worker-utils.js.map +6 -0
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +2 -14
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/library.worker.d.ts.map +1 -1
- package/dist/workers/library.worker.js +2 -14
- package/dist/workers/library.worker.js.map +1 -1
- package/dist/workers/server-runtime.worker.d.ts.map +1 -1
- package/dist/workers/server-runtime.worker.js +11 -11
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/dist/workers/server.worker.d.ts.map +1 -1
- package/dist/workers/server.worker.js +2 -14
- package/dist/workers/server.worker.js.map +1 -1
- package/package.json +4 -5
- package/src/builders/BaseBuilder.ts +71 -4
- package/src/builders/DtsBuilder.ts +2 -49
- package/src/builders/LibraryBuilder.ts +2 -51
- package/src/builders/types.ts +2 -2
- package/src/capacitor/capacitor.ts +2 -1
- package/src/commands/add-client.ts +1 -14
- package/src/commands/add-server.ts +1 -13
- package/src/commands/build.ts +13 -443
- package/src/commands/dev.ts +12 -582
- package/src/commands/device.ts +17 -34
- package/src/commands/init.ts +1 -13
- package/src/commands/lint.ts +85 -146
- package/src/commands/publish.ts +58 -76
- package/src/commands/typecheck.ts +13 -46
- package/src/index.ts +1 -0
- package/src/infra/ResultCollector.ts +2 -2
- package/src/orchestrators/BuildOrchestrator.ts +499 -0
- package/src/orchestrators/DevOrchestrator.ts +703 -0
- package/src/orchestrators/WatchOrchestrator.ts +42 -25
- package/src/orchestrators/index.ts +2 -0
- package/src/sd-cli-entry.ts +14 -14
- package/src/sd-cli.ts +6 -4
- package/src/utils/output-utils.ts +1 -2
- package/src/utils/package-utils.ts +16 -1
- package/src/utils/rebuild-manager.ts +65 -0
- package/src/utils/replace-deps.ts +25 -23
- package/src/utils/vite-config.ts +84 -6
- package/src/utils/worker-events.ts +13 -5
- package/src/utils/worker-utils.ts +26 -0
- package/src/workers/client.worker.ts +3 -19
- package/src/workers/library.worker.ts +2 -19
- package/src/workers/server-runtime.worker.ts +16 -17
- package/src/workers/server.worker.ts +2 -19
- package/templates/add-client/__CLIENT__/package.json.hbs +1 -1
- package/templates/add-server/__SERVER__/package.json.hbs +2 -2
- package/templates/init/package.json.hbs +3 -3
- package/dist/utils/listr-manager.d.ts +0 -37
- package/dist/utils/listr-manager.d.ts.map +0 -1
- package/dist/utils/listr-manager.js +0 -59
- package/dist/utils/listr-manager.js.map +0 -6
- package/src/utils/listr-manager.ts +0 -89
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import os from "os";
|
|
4
|
-
import { Listr } from "listr2";
|
|
5
4
|
import { pathPosix, pathFilterByTargets, Worker, type WorkerProxy } from "@simplysm/core-node";
|
|
6
5
|
import "@simplysm/core-common";
|
|
7
|
-
import { consola
|
|
6
|
+
import { consola } from "consola";
|
|
8
7
|
import type { SdConfig } from "../sd-config.types";
|
|
9
8
|
import { parseRootTsconfig, type TypecheckEnv } from "../utils/tsconfig";
|
|
10
9
|
import { loadSdConfig } from "../utils/sd-config";
|
|
@@ -163,7 +162,7 @@ function createTypecheckTasks(
|
|
|
163
162
|
* - `sd.config.ts`를 로드하여 패키지별 타겟 정보 확인 (없으면 기본값 사용)
|
|
164
163
|
* - Worker threads를 사용하여 실제 병렬 타입체크 수행
|
|
165
164
|
* - incremental 컴파일 사용 (`.cache/typecheck-{env}.tsbuildinfo`)
|
|
166
|
-
* -
|
|
165
|
+
* - consola 로깅을 사용하여 진행 상황 표시
|
|
167
166
|
* - 에러 발생 시 `process.exitCode = 1` 설정
|
|
168
167
|
*
|
|
169
168
|
* @param options - 타입체크 실행 옵션
|
|
@@ -187,7 +186,7 @@ export async function runTypecheck(options: TypecheckOptions): Promise<void> {
|
|
|
187
186
|
try {
|
|
188
187
|
parsedConfig = parseRootTsconfig(cwd);
|
|
189
188
|
} catch (err) {
|
|
190
|
-
|
|
189
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
191
190
|
process.exitCode = 1;
|
|
192
191
|
return;
|
|
193
192
|
}
|
|
@@ -233,39 +232,33 @@ export async function runTypecheck(options: TypecheckOptions): Promise<void> {
|
|
|
233
232
|
const concurrency = Math.min(maxConcurrency, tasks.length);
|
|
234
233
|
logger.debug("동시성 설정", { concurrency, maxConcurrency, taskCount: tasks.length });
|
|
235
234
|
|
|
236
|
-
// Worker 풀 생성
|
|
235
|
+
// Worker 풀 생성
|
|
237
236
|
const workerPath = import.meta.resolve("../workers/dts.worker");
|
|
238
237
|
const workers: WorkerProxy<typeof DtsWorkerModule>[] = [];
|
|
239
238
|
for (let i = 0; i < concurrency; i++) {
|
|
240
239
|
workers.push(Worker.create<typeof DtsWorkerModule>(workerPath));
|
|
241
240
|
}
|
|
242
241
|
|
|
243
|
-
// 결과 수집용
|
|
244
242
|
const allResults: { displayName: string; result: DtsBuildResult }[] = [];
|
|
245
243
|
|
|
246
|
-
// listr2-Worker 연동 패턴:
|
|
247
|
-
// 1. listr2의 각 task는 Promise를 반환하고, 해당 Promise가 resolve되면 task가 완료됨
|
|
248
|
-
// 2. taskResolvers 맵에 task별 resolve 함수를 저장
|
|
249
|
-
// 3. Worker가 작업 완료 시 해당 task의 resolver를 호출하여 listr2 UI 업데이트
|
|
250
|
-
// 4. Worker 풀은 독립적으로 작업 큐에서 task를 가져와 실행
|
|
251
|
-
const taskResolvers = new Map<string, () => void>();
|
|
252
|
-
|
|
253
244
|
try {
|
|
254
|
-
// 작업 큐
|
|
255
245
|
let taskIndex = 0;
|
|
256
246
|
|
|
257
|
-
// Worker에서 작업 실행
|
|
258
247
|
async function runNextTask(worker: WorkerProxy<typeof DtsWorkerModule>): Promise<void> {
|
|
259
248
|
while (taskIndex < tasks.length) {
|
|
260
249
|
const currentIndex = taskIndex++;
|
|
261
250
|
const task = tasks[currentIndex];
|
|
262
251
|
|
|
252
|
+
logger.debug(`[${task.displayName}] 타입체크 시작`);
|
|
263
253
|
try {
|
|
264
254
|
const result = await worker.buildDts(task.buildInfo);
|
|
265
|
-
|
|
266
255
|
allResults.push({ displayName: task.displayName, result });
|
|
256
|
+
if (result.success) {
|
|
257
|
+
logger.debug(`[${task.displayName}] 타입체크 완료`);
|
|
258
|
+
} else {
|
|
259
|
+
logger.debug(`[${task.displayName}] 타입체크 실패`, { errorCount: result.errorCount });
|
|
260
|
+
}
|
|
267
261
|
} catch (err) {
|
|
268
|
-
// Worker 오류 로깅 및 결과로 변환
|
|
269
262
|
logger.error(`Worker 오류: ${task.displayName}`, {
|
|
270
263
|
error: err instanceof Error ? err.message : String(err),
|
|
271
264
|
});
|
|
@@ -279,40 +272,14 @@ export async function runTypecheck(options: TypecheckOptions): Promise<void> {
|
|
|
279
272
|
warningCount: 0,
|
|
280
273
|
},
|
|
281
274
|
});
|
|
282
|
-
} finally {
|
|
283
|
-
// 성공/실패 모두 task 완료 처리
|
|
284
|
-
taskResolvers.get(task.displayName)?.();
|
|
285
275
|
}
|
|
286
276
|
}
|
|
287
277
|
}
|
|
288
278
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
title: task.displayName,
|
|
293
|
-
task: () =>
|
|
294
|
-
new Promise<void>((resolve) => {
|
|
295
|
-
taskResolvers.set(task.displayName, resolve);
|
|
296
|
-
}),
|
|
297
|
-
})),
|
|
298
|
-
{
|
|
299
|
-
concurrent: concurrency,
|
|
300
|
-
exitOnError: false,
|
|
301
|
-
renderer: consola.level >= LogLevels.debug ? "verbose" : "default",
|
|
302
|
-
},
|
|
303
|
-
);
|
|
304
|
-
|
|
305
|
-
// 병렬로 모든 worker 실행
|
|
306
|
-
const workerPromises = workers.map((worker) => runNextTask(worker));
|
|
307
|
-
|
|
308
|
-
// listr와 worker 동시 실행
|
|
309
|
-
await Promise.all([listr.run(), ...workerPromises]);
|
|
279
|
+
logger.start(`타입체크 실행 중... (${tasks.length}개 대상, 동시성: ${concurrency})`);
|
|
280
|
+
await Promise.all(workers.map((worker) => runNextTask(worker)));
|
|
281
|
+
logger.success("타입체크 실행 완료");
|
|
310
282
|
} finally {
|
|
311
|
-
// 미해결 resolver 정리 (타임아웃/비정상 종료 대비)
|
|
312
|
-
for (const resolver of taskResolvers.values()) {
|
|
313
|
-
resolver();
|
|
314
|
-
}
|
|
315
|
-
// Worker 종료 (성공/실패 모두)
|
|
316
283
|
await Promise.all(workers.map((w) => w.terminate()));
|
|
317
284
|
}
|
|
318
285
|
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface BuildResult {
|
|
|
5
5
|
name: string;
|
|
6
6
|
target: string;
|
|
7
7
|
type: "build" | "dts" | "server" | "capacitor";
|
|
8
|
-
status: "pending" | "building" | "success" | "error" | "
|
|
8
|
+
status: "pending" | "building" | "success" | "error" | "running";
|
|
9
9
|
message?: string;
|
|
10
10
|
port?: number;
|
|
11
11
|
}
|
|
@@ -54,7 +54,7 @@ export class ResultCollector {
|
|
|
54
54
|
* 서버 상태인 결과만 조회
|
|
55
55
|
*/
|
|
56
56
|
getServers(): BuildResult[] {
|
|
57
|
-
return this.getAll().filter((r) => r.type === "server" && r.status === "
|
|
57
|
+
return this.getAll().filter((r) => r.type === "server" && r.status === "running");
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import { Worker, type WorkerProxy, fsRm } from "@simplysm/core-node";
|
|
4
|
+
import "@simplysm/core-common";
|
|
5
|
+
import { consola } from "consola";
|
|
6
|
+
import type { SdConfig, SdBuildPackageConfig, SdClientPackageConfig, SdServerPackageConfig } from "../sd-config.types";
|
|
7
|
+
import { loadSdConfig } from "../utils/sd-config";
|
|
8
|
+
import { getVersion } from "../utils/build-env";
|
|
9
|
+
import { setupReplaceDeps } from "../utils/replace-deps";
|
|
10
|
+
import type { TypecheckEnv } from "../utils/tsconfig";
|
|
11
|
+
import { deserializeDiagnostic } from "../utils/typecheck-serialization";
|
|
12
|
+
import { runLint, type LintOptions } from "../commands/lint";
|
|
13
|
+
import type * as LibraryWorkerModule from "../workers/library.worker";
|
|
14
|
+
import type * as ServerWorkerModule from "../workers/server.worker";
|
|
15
|
+
import type * as ClientWorkerModule from "../workers/client.worker";
|
|
16
|
+
import type * as DtsWorkerModule from "../workers/dts.worker";
|
|
17
|
+
import { Capacitor } from "../capacitor/capacitor";
|
|
18
|
+
import { Electron } from "../electron/electron";
|
|
19
|
+
import { copySrcFiles } from "../utils/copy-src";
|
|
20
|
+
|
|
21
|
+
//#region Types
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build Orchestrator 옵션
|
|
25
|
+
*/
|
|
26
|
+
export interface BuildOrchestratorOptions {
|
|
27
|
+
/** 빌드할 패키지 필터 (빈 배열이면 모든 패키지) */
|
|
28
|
+
targets: string[];
|
|
29
|
+
/** sd.config.ts에 전달할 추가 옵션 */
|
|
30
|
+
options: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 빌드 결과
|
|
35
|
+
*/
|
|
36
|
+
interface BuildResult {
|
|
37
|
+
name: string;
|
|
38
|
+
target: string;
|
|
39
|
+
type: "js" | "dts" | "vite" | "capacitor" | "electron";
|
|
40
|
+
success: boolean;
|
|
41
|
+
errors?: string[];
|
|
42
|
+
diagnostics?: ts.Diagnostic[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 패키지 분류 결과
|
|
47
|
+
*/
|
|
48
|
+
interface ClassifiedPackages {
|
|
49
|
+
/** node/browser/neutral 타겟 (JS + dts) */
|
|
50
|
+
buildPackages: Array<{ name: string; config: SdBuildPackageConfig }>;
|
|
51
|
+
/** client 타겟 (Vite build + typecheck) */
|
|
52
|
+
clientPackages: Array<{ name: string; config: SdClientPackageConfig }>;
|
|
53
|
+
/** server 타겟 (JS 빌드, dts 제외) */
|
|
54
|
+
serverPackages: Array<{ name: string; config: SdServerPackageConfig }>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
|
|
59
|
+
//#region Utilities
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 패키지를 타겟별로 분류
|
|
63
|
+
* - node/browser/neutral: buildPackages (JS + dts)
|
|
64
|
+
* - client: clientPackages (Vite build + typecheck)
|
|
65
|
+
* - server: serverPackages (JS 빌드, dts 제외)
|
|
66
|
+
* - scripts: 제외
|
|
67
|
+
*/
|
|
68
|
+
function classifyPackages(
|
|
69
|
+
packages: Record<
|
|
70
|
+
string,
|
|
71
|
+
SdBuildPackageConfig | SdClientPackageConfig | SdServerPackageConfig | { target: "scripts" } | undefined
|
|
72
|
+
>,
|
|
73
|
+
targets: string[],
|
|
74
|
+
): ClassifiedPackages {
|
|
75
|
+
const buildPackages: ClassifiedPackages["buildPackages"] = [];
|
|
76
|
+
const clientPackages: ClassifiedPackages["clientPackages"] = [];
|
|
77
|
+
const serverPackages: ClassifiedPackages["serverPackages"] = [];
|
|
78
|
+
|
|
79
|
+
for (const [name, config] of Object.entries(packages)) {
|
|
80
|
+
if (config == null) continue;
|
|
81
|
+
if (config.target === "scripts") continue;
|
|
82
|
+
|
|
83
|
+
// targets가 지정되면 해당 패키지만 포함
|
|
84
|
+
if (targets.length > 0 && !targets.includes(name)) continue;
|
|
85
|
+
|
|
86
|
+
if (config.target === "client") {
|
|
87
|
+
clientPackages.push({ name, config });
|
|
88
|
+
} else if (config.target === "server") {
|
|
89
|
+
serverPackages.push({ name, config });
|
|
90
|
+
} else {
|
|
91
|
+
buildPackages.push({ name, config });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { buildPackages, clientPackages, serverPackages };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* dist 폴더 삭제
|
|
100
|
+
*/
|
|
101
|
+
async function cleanDistFolders(cwd: string, packageNames: string[]): Promise<void> {
|
|
102
|
+
await Promise.all(packageNames.map((name) => fsRm(path.join(cwd, "packages", name, "dist"))));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
//#endregion
|
|
106
|
+
|
|
107
|
+
//#region BuildOrchestrator
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 프로덕션 빌드를 조율하는 Orchestrator
|
|
111
|
+
*
|
|
112
|
+
* sd.config.ts 기반으로 패키지를 분류하고, 빌드를 실행한다.
|
|
113
|
+
* - lint 실행
|
|
114
|
+
* - dist 폴더 정리 (clean build)
|
|
115
|
+
* - node/browser/neutral 타겟: esbuild JS 빌드 + dts 생성
|
|
116
|
+
* - client 타겟: Vite production 빌드 + typecheck + Capacitor/Electron 빌드
|
|
117
|
+
* - server 타겟: esbuild JS 빌드
|
|
118
|
+
*/
|
|
119
|
+
export class BuildOrchestrator {
|
|
120
|
+
private readonly _cwd: string;
|
|
121
|
+
private readonly _options: BuildOrchestratorOptions;
|
|
122
|
+
private readonly _logger = consola.withTag("sd:cli:build");
|
|
123
|
+
|
|
124
|
+
private _sdConfig: SdConfig | undefined;
|
|
125
|
+
private _classified: ClassifiedPackages | undefined;
|
|
126
|
+
private _allPackageNames: string[] = [];
|
|
127
|
+
private _baseEnv: { VER: string; DEV: string } | undefined;
|
|
128
|
+
|
|
129
|
+
constructor(options: BuildOrchestratorOptions) {
|
|
130
|
+
this._cwd = process.cwd();
|
|
131
|
+
this._options = options;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Orchestrator 초기화
|
|
136
|
+
* - sd.config.ts 로드
|
|
137
|
+
* - replaceDeps 설정
|
|
138
|
+
* - 패키지 분류
|
|
139
|
+
* - 환경변수 준비
|
|
140
|
+
*/
|
|
141
|
+
async initialize(): Promise<void> {
|
|
142
|
+
this._logger.debug("빌드 시작", { targets: this._options.targets });
|
|
143
|
+
|
|
144
|
+
// sd.config.ts 로드
|
|
145
|
+
try {
|
|
146
|
+
this._sdConfig = await loadSdConfig({
|
|
147
|
+
cwd: this._cwd,
|
|
148
|
+
dev: false,
|
|
149
|
+
opt: this._options.options,
|
|
150
|
+
});
|
|
151
|
+
this._logger.debug("sd.config.ts 로드 완료");
|
|
152
|
+
} catch (err) {
|
|
153
|
+
this._logger.error(`sd.config.ts 로드 실패: ${err instanceof Error ? err.message : err}`);
|
|
154
|
+
process.exitCode = 1;
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// replaceDeps 설정이 있으면 symlink 교체
|
|
159
|
+
if (this._sdConfig.replaceDeps != null) {
|
|
160
|
+
await setupReplaceDeps(this._cwd, this._sdConfig.replaceDeps);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// VER, DEV 환경변수 준비
|
|
164
|
+
const version = await getVersion(this._cwd);
|
|
165
|
+
this._baseEnv = { VER: version, DEV: "false" };
|
|
166
|
+
|
|
167
|
+
// 패키지 분류
|
|
168
|
+
this._classified = classifyPackages(this._sdConfig.packages, this._options.targets);
|
|
169
|
+
this._allPackageNames = [
|
|
170
|
+
...this._classified.buildPackages.map((p) => p.name),
|
|
171
|
+
...this._classified.clientPackages.map((p) => p.name),
|
|
172
|
+
...this._classified.serverPackages.map((p) => p.name),
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
if (this._allPackageNames.length === 0) {
|
|
176
|
+
process.stdout.write("✔ 빌드할 패키지가 없습니다.\n");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
this._logger.debug("패키지 분류 완료", {
|
|
181
|
+
buildPackages: this._classified.buildPackages.map((p) => p.name),
|
|
182
|
+
clientPackages: this._classified.clientPackages.map((p) => p.name),
|
|
183
|
+
serverPackages: this._classified.serverPackages.map((p) => p.name),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 빌드 실행
|
|
189
|
+
* - Lint
|
|
190
|
+
* - Clean
|
|
191
|
+
* - Build (concurrent)
|
|
192
|
+
* - 결과 출력
|
|
193
|
+
*
|
|
194
|
+
* @returns 에러 여부 (true: 에러 있음)
|
|
195
|
+
*/
|
|
196
|
+
async start(): Promise<boolean> {
|
|
197
|
+
if (this._allPackageNames.length === 0) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const classified = this._classified!;
|
|
202
|
+
const baseEnv = this._baseEnv!;
|
|
203
|
+
|
|
204
|
+
// 결과 수집
|
|
205
|
+
const results: BuildResult[] = [];
|
|
206
|
+
// 에러 추적 (객체로 래핑하여 콜백 내 수정 추적 가능하게 함)
|
|
207
|
+
const state = { hasError: false };
|
|
208
|
+
|
|
209
|
+
// Worker 경로
|
|
210
|
+
const libraryWorkerPath = import.meta.resolve("../workers/library.worker");
|
|
211
|
+
const serverWorkerPath = import.meta.resolve("../workers/server.worker");
|
|
212
|
+
const clientWorkerPath = import.meta.resolve("../workers/client.worker");
|
|
213
|
+
const dtsWorkerPath = import.meta.resolve("../workers/dts.worker");
|
|
214
|
+
|
|
215
|
+
// 파일 캐시 (diagnostics 출력용)
|
|
216
|
+
const fileCache = new Map<string, string>();
|
|
217
|
+
|
|
218
|
+
// formatHost (diagnostics 출력용)
|
|
219
|
+
const formatHost: ts.FormatDiagnosticsHost = {
|
|
220
|
+
getCanonicalFileName: (f) => f,
|
|
221
|
+
getCurrentDirectory: () => this._cwd,
|
|
222
|
+
getNewLine: () => ts.sys.newLine,
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Lint 옵션 (전체 패키지 대상)
|
|
226
|
+
const lintOptions: LintOptions = {
|
|
227
|
+
targets: this._allPackageNames.map((name) => `packages/${name}`),
|
|
228
|
+
fix: false,
|
|
229
|
+
timing: false,
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Phase 1: Lint
|
|
233
|
+
this._logger.start("Lint");
|
|
234
|
+
await runLint(lintOptions);
|
|
235
|
+
// lint 에러가 있으면 process.exitCode가 1로 설정됨
|
|
236
|
+
if (process.exitCode === 1) {
|
|
237
|
+
state.hasError = true;
|
|
238
|
+
}
|
|
239
|
+
this._logger.success("Lint");
|
|
240
|
+
|
|
241
|
+
// Phase 2: Clean
|
|
242
|
+
this._logger.start("Clean");
|
|
243
|
+
await cleanDistFolders(this._cwd, this._allPackageNames);
|
|
244
|
+
this._logger.success("Clean");
|
|
245
|
+
|
|
246
|
+
// Phase 3: Build (concurrent)
|
|
247
|
+
this._logger.start("Build");
|
|
248
|
+
|
|
249
|
+
// 빌드 작업 목록 생성
|
|
250
|
+
const buildTasks: Array<() => Promise<void>> = [];
|
|
251
|
+
|
|
252
|
+
// buildPackages: JS 빌드 + dts 생성
|
|
253
|
+
for (const { name, config } of classified.buildPackages) {
|
|
254
|
+
const pkgDir = path.join(this._cwd, "packages", name);
|
|
255
|
+
const env: TypecheckEnv = config.target === "node" ? "node" : "browser";
|
|
256
|
+
|
|
257
|
+
buildTasks.push(async () => {
|
|
258
|
+
this._logger.debug(`${name} (${config.target}) 시작`);
|
|
259
|
+
// JS 빌드와 DTS 생성을 병렬 실행
|
|
260
|
+
const libraryWorker: WorkerProxy<typeof LibraryWorkerModule> =
|
|
261
|
+
Worker.create<typeof LibraryWorkerModule>(libraryWorkerPath);
|
|
262
|
+
const dtsWorker: WorkerProxy<typeof DtsWorkerModule> = Worker.create<typeof DtsWorkerModule>(dtsWorkerPath);
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
const [buildResult, dtsResult] = await Promise.all([
|
|
266
|
+
// JS 빌드
|
|
267
|
+
libraryWorker.build({ name, config, cwd: this._cwd, pkgDir }),
|
|
268
|
+
// DTS 생성
|
|
269
|
+
dtsWorker.buildDts({ name, cwd: this._cwd, pkgDir, env, emit: true }),
|
|
270
|
+
]);
|
|
271
|
+
|
|
272
|
+
// JS 빌드 결과 처리
|
|
273
|
+
results.push({
|
|
274
|
+
name,
|
|
275
|
+
target: config.target,
|
|
276
|
+
type: "js",
|
|
277
|
+
success: buildResult.success,
|
|
278
|
+
errors: buildResult.errors,
|
|
279
|
+
});
|
|
280
|
+
if (!buildResult.success) state.hasError = true;
|
|
281
|
+
|
|
282
|
+
// DTS 결과 처리
|
|
283
|
+
const diagnostics = dtsResult.diagnostics.map((d) => deserializeDiagnostic(d, fileCache));
|
|
284
|
+
results.push({
|
|
285
|
+
name,
|
|
286
|
+
target: config.target,
|
|
287
|
+
type: "dts",
|
|
288
|
+
success: dtsResult.success,
|
|
289
|
+
errors: dtsResult.errors,
|
|
290
|
+
diagnostics,
|
|
291
|
+
});
|
|
292
|
+
if (!dtsResult.success) state.hasError = true;
|
|
293
|
+
} finally {
|
|
294
|
+
await Promise.all([libraryWorker.terminate(), dtsWorker.terminate()]);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// copySrc 파일 복사
|
|
298
|
+
if (config.copySrc != null && config.copySrc.length > 0) {
|
|
299
|
+
await copySrcFiles(pkgDir, config.copySrc);
|
|
300
|
+
}
|
|
301
|
+
this._logger.debug(`${name} (${config.target}) 완료`);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// clientPackages: Vite 빌드 + typecheck + Capacitor 빌드
|
|
306
|
+
for (const { name, config } of classified.clientPackages) {
|
|
307
|
+
const pkgDir = path.join(this._cwd, "packages", name);
|
|
308
|
+
|
|
309
|
+
buildTasks.push(async () => {
|
|
310
|
+
this._logger.debug(`${name} (client) 시작`);
|
|
311
|
+
// Vite 빌드와 타입체크를 병렬 실행
|
|
312
|
+
const clientWorker: WorkerProxy<typeof ClientWorkerModule> =
|
|
313
|
+
Worker.create<typeof ClientWorkerModule>(clientWorkerPath);
|
|
314
|
+
const dtsWorker: WorkerProxy<typeof DtsWorkerModule> = Worker.create<typeof DtsWorkerModule>(dtsWorkerPath);
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
const clientConfig: SdClientPackageConfig = {
|
|
318
|
+
...config,
|
|
319
|
+
env: { ...baseEnv, ...config.env },
|
|
320
|
+
};
|
|
321
|
+
const [clientResult, dtsResult] = await Promise.all([
|
|
322
|
+
// Vite production 빌드
|
|
323
|
+
clientWorker.build({ name, config: clientConfig, cwd: this._cwd, pkgDir }),
|
|
324
|
+
// typecheck (dts 없이)
|
|
325
|
+
dtsWorker.buildDts({
|
|
326
|
+
name,
|
|
327
|
+
cwd: this._cwd,
|
|
328
|
+
pkgDir,
|
|
329
|
+
env: "browser",
|
|
330
|
+
emit: false,
|
|
331
|
+
}),
|
|
332
|
+
]);
|
|
333
|
+
|
|
334
|
+
// Vite 빌드 결과 처리
|
|
335
|
+
results.push({
|
|
336
|
+
name,
|
|
337
|
+
target: "client",
|
|
338
|
+
type: "vite",
|
|
339
|
+
success: clientResult.success,
|
|
340
|
+
errors: clientResult.errors,
|
|
341
|
+
});
|
|
342
|
+
if (!clientResult.success) state.hasError = true;
|
|
343
|
+
|
|
344
|
+
// 타입체크 결과 처리
|
|
345
|
+
const diagnostics = dtsResult.diagnostics.map((d) => deserializeDiagnostic(d, fileCache));
|
|
346
|
+
results.push({
|
|
347
|
+
name,
|
|
348
|
+
target: "client",
|
|
349
|
+
type: "dts",
|
|
350
|
+
success: dtsResult.success,
|
|
351
|
+
errors: dtsResult.errors,
|
|
352
|
+
diagnostics,
|
|
353
|
+
});
|
|
354
|
+
if (!dtsResult.success) state.hasError = true;
|
|
355
|
+
} finally {
|
|
356
|
+
await Promise.all([clientWorker.terminate(), dtsWorker.terminate()]);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Capacitor 빌드 (설정이 있는 경우만)
|
|
360
|
+
if (config.capacitor != null) {
|
|
361
|
+
const outPath = path.join(pkgDir, "dist");
|
|
362
|
+
try {
|
|
363
|
+
const cap = await Capacitor.create(pkgDir, config.capacitor);
|
|
364
|
+
await cap.initialize();
|
|
365
|
+
await cap.build(outPath);
|
|
366
|
+
results.push({
|
|
367
|
+
name,
|
|
368
|
+
target: "client",
|
|
369
|
+
type: "capacitor",
|
|
370
|
+
success: true,
|
|
371
|
+
});
|
|
372
|
+
} catch (err) {
|
|
373
|
+
results.push({
|
|
374
|
+
name,
|
|
375
|
+
target: "client",
|
|
376
|
+
type: "capacitor",
|
|
377
|
+
success: false,
|
|
378
|
+
errors: [err instanceof Error ? err.message : String(err)],
|
|
379
|
+
});
|
|
380
|
+
state.hasError = true;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Electron 빌드 (설정이 있는 경우만)
|
|
385
|
+
if (config.electron != null) {
|
|
386
|
+
const outPath = path.join(pkgDir, "dist");
|
|
387
|
+
try {
|
|
388
|
+
const electron = await Electron.create(pkgDir, config.electron);
|
|
389
|
+
await electron.initialize();
|
|
390
|
+
await electron.build(outPath);
|
|
391
|
+
results.push({
|
|
392
|
+
name,
|
|
393
|
+
target: "client",
|
|
394
|
+
type: "electron",
|
|
395
|
+
success: true,
|
|
396
|
+
});
|
|
397
|
+
} catch (err) {
|
|
398
|
+
results.push({
|
|
399
|
+
name,
|
|
400
|
+
target: "client",
|
|
401
|
+
type: "electron",
|
|
402
|
+
success: false,
|
|
403
|
+
errors: [err instanceof Error ? err.message : String(err)],
|
|
404
|
+
});
|
|
405
|
+
state.hasError = true;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
this._logger.debug(`${name} (client) 완료`);
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// serverPackages: JS 빌드만 (dts 생성 제외)
|
|
413
|
+
for (const { name, config } of classified.serverPackages) {
|
|
414
|
+
const pkgDir = path.join(this._cwd, "packages", name);
|
|
415
|
+
|
|
416
|
+
buildTasks.push(async () => {
|
|
417
|
+
this._logger.debug(`${name} (server) 시작`);
|
|
418
|
+
const serverWorker: WorkerProxy<typeof ServerWorkerModule> =
|
|
419
|
+
Worker.create<typeof ServerWorkerModule>(serverWorkerPath);
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
const buildResult = await serverWorker.build({
|
|
423
|
+
name,
|
|
424
|
+
cwd: this._cwd,
|
|
425
|
+
pkgDir,
|
|
426
|
+
env: { ...baseEnv, ...config.env },
|
|
427
|
+
configs: config.configs,
|
|
428
|
+
externals: config.externals,
|
|
429
|
+
pm2: config.pm2,
|
|
430
|
+
packageManager: config.packageManager,
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
results.push({
|
|
434
|
+
name,
|
|
435
|
+
target: "server",
|
|
436
|
+
type: "js",
|
|
437
|
+
success: buildResult.success,
|
|
438
|
+
errors: buildResult.errors,
|
|
439
|
+
});
|
|
440
|
+
if (!buildResult.success) state.hasError = true;
|
|
441
|
+
} finally {
|
|
442
|
+
await serverWorker.terminate();
|
|
443
|
+
}
|
|
444
|
+
this._logger.debug(`${name} (server) 완료`);
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// 모든 빌드를 병렬 실행
|
|
449
|
+
await Promise.allSettled(buildTasks.map((task) => task()));
|
|
450
|
+
this._logger.success("Build");
|
|
451
|
+
|
|
452
|
+
// 결과 출력
|
|
453
|
+
const allDiagnostics: ts.Diagnostic[] = [];
|
|
454
|
+
for (const result of results) {
|
|
455
|
+
if (!result.success) {
|
|
456
|
+
const typeLabel = result.type === "dts" ? "dts" : result.target;
|
|
457
|
+
const errorLines: string[] = [`${result.name} (${typeLabel})`];
|
|
458
|
+
if (result.errors != null) {
|
|
459
|
+
for (const error of result.errors) {
|
|
460
|
+
for (const line of error.split("\n")) {
|
|
461
|
+
errorLines.push(` → ${line}`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
this._logger.error(errorLines.join("\n"));
|
|
466
|
+
}
|
|
467
|
+
if (result.diagnostics != null) {
|
|
468
|
+
allDiagnostics.push(...result.diagnostics);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// diagnostics 출력 (중복 제거)
|
|
473
|
+
if (allDiagnostics.length > 0) {
|
|
474
|
+
const uniqueDiagnostics = ts.sortAndDeduplicateDiagnostics(allDiagnostics);
|
|
475
|
+
const message = ts.formatDiagnosticsWithColorAndContext(uniqueDiagnostics, formatHost);
|
|
476
|
+
process.stdout.write(message);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// 결과 로그 출력
|
|
480
|
+
if (state.hasError) {
|
|
481
|
+
this._logger.error("빌드 실패");
|
|
482
|
+
} else {
|
|
483
|
+
this._logger.info("빌드 완료");
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return state.hasError;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Orchestrator 종료 (현재는 정리할 리소스 없음)
|
|
491
|
+
*/
|
|
492
|
+
async shutdown(): Promise<void> {
|
|
493
|
+
// 프로덕션 빌드는 일회성이므로 종료 시 정리할 리소스가 없음
|
|
494
|
+
// Worker는 각 빌드 태스크 내에서 terminate()로 정리됨
|
|
495
|
+
await Promise.resolve();
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
//#endregion
|