@simplysm/sd-cli 13.0.64 → 13.0.66
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/orchestrators/DevOrchestrator.d.ts +0 -1
- package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevOrchestrator.js +4 -12
- package/dist/orchestrators/DevOrchestrator.js.map +1 -1
- package/dist/utils/esbuild-config.d.ts +1 -1
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/esbuild-config.js +3 -0
- package/dist/utils/esbuild-config.js.map +1 -1
- package/dist/utils/package-utils.d.ts +5 -9
- package/dist/utils/package-utils.d.ts.map +1 -1
- package/dist/utils/package-utils.js +43 -13
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/utils/tailwind-config-deps.d.ts +1 -1
- package/dist/utils/tailwind-config-deps.d.ts.map +1 -1
- package/dist/utils/tailwind-config-deps.js +5 -3
- package/dist/utils/tailwind-config-deps.js.map +1 -1
- package/dist/utils/vite-config.d.ts +3 -3
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +43 -52
- package/dist/utils/vite-config.js.map +1 -1
- package/dist/workers/client.worker.d.ts +2 -2
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +3 -1
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/server.worker.d.ts +2 -2
- package/dist/workers/server.worker.d.ts.map +1 -1
- package/dist/workers/server.worker.js +57 -30
- package/dist/workers/server.worker.js.map +1 -1
- package/package.json +4 -4
- package/src/orchestrators/DevOrchestrator.ts +4 -16
- package/src/utils/esbuild-config.ts +4 -2
- package/src/utils/package-utils.ts +62 -22
- package/src/utils/tailwind-config-deps.ts +6 -3
- package/src/utils/vite-config.ts +64 -92
- package/src/workers/client.worker.ts +7 -3
- package/src/workers/server.worker.ts +78 -38
- 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/src/utils/vite-config.ts
CHANGED
|
@@ -17,14 +17,14 @@ import { FsWatcher, pathNorm } from "@simplysm/core-node";
|
|
|
17
17
|
* preset 등으로 참조하는 scope 패키지의 config 변경을 감지하지 못한다.
|
|
18
18
|
* 이 플러그인이 해당 파일들을 watch하고, 변경 시 Tailwind 캐시를 무효화한다.
|
|
19
19
|
*/
|
|
20
|
-
function sdTailwindConfigDepsPlugin(pkgDir: string,
|
|
20
|
+
function sdTailwindConfigDepsPlugin(pkgDir: string, replaceDeps: string[]): Plugin {
|
|
21
21
|
return {
|
|
22
22
|
name: "sd-tailwind-config-deps",
|
|
23
23
|
configureServer(server) {
|
|
24
24
|
const configPath = path.join(pkgDir, "tailwind.config.ts");
|
|
25
25
|
if (!fs.existsSync(configPath)) return;
|
|
26
26
|
|
|
27
|
-
const allDeps = getTailwindConfigDeps(configPath,
|
|
27
|
+
const allDeps = getTailwindConfigDeps(configPath, replaceDeps);
|
|
28
28
|
const configAbsolute = path.resolve(configPath);
|
|
29
29
|
const externalDeps = allDeps.filter((d) => d !== configAbsolute);
|
|
30
30
|
if (externalDeps.length === 0) return;
|
|
@@ -133,66 +133,51 @@ function sdPublicDevPlugin(pkgDir: string): Plugin {
|
|
|
133
133
|
* 변경 시 Vite의 내부 HMR 파이프라인을 트리거한다.
|
|
134
134
|
* optimizeDeps에서 제외하여 pre-bundled 캐시로 인한 변경 무시를 방지한다.
|
|
135
135
|
*/
|
|
136
|
-
function sdScopeWatchPlugin(
|
|
136
|
+
function sdScopeWatchPlugin(
|
|
137
|
+
pkgDir: string,
|
|
138
|
+
replaceDeps: string[],
|
|
139
|
+
onScopeRebuild?: () => void,
|
|
140
|
+
): Plugin {
|
|
137
141
|
return {
|
|
138
142
|
name: "sd-scope-watch",
|
|
139
143
|
config() {
|
|
140
144
|
const excluded: string[] = [];
|
|
141
145
|
const nestedDepsToInclude: string[] = [];
|
|
142
146
|
|
|
143
|
-
for (const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (isSubpathOnlyPackage(depPkgJsonResolved)) {
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// workspace 패키지는 realpath가 소스 디렉토리로 해석되어 .pnpm 구조가 아님
|
|
179
|
-
// symlink 경로의 node_modules에서 fallback 시도
|
|
180
|
-
const depPkgJsonFallback = path.join(
|
|
181
|
-
scopeDir,
|
|
182
|
-
name,
|
|
183
|
-
"node_modules",
|
|
184
|
-
dep,
|
|
185
|
-
"package.json",
|
|
186
|
-
);
|
|
187
|
-
if (isSubpathOnlyPackage(depPkgJsonFallback)) {
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
nestedDepsToInclude.push(`${excludedPkg} > ${dep}`);
|
|
192
|
-
}
|
|
193
|
-
} catch {
|
|
194
|
-
// package.json 읽기 실패 시 스킵
|
|
147
|
+
for (const pkg of replaceDeps) {
|
|
148
|
+
excluded.push(pkg);
|
|
149
|
+
|
|
150
|
+
const pkgParts = pkg.split("/");
|
|
151
|
+
const depPkgJsonPath = path.join(pkgDir, "node_modules", ...pkgParts, "package.json");
|
|
152
|
+
try {
|
|
153
|
+
const depPkgJson = JSON.parse(fs.readFileSync(depPkgJsonPath, "utf-8")) as {
|
|
154
|
+
dependencies?: Record<string, string>;
|
|
155
|
+
};
|
|
156
|
+
for (const dep of Object.keys(depPkgJson.dependencies ?? {})) {
|
|
157
|
+
if (replaceDeps.includes(dep)) continue;
|
|
158
|
+
if (dep === "solid-js" || dep.startsWith("@solidjs/") || dep.startsWith("solid-"))
|
|
159
|
+
continue;
|
|
160
|
+
if (dep === "tailwindcss") continue;
|
|
161
|
+
|
|
162
|
+
const realPkgPath = fs.realpathSync(path.join(pkgDir, "node_modules", ...pkgParts));
|
|
163
|
+
const pnpmNodeModules = path.resolve(realPkgPath, "../..");
|
|
164
|
+
const depPkgJsonResolved = path.join(pnpmNodeModules, dep, "package.json");
|
|
165
|
+
if (isSubpathOnlyPackage(depPkgJsonResolved)) continue;
|
|
166
|
+
|
|
167
|
+
const depPkgJsonFallback = path.join(
|
|
168
|
+
pkgDir,
|
|
169
|
+
"node_modules",
|
|
170
|
+
...pkgParts,
|
|
171
|
+
"node_modules",
|
|
172
|
+
dep,
|
|
173
|
+
"package.json",
|
|
174
|
+
);
|
|
175
|
+
if (isSubpathOnlyPackage(depPkgJsonFallback)) continue;
|
|
176
|
+
|
|
177
|
+
nestedDepsToInclude.push(`${pkg} > ${dep}`);
|
|
195
178
|
}
|
|
179
|
+
} catch {
|
|
180
|
+
// package.json 읽기 실패 시 스킵
|
|
196
181
|
}
|
|
197
182
|
}
|
|
198
183
|
|
|
@@ -207,56 +192,43 @@ function sdScopeWatchPlugin(pkgDir: string, scopes: string[], onScopeRebuild?: (
|
|
|
207
192
|
async configureServer(server) {
|
|
208
193
|
const watchPaths: string[] = [];
|
|
209
194
|
|
|
210
|
-
for (const
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
for (const pkgName of fs.readdirSync(scopeDir)) {
|
|
215
|
-
const pkgRoot = path.join(scopeDir, pkgName);
|
|
195
|
+
for (const pkg of replaceDeps) {
|
|
196
|
+
const pkgParts = pkg.split("/");
|
|
197
|
+
const pkgRoot = path.join(pkgDir, "node_modules", ...pkgParts);
|
|
198
|
+
if (!fs.existsSync(pkgRoot)) continue;
|
|
216
199
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
200
|
+
const distDir = path.join(pkgRoot, "dist");
|
|
201
|
+
if (fs.existsSync(distDir)) {
|
|
202
|
+
watchPaths.push(distDir);
|
|
203
|
+
}
|
|
222
204
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
)
|
|
230
|
-
watchPaths.push(path.join(pkgRoot, file));
|
|
231
|
-
}
|
|
205
|
+
for (const file of fs.readdirSync(pkgRoot)) {
|
|
206
|
+
if (
|
|
207
|
+
file.endsWith(".css") ||
|
|
208
|
+
file === "tailwind.config.ts" ||
|
|
209
|
+
file === "tailwind.config.js"
|
|
210
|
+
) {
|
|
211
|
+
watchPaths.push(path.join(pkgRoot, file));
|
|
232
212
|
}
|
|
233
213
|
}
|
|
234
214
|
}
|
|
235
215
|
|
|
236
216
|
if (watchPaths.length === 0) return;
|
|
237
217
|
|
|
238
|
-
// Vite의 기본 watcher는 **/node_modules/**를 ignore하고
|
|
239
|
-
// server.watcher.add()로는 이 패턴을 override할 수 없다.
|
|
240
|
-
// 별도의 FsWatcher로 scope 패키지의 dist 디렉토리를 감시한다.
|
|
241
218
|
const scopeWatcher = await FsWatcher.watch(watchPaths);
|
|
242
219
|
scopeWatcher.onChange({ delay: 300 }, (changeInfos) => {
|
|
243
220
|
for (const { path: changedPath } of changeInfos) {
|
|
244
|
-
// pnpm symlink → real path 변환 (Vite module graph은 real path 사용)
|
|
245
221
|
let realPath: string;
|
|
246
222
|
try {
|
|
247
223
|
realPath = fs.realpathSync(changedPath);
|
|
248
224
|
} catch {
|
|
249
|
-
continue;
|
|
225
|
+
continue;
|
|
250
226
|
}
|
|
251
|
-
|
|
252
|
-
// Vite의 내부 HMR 파이프라인 트리거
|
|
253
227
|
server.watcher.emit("change", realPath);
|
|
254
228
|
}
|
|
255
|
-
|
|
256
229
|
onScopeRebuild?.();
|
|
257
230
|
});
|
|
258
231
|
|
|
259
|
-
// 서버 종료 시 watcher 정리
|
|
260
232
|
server.httpServer?.on("close", () => void scopeWatcher.close());
|
|
261
233
|
},
|
|
262
234
|
};
|
|
@@ -274,9 +246,9 @@ export interface ViteConfigOptions {
|
|
|
274
246
|
mode: "build" | "dev";
|
|
275
247
|
/** dev 모드일 때 서버 포트 (0이면 자동 할당) */
|
|
276
248
|
serverPort?: number;
|
|
277
|
-
/**
|
|
278
|
-
|
|
279
|
-
/**
|
|
249
|
+
/** replaceDeps 패키지명 배열 (resolve 완료된 상태) */
|
|
250
|
+
replaceDeps?: string[];
|
|
251
|
+
/** replaceDeps 패키지 dist 변경 감지 시 콜백 */
|
|
280
252
|
onScopeRebuild?: () => void;
|
|
281
253
|
}
|
|
282
254
|
|
|
@@ -288,7 +260,7 @@ export interface ViteConfigOptions {
|
|
|
288
260
|
* - dev 모드: dev server (define으로 env 치환, server 설정)
|
|
289
261
|
*/
|
|
290
262
|
export function createViteConfig(options: ViteConfigOptions): ViteUserConfig {
|
|
291
|
-
const { pkgDir, name, tsconfigPath, compilerOptions, env, mode, serverPort,
|
|
263
|
+
const { pkgDir, name, tsconfigPath, compilerOptions, env, mode, serverPort, replaceDeps } =
|
|
292
264
|
options;
|
|
293
265
|
|
|
294
266
|
// Read package.json to extract app name for PWA manifest
|
|
@@ -322,11 +294,11 @@ export function createViteConfig(options: ViteConfigOptions): ViteUserConfig {
|
|
|
322
294
|
globPatterns: ["**/*.{js,css,html,ico,png,svg,woff2}"],
|
|
323
295
|
},
|
|
324
296
|
}),
|
|
325
|
-
...(
|
|
326
|
-
? [sdTailwindConfigDepsPlugin(pkgDir,
|
|
297
|
+
...(replaceDeps != null && replaceDeps.length > 0
|
|
298
|
+
? [sdTailwindConfigDepsPlugin(pkgDir, replaceDeps)]
|
|
327
299
|
: []),
|
|
328
|
-
...(
|
|
329
|
-
? [sdScopeWatchPlugin(pkgDir,
|
|
300
|
+
...(replaceDeps != null && replaceDeps.length > 0
|
|
301
|
+
? [sdScopeWatchPlugin(pkgDir, replaceDeps, options.onScopeRebuild)]
|
|
330
302
|
: []),
|
|
331
303
|
...(mode === "dev" ? [sdPublicDevPlugin(pkgDir)] : []),
|
|
332
304
|
],
|
|
@@ -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 { collectDeps } from "../utils/package-utils";
|
|
9
10
|
import { registerCleanupHandlers } from "../utils/worker-utils";
|
|
10
11
|
|
|
11
12
|
//#region Types
|
|
@@ -36,8 +37,8 @@ export interface ClientWatchInfo {
|
|
|
36
37
|
config: SdClientPackageConfig;
|
|
37
38
|
cwd: string;
|
|
38
39
|
pkgDir: string;
|
|
39
|
-
/**
|
|
40
|
-
|
|
40
|
+
/** sd.config.ts의 replaceDeps 설정 */
|
|
41
|
+
replaceDeps?: Record<string, string>;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
/**
|
|
@@ -176,6 +177,9 @@ async function startWatch(info: ClientWatchInfo): Promise<void> {
|
|
|
176
177
|
// server가 숫자면 해당 포트로 고정 (standalone 클라이언트)
|
|
177
178
|
const serverPort = typeof info.config.server === "number" ? info.config.server : 0;
|
|
178
179
|
|
|
180
|
+
// 의존성 기반 replaceDeps 수집
|
|
181
|
+
const { replaceDeps } = collectDeps(info.pkgDir, info.cwd, info.replaceDeps);
|
|
182
|
+
|
|
179
183
|
// Vite 설정 생성
|
|
180
184
|
const viteConfig = createViteConfig({
|
|
181
185
|
pkgDir: info.pkgDir,
|
|
@@ -185,7 +189,7 @@ async function startWatch(info: ClientWatchInfo): Promise<void> {
|
|
|
185
189
|
env: info.config.env,
|
|
186
190
|
mode: "dev",
|
|
187
191
|
serverPort,
|
|
188
|
-
|
|
192
|
+
replaceDeps,
|
|
189
193
|
onScopeRebuild: () => sender.send("scopeRebuild", {}),
|
|
190
194
|
});
|
|
191
195
|
|
|
@@ -2,7 +2,7 @@ import path from "path";
|
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import cp from "child_process";
|
|
4
4
|
import esbuild from "esbuild";
|
|
5
|
-
import { createWorker, FsWatcher } from "@simplysm/core-node";
|
|
5
|
+
import { createWorker, FsWatcher, pathNorm } from "@simplysm/core-node";
|
|
6
6
|
import { consola } from "consola";
|
|
7
7
|
import {
|
|
8
8
|
parseRootTsconfig,
|
|
@@ -13,8 +13,10 @@ import {
|
|
|
13
13
|
createServerEsbuildOptions,
|
|
14
14
|
collectUninstalledOptionalPeerDeps,
|
|
15
15
|
collectNativeModuleExternals,
|
|
16
|
+
writeChangedOutputFiles,
|
|
16
17
|
} from "../utils/esbuild-config";
|
|
17
18
|
import { registerCleanupHandlers } from "../utils/worker-utils";
|
|
19
|
+
import { collectDeps } from "../utils/package-utils";
|
|
18
20
|
import { copyPublicFiles, watchPublicFiles } from "../utils/copy-public";
|
|
19
21
|
|
|
20
22
|
//#region Types
|
|
@@ -64,8 +66,8 @@ export interface ServerWatchInfo {
|
|
|
64
66
|
configs?: Record<string, unknown>;
|
|
65
67
|
/** sd.config.ts에서 수동 지정한 external 모듈 */
|
|
66
68
|
externals?: string[];
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
+
/** sd.config.ts의 replaceDeps 설정 */
|
|
70
|
+
replaceDeps?: Record<string, string>;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
@@ -103,6 +105,9 @@ const logger = consola.withTag("sd:cli:server:worker");
|
|
|
103
105
|
/** esbuild build context (정리 대상) */
|
|
104
106
|
let esbuildContext: esbuild.BuildContext | undefined;
|
|
105
107
|
|
|
108
|
+
/** 마지막 빌드의 metafile (rebuild 시 변경 파일 필터링용) */
|
|
109
|
+
let lastMetafile: esbuild.Metafile | undefined;
|
|
110
|
+
|
|
106
111
|
/** public 파일 watcher (정리 대상) */
|
|
107
112
|
let publicWatcher: FsWatcher | undefined;
|
|
108
113
|
|
|
@@ -117,6 +122,7 @@ async function cleanup(): Promise<void> {
|
|
|
117
122
|
// (Promise.all 대기 중 다른 호출에서 전역 변수를 수정할 수 있으므로)
|
|
118
123
|
const contextToDispose = esbuildContext;
|
|
119
124
|
esbuildContext = undefined;
|
|
125
|
+
lastMetafile = undefined;
|
|
120
126
|
|
|
121
127
|
const watcherToClose = publicWatcher;
|
|
122
128
|
publicWatcher = undefined;
|
|
@@ -385,6 +391,8 @@ async function createAndBuildContext(
|
|
|
385
391
|
|
|
386
392
|
const context = await esbuild.context({
|
|
387
393
|
...baseOptions,
|
|
394
|
+
metafile: true,
|
|
395
|
+
write: false,
|
|
388
396
|
plugins: [
|
|
389
397
|
{
|
|
390
398
|
name: "watch-notify",
|
|
@@ -393,22 +401,38 @@ async function createAndBuildContext(
|
|
|
393
401
|
sender.send("buildStart", {});
|
|
394
402
|
});
|
|
395
403
|
|
|
396
|
-
pluginBuild.onEnd((result) => {
|
|
404
|
+
pluginBuild.onEnd(async (result) => {
|
|
405
|
+
// metafile 저장
|
|
406
|
+
if (result.metafile != null) {
|
|
407
|
+
lastMetafile = result.metafile;
|
|
408
|
+
}
|
|
409
|
+
|
|
397
410
|
const errors = result.errors.map((e) => e.text);
|
|
398
411
|
const warnings = result.warnings.map((w) => w.text);
|
|
399
412
|
const success = result.errors.length === 0;
|
|
400
413
|
|
|
414
|
+
// output 파일 쓰기 및 변경 여부 확인
|
|
415
|
+
let hasOutputChange = false;
|
|
416
|
+
if (success && result.outputFiles != null) {
|
|
417
|
+
hasOutputChange = await writeChangedOutputFiles(result.outputFiles);
|
|
418
|
+
}
|
|
419
|
+
|
|
401
420
|
if (isBuildFirstTime && success) {
|
|
402
421
|
const confDistPath = path.join(info.pkgDir, "dist", ".config.json");
|
|
403
422
|
fs.writeFileSync(confDistPath, JSON.stringify(info.configs ?? {}, undefined, 2));
|
|
404
423
|
}
|
|
405
424
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
425
|
+
// 첫 빌드이거나, output이 변경되었거나, 에러인 경우에만 build 이벤트 발생
|
|
426
|
+
if (isBuildFirstTime || hasOutputChange || !success) {
|
|
427
|
+
sender.send("build", {
|
|
428
|
+
success,
|
|
429
|
+
mainJsPath,
|
|
430
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
431
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
432
|
+
});
|
|
433
|
+
} else {
|
|
434
|
+
logger.debug("output 변경 없음, 서버 재시작 skip");
|
|
435
|
+
}
|
|
412
436
|
|
|
413
437
|
if (isBuildFirstTime) {
|
|
414
438
|
isBuildFirstTime = false;
|
|
@@ -452,25 +476,27 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
|
|
|
452
476
|
// Watch public/ and public-dev/ (dev mode includes public-dev)
|
|
453
477
|
publicWatcher = await watchPublicFiles(info.pkgDir, true);
|
|
454
478
|
|
|
455
|
-
//
|
|
456
|
-
const
|
|
479
|
+
// 의존성 기반 감시 경로 수집
|
|
480
|
+
const { workspaceDeps, replaceDeps } = collectDeps(info.pkgDir, info.cwd, info.replaceDeps);
|
|
457
481
|
|
|
458
|
-
|
|
459
|
-
watchPaths.push(path.join(info.pkgDir, "src", "**", "*.{ts,tsx}"));
|
|
482
|
+
const watchPaths: string[] = [];
|
|
460
483
|
|
|
461
|
-
//
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
484
|
+
// 1) 서버 패키지 자신 + workspace 의존 패키지 소스
|
|
485
|
+
const watchDirs = [
|
|
486
|
+
info.pkgDir,
|
|
487
|
+
...workspaceDeps.map((d) => path.join(info.cwd, "packages", d)),
|
|
488
|
+
];
|
|
489
|
+
for (const dir of watchDirs) {
|
|
490
|
+
watchPaths.push(path.join(dir, "src", "**", "*"));
|
|
491
|
+
watchPaths.push(path.join(dir, "*.{ts,js,css}"));
|
|
492
|
+
}
|
|
466
493
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
494
|
+
// 2) replaceDeps 의존 패키지 dist (루트 + 패키지 node_modules)
|
|
495
|
+
for (const pkg of replaceDeps) {
|
|
496
|
+
watchPaths.push(path.join(info.cwd, "node_modules", ...pkg.split("/"), "dist", "**", "*.js"));
|
|
497
|
+
watchPaths.push(
|
|
498
|
+
path.join(info.pkgDir, "node_modules", ...pkg.split("/"), "dist", "**", "*.js"),
|
|
499
|
+
);
|
|
474
500
|
}
|
|
475
501
|
|
|
476
502
|
// FsWatcher 시작
|
|
@@ -479,16 +505,11 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
|
|
|
479
505
|
// 파일 변경 감지 시 처리
|
|
480
506
|
srcWatcher.onChange({ delay: 300 }, async (changes) => {
|
|
481
507
|
try {
|
|
482
|
-
//
|
|
483
|
-
const
|
|
484
|
-
const hasEntryPointChange = changes.some(
|
|
485
|
-
(c) =>
|
|
486
|
-
(c.event === "add" || c.event === "unlink") &&
|
|
487
|
-
c.path.startsWith(srcDir.replace(/\\/g, "/")),
|
|
488
|
-
);
|
|
508
|
+
// 파일 추가/삭제가 있으면 context 재생성 (import graph 변경 가능)
|
|
509
|
+
const hasFileAddOrRemove = changes.some((c) => c.event === "add" || c.event === "unlink");
|
|
489
510
|
|
|
490
|
-
if (
|
|
491
|
-
logger.debug("
|
|
511
|
+
if (hasFileAddOrRemove) {
|
|
512
|
+
logger.debug("파일 추가/삭제 감지, context 재생성");
|
|
492
513
|
|
|
493
514
|
const oldContext = esbuildContext;
|
|
494
515
|
esbuildContext = await createAndBuildContext(info, false);
|
|
@@ -496,10 +517,29 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
|
|
|
496
517
|
if (oldContext != null) {
|
|
497
518
|
await oldContext.dispose();
|
|
498
519
|
}
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// 파일 변경만 있는 경우: metafile 필터링
|
|
524
|
+
if (esbuildContext == null) return;
|
|
525
|
+
|
|
526
|
+
// metafile이 없으면 (첫 빌드 전) 무조건 rebuild
|
|
527
|
+
if (lastMetafile == null) {
|
|
528
|
+
await esbuildContext.rebuild();
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// metafile.inputs 키를 절대경로(NormPath)로 변환하여 비교
|
|
533
|
+
const metafileAbsPaths = new Set(
|
|
534
|
+
Object.keys(lastMetafile.inputs).map((key) => pathNorm(info.cwd, key)),
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
const hasRelevantChange = changes.some((c) => metafileAbsPaths.has(c.path));
|
|
538
|
+
|
|
539
|
+
if (hasRelevantChange) {
|
|
540
|
+
await esbuildContext.rebuild();
|
|
499
541
|
} else {
|
|
500
|
-
|
|
501
|
-
await esbuildContext.rebuild();
|
|
502
|
-
}
|
|
542
|
+
logger.debug("변경된 파일이 빌드에 포함되지 않음, rebuild skip");
|
|
503
543
|
}
|
|
504
544
|
} catch (err) {
|
|
505
545
|
sender.send("error", {
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"vitest": "vitest"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@simplysm/sd-cli": "~13.0.
|
|
19
|
-
"@simplysm/sd-claude": "~13.0.
|
|
20
|
-
"@simplysm/lint": "~13.0.
|
|
18
|
+
"@simplysm/sd-cli": "~13.0.66",
|
|
19
|
+
"@simplysm/sd-claude": "~13.0.66",
|
|
20
|
+
"@simplysm/lint": "~13.0.66",
|
|
21
21
|
"@types/node": "^20.19.33",
|
|
22
22
|
"eslint": "^9.39.2",
|
|
23
23
|
"prettier": "^3.8.1",
|