@simplysm/sd-cli 13.0.11 → 13.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 +42 -2
- 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/esbuild-config.d.ts +8 -0
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/esbuild-config.js +23 -28
- package/dist/utils/esbuild-config.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 +61 -7
- 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 +14 -16
- 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/esbuild-config.ts +31 -33
- 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 +115 -9
- 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 +16 -22
- 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
package/src/utils/vite-config.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { VitePWA } from "vite-plugin-pwa";
|
|
|
8
8
|
import tailwindcss from "tailwindcss";
|
|
9
9
|
import type esbuild from "esbuild";
|
|
10
10
|
import { getTailwindConfigDeps } from "./tailwind-config-deps.js";
|
|
11
|
+
import { FsWatcher } from "@simplysm/core-node";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Tailwind config의 scope 패키지 의존성을 watch하는 Vite 플러그인.
|
|
@@ -60,30 +61,111 @@ function sdTailwindConfigDepsPlugin(pkgDir: string): Plugin {
|
|
|
60
61
|
};
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
/**
|
|
65
|
+
* 패키지가 subpath-only export인지 확인 (exports에 "."이 없는 패키지)
|
|
66
|
+
*
|
|
67
|
+
* 예: @tiptap/pm은 "./state", "./view" 등 subpath만 export하므로 pre-bundling 불가
|
|
68
|
+
* pnpm 구조에서 두 경로를 시도:
|
|
69
|
+
* 1. realpath를 따라 .pnpm node_modules에서 찾기
|
|
70
|
+
* 2. symlink된 workspace 패키지의 node_modules에서 fallback
|
|
71
|
+
*/
|
|
72
|
+
function isSubpathOnlyPackage(pkgJsonPath: string): boolean {
|
|
73
|
+
try {
|
|
74
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8")) as {
|
|
75
|
+
exports?: Record<string, unknown> | string;
|
|
76
|
+
main?: string;
|
|
77
|
+
module?: string;
|
|
78
|
+
};
|
|
79
|
+
if (
|
|
80
|
+
pkgJson.exports != null &&
|
|
81
|
+
typeof pkgJson.exports === "object" &&
|
|
82
|
+
!("." in pkgJson.exports) &&
|
|
83
|
+
pkgJson.main == null &&
|
|
84
|
+
pkgJson.module == null
|
|
85
|
+
) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
// 읽기 실패 시 false 반환 (pre-bundling 포함)
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
63
94
|
/**
|
|
64
95
|
* scope 패키지의 dist 디렉토리 변경을 감지하는 Vite 플러그인.
|
|
65
96
|
*
|
|
66
97
|
* Vite는 node_modules를 기본적으로 watch에서 제외하므로,
|
|
67
98
|
* scope 패키지의 dist 파일이 변경되어도 HMR/리빌드가 트리거되지 않는다.
|
|
68
|
-
* 이 플러그인은 scope 패키지의 dist 디렉토리를
|
|
99
|
+
* 이 플러그인은 별도의 FsWatcher로 scope 패키지의 dist 디렉토리를 감시하고,
|
|
100
|
+
* 변경 시 Vite의 내부 HMR 파이프라인을 트리거한다.
|
|
69
101
|
* optimizeDeps에서 제외하여 pre-bundled 캐시로 인한 변경 무시를 방지한다.
|
|
70
102
|
*/
|
|
71
103
|
function sdScopeWatchPlugin(pkgDir: string, scopes: string[]): Plugin {
|
|
72
104
|
return {
|
|
73
105
|
name: "sd-scope-watch",
|
|
74
106
|
config() {
|
|
107
|
+
const excluded: string[] = [];
|
|
108
|
+
const nestedDepsToInclude: string[] = [];
|
|
109
|
+
|
|
110
|
+
for (const scope of scopes) {
|
|
111
|
+
// scope 패키지를 pre-bundling에서 제외하여 소스 코드로 취급
|
|
112
|
+
const scopeDir = path.join(pkgDir, "node_modules", scope);
|
|
113
|
+
if (!fs.existsSync(scopeDir)) continue;
|
|
114
|
+
|
|
115
|
+
for (const name of fs.readdirSync(scopeDir)) {
|
|
116
|
+
excluded.push(`${scope}/${name}`);
|
|
117
|
+
|
|
118
|
+
// excluded 패키지의 dependencies를 nested include로 추가하여 pre-bundling 보장
|
|
119
|
+
// Vite nested dependency 구문: "excluded-pkg > dep"
|
|
120
|
+
// (pnpm strict 모듈 격리에서 transitive dep을 resolve하기 위해 필요)
|
|
121
|
+
const depPkgJsonPath = path.join(scopeDir, name, "package.json");
|
|
122
|
+
try {
|
|
123
|
+
const depPkgJson = JSON.parse(fs.readFileSync(depPkgJsonPath, "utf-8")) as {
|
|
124
|
+
dependencies?: Record<string, string>;
|
|
125
|
+
};
|
|
126
|
+
const excludedPkg = `${scope}/${name}`;
|
|
127
|
+
for (const dep of Object.keys(depPkgJson.dependencies ?? {})) {
|
|
128
|
+
// 같은 scope 내 패키지는 이미 excluded이므로 제외
|
|
129
|
+
if (scopes.some((s) => dep.startsWith(`${s}/`))) continue;
|
|
130
|
+
// SolidJS 관련 패키지는 solid 플러그인 transform이 필요하므로 pre-bundling 불가
|
|
131
|
+
if (dep === "solid-js" || dep.startsWith("@solidjs/") || dep.startsWith("solid-")) continue;
|
|
132
|
+
// PostCSS/빌드 도구는 브라우저 pre-bundling 대상 아님
|
|
133
|
+
if (dep === "tailwindcss") continue;
|
|
134
|
+
|
|
135
|
+
// subpath-only 패키지 필터링: 두 경로를 시도하여 확인
|
|
136
|
+
// pnpm 구조에서 realpath를 따라 .pnpm node_modules에서 먼저 찾기
|
|
137
|
+
const realPkgPath = fs.realpathSync(path.join(scopeDir, name));
|
|
138
|
+
const pnpmNodeModules = path.resolve(realPkgPath, "../..");
|
|
139
|
+
const depPkgJsonResolved = path.join(pnpmNodeModules, dep, "package.json");
|
|
140
|
+
if (isSubpathOnlyPackage(depPkgJsonResolved)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// workspace 패키지는 realpath가 소스 디렉토리로 해석되어 .pnpm 구조가 아님
|
|
145
|
+
// symlink 경로의 node_modules에서 fallback 시도
|
|
146
|
+
const depPkgJsonFallback = path.join(scopeDir, name, "node_modules", dep, "package.json");
|
|
147
|
+
if (isSubpathOnlyPackage(depPkgJsonFallback)) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
nestedDepsToInclude.push(`${excludedPkg} > ${dep}`);
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
// package.json 읽기 실패 시 스킵
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
75
159
|
return {
|
|
76
160
|
optimizeDeps: {
|
|
77
|
-
exclude:
|
|
78
|
-
|
|
79
|
-
const scopeDir = path.join(pkgDir, "node_modules", s);
|
|
80
|
-
if (!fs.existsSync(scopeDir)) return [];
|
|
81
|
-
return fs.readdirSync(scopeDir).map((name) => `${s}/${name}`);
|
|
82
|
-
}),
|
|
161
|
+
exclude: excluded,
|
|
162
|
+
include: [...new Set(nestedDepsToInclude)],
|
|
83
163
|
},
|
|
84
164
|
};
|
|
85
165
|
},
|
|
86
|
-
configureServer(server) {
|
|
166
|
+
async configureServer(server) {
|
|
167
|
+
const distDirs: string[] = [];
|
|
168
|
+
|
|
87
169
|
for (const scope of scopes) {
|
|
88
170
|
const scopeDir = path.join(pkgDir, "node_modules", scope);
|
|
89
171
|
if (!fs.existsSync(scopeDir)) continue;
|
|
@@ -91,10 +173,34 @@ function sdScopeWatchPlugin(pkgDir: string, scopes: string[]): Plugin {
|
|
|
91
173
|
for (const pkgName of fs.readdirSync(scopeDir)) {
|
|
92
174
|
const distDir = path.join(scopeDir, pkgName, "dist");
|
|
93
175
|
if (fs.existsSync(distDir)) {
|
|
94
|
-
|
|
176
|
+
distDirs.push(distDir);
|
|
95
177
|
}
|
|
96
178
|
}
|
|
97
179
|
}
|
|
180
|
+
|
|
181
|
+
if (distDirs.length === 0) return;
|
|
182
|
+
|
|
183
|
+
// Vite의 기본 watcher는 **/node_modules/**를 ignore하고
|
|
184
|
+
// server.watcher.add()로는 이 패턴을 override할 수 없다.
|
|
185
|
+
// 별도의 FsWatcher로 scope 패키지의 dist 디렉토리를 감시한다.
|
|
186
|
+
const scopeWatcher = await FsWatcher.watch(distDirs);
|
|
187
|
+
scopeWatcher.onChange({ delay: 300 }, (changeInfos) => {
|
|
188
|
+
for (const { path: changedPath } of changeInfos) {
|
|
189
|
+
// pnpm symlink → real path 변환 (Vite module graph은 real path 사용)
|
|
190
|
+
let realPath: string;
|
|
191
|
+
try {
|
|
192
|
+
realPath = fs.realpathSync(changedPath);
|
|
193
|
+
} catch {
|
|
194
|
+
continue; // 삭제된 파일
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Vite의 내부 HMR 파이프라인 트리거
|
|
198
|
+
server.watcher.emit("change", realPath);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// 서버 종료 시 watcher 정리
|
|
203
|
+
server.httpServer?.on("close", () => void scopeWatcher.close());
|
|
98
204
|
},
|
|
99
205
|
};
|
|
100
206
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { consola } from "consola";
|
|
1
2
|
import type { PackageResult } from "./package-utils";
|
|
2
3
|
import type { SdPackageConfig } from "../sd-config.types";
|
|
3
|
-
import type {
|
|
4
|
+
import type { RebuildManager } from "./rebuild-manager";
|
|
5
|
+
|
|
6
|
+
const workerEventsLogger = consola.withTag("sd:cli:worker-events");
|
|
4
7
|
|
|
5
8
|
/** Worker 빌드 완료 이벤트 데이터 */
|
|
6
9
|
export interface BuildEventData {
|
|
@@ -28,10 +31,13 @@ export interface ServerBuildEventData {
|
|
|
28
31
|
/**
|
|
29
32
|
* 기본 Worker 정보 타입
|
|
30
33
|
*/
|
|
31
|
-
export interface BaseWorkerInfo {
|
|
34
|
+
export interface BaseWorkerInfo<TEvents extends Record<string, any[]> = Record<string, any[]>> {
|
|
32
35
|
name: string;
|
|
33
36
|
config: SdPackageConfig;
|
|
34
|
-
worker: {
|
|
37
|
+
worker: {
|
|
38
|
+
on<K extends keyof TEvents>(event: K, handler: (data: TEvents[K][0]) => void): void;
|
|
39
|
+
send<K extends keyof TEvents>(event: K, data: TEvents[K][0]): void;
|
|
40
|
+
};
|
|
35
41
|
isInitialBuild: boolean;
|
|
36
42
|
buildResolver: (() => void) | undefined;
|
|
37
43
|
}
|
|
@@ -54,11 +60,11 @@ export interface WorkerEventHandlerOptions {
|
|
|
54
60
|
* @param rebuildManager 리빌드 매니저
|
|
55
61
|
* @returns completeTask 함수 (결과를 저장하고 빌드 완료를 알림)
|
|
56
62
|
*/
|
|
57
|
-
export function registerWorkerEventHandlers<T extends BaseWorkerInfo
|
|
63
|
+
export function registerWorkerEventHandlers<TEvents extends Record<string, any[]>, T extends BaseWorkerInfo<TEvents>>(
|
|
58
64
|
workerInfo: T,
|
|
59
65
|
opts: WorkerEventHandlerOptions,
|
|
60
66
|
results: Map<string, PackageResult>,
|
|
61
|
-
rebuildManager:
|
|
67
|
+
rebuildManager: RebuildManager,
|
|
62
68
|
): (result: PackageResult) => void {
|
|
63
69
|
const completeTask = (result: PackageResult): void => {
|
|
64
70
|
results.set(opts.resultKey, result);
|
|
@@ -77,6 +83,7 @@ export function registerWorkerEventHandlers<T extends BaseWorkerInfo>(
|
|
|
77
83
|
// 빌드 완료
|
|
78
84
|
workerInfo.worker.on("build", (data) => {
|
|
79
85
|
const event = data as BuildEventData;
|
|
86
|
+
workerEventsLogger.debug(`[${workerInfo.name}] build: success=${String(event.success)}`);
|
|
80
87
|
completeTask({
|
|
81
88
|
name: workerInfo.name,
|
|
82
89
|
target: workerInfo.config.target,
|
|
@@ -89,6 +96,7 @@ export function registerWorkerEventHandlers<T extends BaseWorkerInfo>(
|
|
|
89
96
|
// 에러
|
|
90
97
|
workerInfo.worker.on("error", (data) => {
|
|
91
98
|
const event = data as ErrorEventData;
|
|
99
|
+
workerEventsLogger.debug(`[${workerInfo.name}] error: ${event.message}`);
|
|
92
100
|
completeTask({
|
|
93
101
|
name: workerInfo.name,
|
|
94
102
|
target: workerInfo.config.target,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ConsolaInstance } from "consola";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Register cleanup handlers for worker process shutdown signals
|
|
5
|
+
*
|
|
6
|
+
* Registers SIGINT and SIGTERM handlers to gracefully cleanup resources
|
|
7
|
+
* before process exit. Both handlers execute the cleanup function and
|
|
8
|
+
* exit with code 0.
|
|
9
|
+
*
|
|
10
|
+
* @param cleanup - Async cleanup function to execute on shutdown
|
|
11
|
+
* @param logger - Consola logger instance for error logging
|
|
12
|
+
*/
|
|
13
|
+
export function registerCleanupHandlers(cleanup: () => Promise<void>, logger: ConsolaInstance): void {
|
|
14
|
+
const handleSignal = () => {
|
|
15
|
+
cleanup()
|
|
16
|
+
.catch((err) => {
|
|
17
|
+
logger.error("cleanup 실패", err);
|
|
18
|
+
})
|
|
19
|
+
.finally(() => {
|
|
20
|
+
process.exit(0);
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
process.on("SIGTERM", handleSignal);
|
|
25
|
+
process.on("SIGINT", handleSignal);
|
|
26
|
+
}
|
|
@@ -6,6 +6,7 @@ import { consola } from "consola";
|
|
|
6
6
|
import type { SdClientPackageConfig } from "../sd-config.types";
|
|
7
7
|
import { parseRootTsconfig, getCompilerOptionsForPackage } from "../utils/tsconfig";
|
|
8
8
|
import { createViteConfig } from "../utils/vite-config";
|
|
9
|
+
import { registerCleanupHandlers } from "../utils/worker-utils";
|
|
9
10
|
|
|
10
11
|
//#region Types
|
|
11
12
|
|
|
@@ -97,25 +98,7 @@ async function cleanup(): Promise<void> {
|
|
|
97
98
|
// 프로세스 종료 전 리소스 정리 (SIGTERM/SIGINT)
|
|
98
99
|
// 주의: worker.terminate()는 이 핸들러들을 호출하지 않고 즉시 종료됨.
|
|
99
100
|
// 그러나 watch 모드에서 정상 종료는 메인 프로세스의 SIGINT/SIGTERM을 통해 이루어지므로 문제없음.
|
|
100
|
-
|
|
101
|
-
cleanup()
|
|
102
|
-
.catch((err) => {
|
|
103
|
-
logger.error("cleanup 실패", err);
|
|
104
|
-
})
|
|
105
|
-
.finally(() => {
|
|
106
|
-
process.exit(0);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
process.on("SIGINT", () => {
|
|
111
|
-
cleanup()
|
|
112
|
-
.catch((err) => {
|
|
113
|
-
logger.error("cleanup 실패", err);
|
|
114
|
-
})
|
|
115
|
-
.finally(() => {
|
|
116
|
-
process.exit(0);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
101
|
+
registerCleanupHandlers(cleanup, logger);
|
|
119
102
|
|
|
120
103
|
//#endregion
|
|
121
104
|
|
|
@@ -208,6 +191,7 @@ async function startWatch(info: ClientWatchInfo): Promise<void> {
|
|
|
208
191
|
// 실제 할당된 포트 반환 (config.server.port는 설정값이므로 httpServer에서 실제 포트를 가져옴)
|
|
209
192
|
const address = viteServer.httpServer?.address();
|
|
210
193
|
const actualPort = typeof address === "object" && address != null ? address.port : viteServer.config.server.port;
|
|
194
|
+
|
|
211
195
|
sender.send("serverReady", { port: actualPort });
|
|
212
196
|
} catch (err) {
|
|
213
197
|
sender.send("error", {
|
|
@@ -4,7 +4,12 @@ import { createWorker, FsWatcher } from "@simplysm/core-node";
|
|
|
4
4
|
import { consola } from "consola";
|
|
5
5
|
import type { SdBuildPackageConfig } from "../sd-config.types";
|
|
6
6
|
import { parseRootTsconfig, getPackageSourceFiles, getCompilerOptionsForPackage } from "../utils/tsconfig";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
createLibraryEsbuildOptions,
|
|
9
|
+
getTypecheckEnvFromTarget,
|
|
10
|
+
writeChangedOutputFiles,
|
|
11
|
+
} from "../utils/esbuild-config";
|
|
12
|
+
import { registerCleanupHandlers } from "../utils/worker-utils";
|
|
8
13
|
|
|
9
14
|
//#region Types
|
|
10
15
|
|
|
@@ -90,25 +95,7 @@ async function cleanup(): Promise<void> {
|
|
|
90
95
|
// 프로세스 종료 전 리소스 정리 (SIGTERM/SIGINT)
|
|
91
96
|
// 주의: worker.terminate()는 이 핸들러들을 호출하지 않고 즉시 종료됨.
|
|
92
97
|
// 그러나 watch 모드에서 정상 종료는 메인 프로세스의 SIGINT/SIGTERM을 통해 이루어지므로 문제없음.
|
|
93
|
-
|
|
94
|
-
cleanup()
|
|
95
|
-
.catch((err) => {
|
|
96
|
-
logger.error("cleanup 실패", err);
|
|
97
|
-
})
|
|
98
|
-
.finally(() => {
|
|
99
|
-
process.exit(0);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
process.on("SIGINT", () => {
|
|
104
|
-
cleanup()
|
|
105
|
-
.catch((err) => {
|
|
106
|
-
logger.error("cleanup 실패", err);
|
|
107
|
-
})
|
|
108
|
-
.finally(() => {
|
|
109
|
-
process.exit(0);
|
|
110
|
-
});
|
|
111
|
-
});
|
|
98
|
+
registerCleanupHandlers(cleanup, logger);
|
|
112
99
|
|
|
113
100
|
//#endregion
|
|
114
101
|
|
|
@@ -136,7 +123,9 @@ async function build(info: LibraryBuildInfo): Promise<LibraryBuildResult> {
|
|
|
136
123
|
});
|
|
137
124
|
|
|
138
125
|
const result = await esbuild.build(esbuildOptions);
|
|
139
|
-
|
|
126
|
+
if (result.outputFiles) {
|
|
127
|
+
await writeChangedOutputFiles(result.outputFiles);
|
|
128
|
+
}
|
|
140
129
|
const errors = result.errors.map((e) => e.text);
|
|
141
130
|
return {
|
|
142
131
|
success: result.errors.length === 0,
|
|
@@ -193,7 +182,12 @@ async function createAndBuildContext(
|
|
|
193
182
|
sender.send("buildStart", {});
|
|
194
183
|
});
|
|
195
184
|
|
|
196
|
-
pluginBuild.onEnd((result) => {
|
|
185
|
+
pluginBuild.onEnd(async (result) => {
|
|
186
|
+
// Write only changed files to disk
|
|
187
|
+
if (result.outputFiles) {
|
|
188
|
+
await writeChangedOutputFiles(result.outputFiles);
|
|
189
|
+
}
|
|
190
|
+
|
|
197
191
|
const errors = result.errors.map((e) => e.text);
|
|
198
192
|
const success = result.errors.length === 0;
|
|
199
193
|
|
|
@@ -2,6 +2,7 @@ import proxy from "@fastify/http-proxy";
|
|
|
2
2
|
import { createWorker } from "@simplysm/core-node";
|
|
3
3
|
import { consola } from "consola";
|
|
4
4
|
import net from "net";
|
|
5
|
+
import { registerCleanupHandlers } from "../utils/worker-utils";
|
|
5
6
|
|
|
6
7
|
//#region Types
|
|
7
8
|
|
|
@@ -53,26 +54,24 @@ async function cleanup(): Promise<void> {
|
|
|
53
54
|
serverInstance = undefined;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
});
|
|
57
|
+
// 서버 listen() 이후 발생하는 런타임 에러를 잡아서 custom "error" 이벤트로 전송
|
|
58
|
+
// (이 핸들러가 없으면 worker가 crash만 하고, dev.ts의 buildResolver가 호출되지 않아 listr가 멈춤)
|
|
59
|
+
process.on("uncaughtException", (err) => {
|
|
60
|
+
logger.error("Server Runtime 미처리 오류", err);
|
|
61
|
+
sender.send("error", {
|
|
62
|
+
message: err instanceof Error ? err.message : String(err),
|
|
63
|
+
});
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
process.on("
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.finally(() => {
|
|
72
|
-
process.exit(0);
|
|
73
|
-
});
|
|
66
|
+
process.on("unhandledRejection", (reason) => {
|
|
67
|
+
logger.error("Server Runtime 미처리 Promise 거부", reason);
|
|
68
|
+
sender.send("error", {
|
|
69
|
+
message: reason instanceof Error ? reason.message : String(reason),
|
|
70
|
+
});
|
|
74
71
|
});
|
|
75
72
|
|
|
73
|
+
registerCleanupHandlers(cleanup, logger);
|
|
74
|
+
|
|
76
75
|
/**
|
|
77
76
|
* 포트가 사용 가능한지 확인
|
|
78
77
|
*/
|
|
@@ -97,7 +96,7 @@ async function findAvailablePort(startPort: number, maxRetries = 20): Promise<nu
|
|
|
97
96
|
return port;
|
|
98
97
|
}
|
|
99
98
|
}
|
|
100
|
-
|
|
99
|
+
throw new Error(`포트 ${startPort}부터 ${startPort + maxRetries - 1}까지 사용 가능한 포트가 없습니다.`);
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
/**
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
collectUninstalledOptionalPeerDeps,
|
|
11
11
|
collectNativeModuleExternals,
|
|
12
12
|
} from "../utils/esbuild-config";
|
|
13
|
+
import { registerCleanupHandlers } from "../utils/worker-utils";
|
|
13
14
|
|
|
14
15
|
//#region Types
|
|
15
16
|
|
|
@@ -251,25 +252,7 @@ function generateProductionFiles(info: ServerBuildInfo, externals: string[]): vo
|
|
|
251
252
|
// 프로세스 종료 전 리소스 정리 (SIGTERM/SIGINT)
|
|
252
253
|
// 주의: worker.terminate()는 이 핸들러들을 호출하지 않고 즉시 종료됨.
|
|
253
254
|
// 그러나 watch 모드에서 정상 종료는 메인 프로세스의 SIGINT/SIGTERM을 통해 이루어지므로 문제없음.
|
|
254
|
-
|
|
255
|
-
cleanup()
|
|
256
|
-
.catch((err) => {
|
|
257
|
-
logger.error("cleanup 실패", err);
|
|
258
|
-
})
|
|
259
|
-
.finally(() => {
|
|
260
|
-
process.exit(0);
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
process.on("SIGINT", () => {
|
|
265
|
-
cleanup()
|
|
266
|
-
.catch((err) => {
|
|
267
|
-
logger.error("cleanup 실패", err);
|
|
268
|
-
})
|
|
269
|
-
.finally(() => {
|
|
270
|
-
process.exit(0);
|
|
271
|
-
});
|
|
272
|
-
});
|
|
255
|
+
registerCleanupHandlers(cleanup, logger);
|
|
273
256
|
|
|
274
257
|
//#endregion
|
|
275
258
|
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"vitest": "vitest"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@simplysm/sd-cli": "~13.0.
|
|
19
|
-
"@simplysm/claude": "~13.0.
|
|
20
|
-
"@simplysm/lint": "~13.0.
|
|
18
|
+
"@simplysm/sd-cli": "~13.0.13",
|
|
19
|
+
"@simplysm/claude": "~13.0.13",
|
|
20
|
+
"@simplysm/lint": "~13.0.13",
|
|
21
21
|
"@types/node": "^20.19.33",
|
|
22
22
|
"eslint": "^9.39.2",
|
|
23
23
|
"prettier": "^3.8.1",
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "node:events";
|
|
2
|
-
import type { consola } from "consola";
|
|
3
|
-
/**
|
|
4
|
-
* RebuildListrManager 이벤트 타입
|
|
5
|
-
*/
|
|
6
|
-
interface RebuildListrManagerEvents {
|
|
7
|
-
batchComplete: [];
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* 리빌드 시 Listr 실행을 관리하는 클래스
|
|
11
|
-
*
|
|
12
|
-
* 여러 Worker가 동시에 buildStart를 발생시킬 때, 한 번에 하나의 Listr만 실행되도록 보장합니다.
|
|
13
|
-
* 실행 중에 들어온 빌드 요청은 pending에 모아두었다가 현재 배치가 완료되면 다음 배치로 실행합니다.
|
|
14
|
-
*
|
|
15
|
-
* EventEmitter를 확장하여 배치 완료 시 `batchComplete` 이벤트를 발생시킵니다.
|
|
16
|
-
*/
|
|
17
|
-
export declare class RebuildListrManager extends EventEmitter<RebuildListrManagerEvents> {
|
|
18
|
-
private readonly _logger;
|
|
19
|
-
private _isRunning;
|
|
20
|
-
private readonly _pendingBuilds;
|
|
21
|
-
constructor(_logger: ReturnType<typeof consola.withTag>);
|
|
22
|
-
/**
|
|
23
|
-
* 빌드를 등록하고 resolver 함수를 반환합니다.
|
|
24
|
-
*
|
|
25
|
-
* @param key - 빌드를 식별하는 고유 키 (예: "core-common:build")
|
|
26
|
-
* @param title - Listr에 표시할 제목 (예: "core-common (node)")
|
|
27
|
-
* @returns 워커가 빌드 완료 시 호출할 resolver 함수
|
|
28
|
-
*/
|
|
29
|
-
registerBuild(key: string, title: string): () => void;
|
|
30
|
-
/**
|
|
31
|
-
* pending에 있는 빌드들을 모아서 하나의 Listr로 실행합니다.
|
|
32
|
-
* 실행 중에 들어온 새 빌드는 다음 배치로 넘어갑니다.
|
|
33
|
-
*/
|
|
34
|
-
private _runBatch;
|
|
35
|
-
}
|
|
36
|
-
export {};
|
|
37
|
-
//# sourceMappingURL=listr-manager.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"listr-manager.d.ts","sourceRoot":"","sources":["../../src/utils/listr-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC;;GAEG;AACH,UAAU,yBAAyB;IACjC,aAAa,EAAE,EAAE,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,qBAAa,mBAAoB,SAAQ,YAAY,CAAC,yBAAyB,CAAC;IAIlE,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAsF;gBAExF,OAAO,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,OAAO,CAAC;IAIxE;;;;;;OAMG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,IAAI;IAgBrD;;;OAGG;YACW,SAAS;CAkCxB"}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "node:events";
|
|
2
|
-
import { Listr } from "listr2";
|
|
3
|
-
class RebuildListrManager extends EventEmitter {
|
|
4
|
-
constructor(_logger) {
|
|
5
|
-
super();
|
|
6
|
-
this._logger = _logger;
|
|
7
|
-
}
|
|
8
|
-
_isRunning = false;
|
|
9
|
-
_pendingBuilds = /* @__PURE__ */ new Map();
|
|
10
|
-
/**
|
|
11
|
-
* 빌드를 등록하고 resolver 함수를 반환합니다.
|
|
12
|
-
*
|
|
13
|
-
* @param key - 빌드를 식별하는 고유 키 (예: "core-common:build")
|
|
14
|
-
* @param title - Listr에 표시할 제목 (예: "core-common (node)")
|
|
15
|
-
* @returns 워커가 빌드 완료 시 호출할 resolver 함수
|
|
16
|
-
*/
|
|
17
|
-
registerBuild(key, title) {
|
|
18
|
-
let resolver;
|
|
19
|
-
const promise = new Promise((resolve) => {
|
|
20
|
-
resolver = resolve;
|
|
21
|
-
});
|
|
22
|
-
this._pendingBuilds.set(key, { title, promise, resolver });
|
|
23
|
-
if (!this._isRunning) {
|
|
24
|
-
void Promise.resolve().then(() => void this._runBatch());
|
|
25
|
-
}
|
|
26
|
-
return resolver;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* pending에 있는 빌드들을 모아서 하나의 Listr로 실행합니다.
|
|
30
|
-
* 실행 중에 들어온 새 빌드는 다음 배치로 넘어갑니다.
|
|
31
|
-
*/
|
|
32
|
-
async _runBatch() {
|
|
33
|
-
if (this._isRunning || this._pendingBuilds.size === 0) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
this._isRunning = true;
|
|
37
|
-
const batchBuilds = new Map(this._pendingBuilds);
|
|
38
|
-
this._pendingBuilds.clear();
|
|
39
|
-
const tasks = Array.from(batchBuilds.entries()).map(([, { title, promise }]) => ({
|
|
40
|
-
title,
|
|
41
|
-
task: () => promise
|
|
42
|
-
}));
|
|
43
|
-
const listr = new Listr(tasks, { concurrent: true });
|
|
44
|
-
try {
|
|
45
|
-
await listr.run();
|
|
46
|
-
this.emit("batchComplete");
|
|
47
|
-
} catch (err) {
|
|
48
|
-
this._logger.error("listr \uC2E4\uD589 \uC911 \uC624\uB958 \uBC1C\uC0DD", { error: String(err) });
|
|
49
|
-
}
|
|
50
|
-
this._isRunning = false;
|
|
51
|
-
if (this._pendingBuilds.size > 0) {
|
|
52
|
-
void this._runBatch();
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
export {
|
|
57
|
-
RebuildListrManager
|
|
58
|
-
};
|
|
59
|
-
//# sourceMappingURL=listr-manager.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/utils/listr-manager.ts"],
|
|
4
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,aAAa;AAkBf,MAAM,4BAA4B,aAAwC;AAAA,EAI/E,YAA6B,SAA6C;AACxE,UAAM;AADqB;AAAA,EAE7B;AAAA,EALQ,aAAa;AAAA,EACJ,iBAAiB,oBAAI,IAA6E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAanH,cAAc,KAAa,OAA2B;AACpD,QAAI;AACJ,UAAM,UAAU,IAAI,QAAc,CAAC,YAAY;AAC7C,iBAAW;AAAA,IACb,CAAC;AAED,SAAK,eAAe,IAAI,KAAK,EAAE,OAAO,SAAS,SAAS,CAAC;AAGzD,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,QAAQ,QAAQ,EAAE,KAAK,MAAM,KAAK,KAAK,UAAU,CAAC;AAAA,IACzD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,YAA2B;AACvC,QAAI,KAAK,cAAc,KAAK,eAAe,SAAS,GAAG;AACrD;AAAA,IACF;AAEA,SAAK,aAAa;AAGlB,UAAM,cAAc,IAAI,IAAI,KAAK,cAAc;AAC/C,SAAK,eAAe,MAAM;AAG1B,UAAM,QAAQ,MAAM,KAAK,YAAY,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,OAAO,QAAQ,CAAC,OAAO;AAAA,MAC/E;AAAA,MACA,MAAM,MAAM;AAAA,IACd,EAAE;AAEF,UAAM,QAAQ,IAAI,MAAM,OAAO,EAAE,YAAY,KAAK,CAAC;AAEnD,QAAI;AACF,YAAM,MAAM,IAAI;AAEhB,WAAK,KAAK,eAAe;AAAA,IAC3B,SAAS,KAAK;AACZ,WAAK,QAAQ,MAAM,uDAAoB,EAAE,OAAO,OAAO,GAAG,EAAE,CAAC;AAAA,IAC/D;AAEA,SAAK,aAAa;AAGlB,QAAI,KAAK,eAAe,OAAO,GAAG;AAChC,WAAK,KAAK,UAAU;AAAA,IACtB;AAAA,EACF;AACF;",
|
|
5
|
-
"names": []
|
|
6
|
-
}
|