@simplysm/sd-cli 13.0.27 → 13.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/capacitor/capacitor.d.ts.map +1 -1
  2. package/dist/capacitor/capacitor.js +55 -10
  3. package/dist/capacitor/capacitor.js.map +1 -1
  4. package/dist/commands/add-client.d.ts.map +1 -1
  5. package/dist/commands/add-client.js +5 -1
  6. package/dist/commands/add-client.js.map +1 -1
  7. package/dist/commands/build.d.ts.map +1 -1
  8. package/dist/commands/build.js +3 -1
  9. package/dist/commands/build.js.map +1 -1
  10. package/dist/commands/device.d.ts.map +1 -1
  11. package/dist/commands/device.js +9 -3
  12. package/dist/commands/device.js.map +1 -1
  13. package/dist/commands/init.d.ts.map +1 -1
  14. package/dist/commands/init.js +3 -1
  15. package/dist/commands/init.js.map +1 -1
  16. package/dist/commands/lint.d.ts.map +1 -1
  17. package/dist/commands/lint.js +17 -4
  18. package/dist/commands/lint.js.map +1 -1
  19. package/dist/commands/publish.d.ts.map +1 -1
  20. package/dist/commands/publish.js +16 -4
  21. package/dist/commands/publish.js.map +1 -1
  22. package/dist/commands/typecheck.d.ts.map +1 -1
  23. package/dist/commands/typecheck.js.map +1 -1
  24. package/dist/commands/watch.d.ts.map +1 -1
  25. package/dist/commands/watch.js +3 -1
  26. package/dist/commands/watch.js.map +1 -1
  27. package/dist/electron/electron.d.ts.map +1 -1
  28. package/dist/electron/electron.js +8 -2
  29. package/dist/electron/electron.js.map +1 -1
  30. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  31. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  32. package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
  33. package/dist/orchestrators/DevOrchestrator.js +23 -6
  34. package/dist/orchestrators/DevOrchestrator.js.map +1 -1
  35. package/dist/orchestrators/WatchOrchestrator.d.ts.map +1 -1
  36. package/dist/orchestrators/WatchOrchestrator.js +4 -1
  37. package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
  38. package/dist/sd-cli-entry.js.map +1 -1
  39. package/dist/sd-cli.js +6 -1
  40. package/dist/sd-cli.js.map +1 -1
  41. package/dist/utils/config-editor.d.ts.map +1 -1
  42. package/dist/utils/config-editor.js +3 -1
  43. package/dist/utils/config-editor.js.map +1 -1
  44. package/dist/utils/copy-public.d.ts.map +1 -1
  45. package/dist/utils/copy-public.js.map +1 -1
  46. package/dist/utils/esbuild-config.d.ts.map +1 -1
  47. package/dist/utils/esbuild-config.js +6 -2
  48. package/dist/utils/esbuild-config.js.map +1 -1
  49. package/dist/utils/output-utils.d.ts.map +1 -1
  50. package/dist/utils/output-utils.js.map +1 -1
  51. package/dist/utils/rebuild-manager.d.ts.map +1 -1
  52. package/dist/utils/rebuild-manager.js.map +1 -1
  53. package/dist/utils/replace-deps.d.ts.map +1 -1
  54. package/dist/utils/replace-deps.js +3 -1
  55. package/dist/utils/replace-deps.js.map +1 -1
  56. package/dist/utils/sd-config.d.ts.map +1 -1
  57. package/dist/utils/sd-config.js.map +1 -1
  58. package/dist/utils/spawn.d.ts.map +1 -1
  59. package/dist/utils/spawn.js +4 -2
  60. package/dist/utils/spawn.js.map +1 -1
  61. package/dist/utils/template.d.ts.map +1 -1
  62. package/dist/utils/template.js +6 -1
  63. package/dist/utils/template.js.map +1 -1
  64. package/dist/utils/tsconfig.d.ts.map +1 -1
  65. package/dist/utils/tsconfig.js +3 -1
  66. package/dist/utils/tsconfig.js.map +1 -1
  67. package/dist/utils/typecheck-serialization.d.ts.map +1 -1
  68. package/dist/utils/typecheck-serialization.js +2 -1
  69. package/dist/utils/typecheck-serialization.js.map +1 -1
  70. package/dist/utils/vite-config.d.ts.map +1 -1
  71. package/dist/utils/vite-config.js +21 -7
  72. package/dist/utils/vite-config.js.map +1 -1
  73. package/dist/utils/worker-events.d.ts.map +1 -1
  74. package/dist/utils/worker-events.js.map +1 -1
  75. package/dist/utils/worker-utils.d.ts.map +1 -1
  76. package/dist/utils/worker-utils.js.map +1 -1
  77. package/dist/workers/client.worker.d.ts.map +1 -1
  78. package/dist/workers/client.worker.js +10 -2
  79. package/dist/workers/client.worker.js.map +1 -1
  80. package/dist/workers/dts.worker.d.ts.map +1 -1
  81. package/dist/workers/dts.worker.js +14 -4
  82. package/dist/workers/dts.worker.js.map +1 -1
  83. package/dist/workers/library.worker.d.ts.map +1 -1
  84. package/dist/workers/library.worker.js +17 -3
  85. package/dist/workers/library.worker.js.map +1 -1
  86. package/dist/workers/server-runtime.worker.d.ts.map +1 -1
  87. package/dist/workers/server-runtime.worker.js +3 -1
  88. package/dist/workers/server-runtime.worker.js.map +1 -1
  89. package/dist/workers/server.worker.d.ts.map +1 -1
  90. package/dist/workers/server.worker.js +20 -4
  91. package/dist/workers/server.worker.js.map +1 -1
  92. package/package.json +5 -5
  93. package/src/capacitor/capacitor.ts +78 -18
  94. package/src/commands/add-client.ts +5 -1
  95. package/src/commands/build.ts +4 -1
  96. package/src/commands/device.ts +9 -3
  97. package/src/commands/init.ts +3 -1
  98. package/src/commands/lint.ts +22 -5
  99. package/src/commands/publish.ts +22 -6
  100. package/src/commands/typecheck.ts +5 -1
  101. package/src/commands/watch.ts +4 -1
  102. package/src/electron/electron.ts +14 -3
  103. package/src/orchestrators/BuildOrchestrator.ts +15 -4
  104. package/src/orchestrators/DevOrchestrator.ts +41 -11
  105. package/src/orchestrators/WatchOrchestrator.ts +5 -1
  106. package/src/sd-cli-entry.ts +4 -1
  107. package/src/sd-cli.ts +6 -1
  108. package/src/utils/config-editor.ts +15 -5
  109. package/src/utils/copy-public.ts +4 -1
  110. package/src/utils/esbuild-config.ts +18 -4
  111. package/src/utils/output-utils.ts +4 -1
  112. package/src/utils/rebuild-manager.ts +4 -1
  113. package/src/utils/replace-deps.ts +7 -2
  114. package/src/utils/sd-config.ts +5 -1
  115. package/src/utils/spawn.ts +3 -1
  116. package/src/utils/template.ts +6 -1
  117. package/src/utils/tsconfig.ts +7 -2
  118. package/src/utils/typecheck-serialization.ts +6 -2
  119. package/src/utils/vite-config.ts +31 -8
  120. package/src/utils/worker-events.ts +4 -1
  121. package/src/utils/worker-utils.ts +4 -1
  122. package/src/workers/client.worker.ts +12 -3
  123. package/src/workers/dts.worker.ts +24 -7
  124. package/src/workers/library.worker.ts +17 -3
  125. package/src/workers/server-runtime.worker.ts +3 -1
  126. package/src/workers/server.worker.ts +23 -5
  127. package/templates/add-client/__CLIENT__/package.json.hbs +1 -1
  128. package/templates/add-server/__SERVER__/package.json.hbs +2 -2
  129. package/templates/init/package.json.hbs +3 -3
@@ -5,12 +5,20 @@ import type { SdConfig, SdClientPackageConfig, SdServerPackageConfig } from "../
5
5
  import { consola } from "consola";
6
6
  import { loadSdConfig } from "../utils/sd-config";
7
7
  import { getVersion } from "../utils/build-env";
8
- import { setupReplaceDeps, watchReplaceDeps, type WatchReplaceDepResult } from "../utils/replace-deps";
8
+ import {
9
+ setupReplaceDeps,
10
+ watchReplaceDeps,
11
+ type WatchReplaceDepResult,
12
+ } from "../utils/replace-deps";
9
13
  import type * as ClientWorkerModule from "../workers/client.worker";
10
14
  import type * as ServerWorkerModule from "../workers/server.worker";
11
15
  import type * as ServerRuntimeWorkerModule from "../workers/server-runtime.worker";
12
16
  import { Capacitor } from "../capacitor/capacitor";
13
- import { filterPackagesByTargets, getWatchScopes, type PackageResult } from "../utils/package-utils";
17
+ import {
18
+ filterPackagesByTargets,
19
+ getWatchScopes,
20
+ type PackageResult,
21
+ } from "../utils/package-utils";
14
22
  import { printErrors, printServers } from "../utils/output-utils";
15
23
  import { RebuildManager } from "../utils/rebuild-manager";
16
24
  import {
@@ -86,7 +94,10 @@ export class DevOrchestrator {
86
94
  mainJsPath?: string;
87
95
  }
88
96
  >();
89
- private readonly _serverRuntimeWorkers = new Map<string, WorkerProxy<typeof ServerRuntimeWorkerModule>>();
97
+ private readonly _serverRuntimeWorkers = new Map<
98
+ string,
99
+ WorkerProxy<typeof ServerRuntimeWorkerModule>
100
+ >();
90
101
 
91
102
  // State
92
103
  private readonly _results = new Map<string, PackageResult>();
@@ -125,7 +136,11 @@ export class DevOrchestrator {
125
136
 
126
137
  // sd.config.ts 로드 (dev는 패키지 빌드 정보가 필요하므로 필수)
127
138
  try {
128
- this._sdConfig = await loadSdConfig({ cwd: this._cwd, dev: true, opt: this._options.options });
139
+ this._sdConfig = await loadSdConfig({
140
+ cwd: this._cwd,
141
+ dev: true,
142
+ opt: this._options.options,
143
+ });
129
144
  this._logger.debug("sd.config.ts 로드 완료");
130
145
  } catch (err) {
131
146
  this._logger.error(`sd.config.ts 로드 실패: ${err instanceof Error ? err.message : err}`);
@@ -213,7 +228,8 @@ export class DevOrchestrator {
213
228
  this._standaloneClientWorkers = this._clientPackages
214
229
  .filter(
215
230
  ({ config }) =>
216
- typeof config.server === "number" || (typeof config.server === "string" && !serverNames.has(config.server)),
231
+ typeof config.server === "number" ||
232
+ (typeof config.server === "string" && !serverNames.has(config.server)),
217
233
  )
218
234
  .map(({ name, config }) => ({
219
235
  name,
@@ -247,7 +263,10 @@ export class DevOrchestrator {
247
263
 
248
264
  // Vite client 빌드 Promise 미리 생성 (서버 연결 클라이언트)
249
265
  const viteClientBuildPromises = new Map<string, Promise<void>>();
250
- const viteClientReadyPromises = new Map<string, { promise: Promise<void>; resolver: () => void }>();
266
+ const viteClientReadyPromises = new Map<
267
+ string,
268
+ { promise: Promise<void>; resolver: () => void }
269
+ >();
251
270
  for (const workerInfo of this._viteClientWorkers) {
252
271
  viteClientBuildPromises.set(
253
272
  workerInfo.name,
@@ -260,7 +279,10 @@ export class DevOrchestrator {
260
279
  const readyPromise = new Promise<void>((resolve) => {
261
280
  readyResolver = resolve;
262
281
  });
263
- viteClientReadyPromises.set(workerInfo.name, { promise: readyPromise, resolver: readyResolver });
282
+ viteClientReadyPromises.set(workerInfo.name, {
283
+ promise: readyPromise,
284
+ resolver: readyResolver,
285
+ });
264
286
  }
265
287
 
266
288
  // Server Build Worker 및 Promise 생성
@@ -277,7 +299,10 @@ export class DevOrchestrator {
277
299
  }
278
300
 
279
301
  // Server Runtime Promise (초기 서버 시작 완료 대기용)
280
- const serverRuntimePromises = new Map<string, { promise: Promise<void>; resolver: () => void }>();
302
+ const serverRuntimePromises = new Map<
303
+ string,
304
+ { promise: Promise<void>; resolver: () => void }
305
+ >();
281
306
  for (const { name } of this._serverPackages) {
282
307
  let resolver!: () => void;
283
308
  const promise = new Promise<void>((resolve) => {
@@ -448,7 +473,8 @@ export class DevOrchestrator {
448
473
  }
449
474
 
450
475
  // 새 Server Runtime Worker 생성 및 시작
451
- const runtimeWorker = Worker.create<typeof ServerRuntimeWorkerModule>(serverRuntimeWorkerPath);
476
+ const runtimeWorker =
477
+ Worker.create<typeof ServerRuntimeWorkerModule>(serverRuntimeWorkerPath);
452
478
  this._serverRuntimeWorkers.set(serverName, runtimeWorker);
453
479
 
454
480
  // 이 서버에 연결된 클라이언트들의 Vite 서버가 준비될 때까지 대기
@@ -456,7 +482,9 @@ export class DevOrchestrator {
456
482
  const clientReadyPromises = connectedClients
457
483
  .map((clientName) => viteClientReadyPromises.get(clientName)?.promise)
458
484
  .filter((p): p is Promise<void> => p != null);
459
- this._logger.debug(`[${serverName}] 클라이언트 대기: ${String(clientReadyPromises.length)}개`);
485
+ this._logger.debug(
486
+ `[${serverName}] 클라이언트 대기: ${String(clientReadyPromises.length)}개`,
487
+ );
460
488
  if (clientReadyPromises.length > 0) {
461
489
  await Promise.all(clientReadyPromises);
462
490
  }
@@ -651,7 +679,9 @@ export class DevOrchestrator {
651
679
  })),
652
680
  ];
653
681
 
654
- const initialResults = await Promise.allSettled(initialBuildPromises.map((item) => item.promise));
682
+ const initialResults = await Promise.allSettled(
683
+ initialBuildPromises.map((item) => item.promise),
684
+ );
655
685
 
656
686
  initialResults.forEach((result, index) => {
657
687
  const taskName = initialBuildPromises[index].name;
@@ -3,7 +3,11 @@ import { consola } from "consola";
3
3
  import type { BuildTarget, SdConfig, SdPackageConfig } from "../sd-config.types";
4
4
  import { loadSdConfig } from "../utils/sd-config";
5
5
  import { filterPackagesByTargets } from "../utils/package-utils";
6
- import { setupReplaceDeps, watchReplaceDeps, type WatchReplaceDepResult } from "../utils/replace-deps";
6
+ import {
7
+ setupReplaceDeps,
8
+ watchReplaceDeps,
9
+ type WatchReplaceDepResult,
10
+ } from "../utils/replace-deps";
7
11
  import { printErrors } from "../utils/output-utils";
8
12
  import { RebuildManager } from "../utils/rebuild-manager";
9
13
  import { ResultCollector } from "../infra/ResultCollector";
@@ -325,6 +325,9 @@ export function createCliParser(argv: string[]): Argv {
325
325
  // CLI로 직접 실행될 때만 파싱 수행
326
326
  // ESM에서 메인 모듈 판별: import.meta.url과 process.argv[1]을 정규화하여 비교
327
327
  const cliEntryPath = process.argv.at(1);
328
- if (cliEntryPath != null && fileURLToPath(import.meta.url) === fs.realpathSync(path.resolve(cliEntryPath))) {
328
+ if (
329
+ cliEntryPath != null &&
330
+ fileURLToPath(import.meta.url) === fs.realpathSync(path.resolve(cliEntryPath))
331
+ ) {
329
332
  await createCliParser(hideBin(process.argv)).parse();
330
333
  }
package/src/sd-cli.ts CHANGED
@@ -42,7 +42,12 @@ if (isDev) {
42
42
  const cliEntryFilePath = path.join(__dirname, "sd-cli-entry.js");
43
43
  const child = spawn(
44
44
  "node",
45
- ["--max-old-space-size=8192", "--max-semi-space-size=16", cliEntryFilePath, ...process.argv.slice(2)],
45
+ [
46
+ "--max-old-space-size=8192",
47
+ "--max-semi-space-size=16",
48
+ cliEntryFilePath,
49
+ ...process.argv.slice(2),
50
+ ],
46
51
  { stdio: "inherit" },
47
52
  );
48
53
  child.on("spawn", () => {
@@ -31,7 +31,9 @@ function findPackagesObject(configPath: string): {
31
31
  }
32
32
 
33
33
  // "packages" 프로퍼티 찾기
34
- const packagesProp = returnObj.getPropertyOrThrow("packages").asKindOrThrow(SyntaxKind.PropertyAssignment);
34
+ const packagesProp = returnObj
35
+ .getPropertyOrThrow("packages")
36
+ .asKindOrThrow(SyntaxKind.PropertyAssignment);
35
37
  const packagesObj = packagesProp.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
36
38
 
37
39
  return { project, packagesObj };
@@ -50,7 +52,8 @@ export function addPackageToSdConfig(
50
52
  const { project, packagesObj } = findPackagesObject(configPath);
51
53
 
52
54
  // 이미 존재하는지 확인 (따옴표 있는 형태와 없는 형태 모두 체크)
53
- const existing = packagesObj.getProperty(`"${packageName}"`) ?? packagesObj.getProperty(packageName);
55
+ const existing =
56
+ packagesObj.getProperty(`"${packageName}"`) ?? packagesObj.getProperty(packageName);
54
57
  if (existing) {
55
58
  return false;
56
59
  }
@@ -72,10 +75,15 @@ export function addPackageToSdConfig(
72
75
  /**
73
76
  * sd.config.ts에서 특정 클라이언트의 server 필드를 설정한다.
74
77
  */
75
- export function setClientServerInSdConfig(configPath: string, clientName: string, serverName: string): void {
78
+ export function setClientServerInSdConfig(
79
+ configPath: string,
80
+ clientName: string,
81
+ serverName: string,
82
+ ): void {
76
83
  const { project, packagesObj } = findPackagesObject(configPath);
77
84
 
78
- const clientPropNode = packagesObj.getProperty(`"${clientName}"`) ?? packagesObj.getProperty(clientName);
85
+ const clientPropNode =
86
+ packagesObj.getProperty(`"${clientName}"`) ?? packagesObj.getProperty(clientName);
79
87
  if (clientPropNode == null) {
80
88
  throw new Error(`클라이언트 "${clientName}"을(를) sd.config.ts에서 찾을 수 없습니다.`);
81
89
  }
@@ -108,7 +116,9 @@ export function addTailwindToEslintConfig(configPath: string, clientName: string
108
116
  const sourceFile = project.addSourceFileAtPath(configPath);
109
117
 
110
118
  // default export 배열 찾기
111
- const defaultExport = sourceFile.getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression);
119
+ const defaultExport = sourceFile.getFirstDescendantByKindOrThrow(
120
+ SyntaxKind.ArrayLiteralExpression,
121
+ );
112
122
 
113
123
  // tailwindcss 설정이 이미 있는지 확인
114
124
  const text = defaultExport.getText();
@@ -47,7 +47,10 @@ export async function copyPublicFiles(pkgDir: string, includeDev: boolean): Prom
47
47
  * @param includeDev public-dev/ 포함 여부 (dev 모드에서만 true)
48
48
  * @returns FsWatcher 인스턴스 (shutdown 시 close() 호출 필요) 또는 watch할 대상이 없으면 undefined
49
49
  */
50
- export async function watchPublicFiles(pkgDir: string, includeDev: boolean): Promise<FsWatcher | undefined> {
50
+ export async function watchPublicFiles(
51
+ pkgDir: string,
52
+ includeDev: boolean,
53
+ ): Promise<FsWatcher | undefined> {
51
54
  const distDir = path.join(pkgDir, "dist");
52
55
  const publicDir = path.join(pkgDir, "public");
53
56
  const publicDevDir = path.join(pkgDir, "public-dev");
@@ -99,7 +99,9 @@ export function createLibraryEsbuildOptions(options: LibraryEsbuildOptions): esb
99
99
  target: options.target === "node" ? "node20" : "chrome84",
100
100
  bundle: false,
101
101
  write: false,
102
- tsconfigRaw: { compilerOptions: options.compilerOptions as esbuild.TsconfigRaw["compilerOptions"] },
102
+ tsconfigRaw: {
103
+ compilerOptions: options.compilerOptions as esbuild.TsconfigRaw["compilerOptions"],
104
+ },
103
105
  logLevel: "silent",
104
106
  plugins,
105
107
  };
@@ -135,7 +137,9 @@ export function createServerEsbuildOptions(options: ServerEsbuildOptions): esbui
135
137
  },
136
138
  external: options.external,
137
139
  define,
138
- tsconfigRaw: { compilerOptions: options.compilerOptions as esbuild.TsconfigRaw["compilerOptions"] },
140
+ tsconfigRaw: {
141
+ compilerOptions: options.compilerOptions as esbuild.TsconfigRaw["compilerOptions"],
142
+ },
139
143
  logLevel: "silent",
140
144
  };
141
145
  }
@@ -176,7 +180,12 @@ export function collectUninstalledOptionalPeerDeps(pkgDir: string): string[] {
176
180
  return [...external];
177
181
  }
178
182
 
179
- function scanOptionalPeerDeps(pkgName: string, resolveDir: string, external: Set<string>, visited: Set<string>): void {
183
+ function scanOptionalPeerDeps(
184
+ pkgName: string,
185
+ resolveDir: string,
186
+ external: Set<string>,
187
+ visited: Set<string>,
188
+ ): void {
180
189
  if (visited.has(pkgName)) return;
181
190
  visited.add(pkgName);
182
191
 
@@ -233,7 +242,12 @@ export function collectNativeModuleExternals(pkgDir: string): string[] {
233
242
  return [...external];
234
243
  }
235
244
 
236
- function scanNativeModules(pkgName: string, resolveDir: string, external: Set<string>, visited: Set<string>): void {
245
+ function scanNativeModules(
246
+ pkgName: string,
247
+ resolveDir: string,
248
+ external: Set<string>,
249
+ visited: Set<string>,
250
+ ): void {
237
251
  if (visited.has(pkgName)) return;
238
252
  visited.add(pkgName);
239
253
 
@@ -32,7 +32,10 @@ export function printErrors(results: Map<string, ErrorResult>): void {
32
32
  * @param results 패키지별 빌드 결과 상태
33
33
  * @param serverClientsMap 서버별 연결된 클라이언트 목록
34
34
  */
35
- export function printServers(results: Map<string, PackageResult>, serverClientsMap?: Map<string, string[]>): void {
35
+ export function printServers(
36
+ results: Map<string, PackageResult>,
37
+ serverClientsMap?: Map<string, string[]>,
38
+ ): void {
36
39
  // 서버 정보 수집
37
40
  const servers = [...results.values()].filter((r) => r.status === "running" && r.port != null);
38
41
 
@@ -7,7 +7,10 @@ interface RebuildManagerEvents {
7
7
 
8
8
  export class RebuildManager extends EventEmitter<RebuildManagerEvents> {
9
9
  private _isRunning = false;
10
- private readonly _pendingBuilds = new Map<string, { title: string; promise: Promise<void>; resolver: () => void }>();
10
+ private readonly _pendingBuilds = new Map<
11
+ string,
12
+ { title: string; promise: Promise<void>; resolver: () => void }
13
+ >();
11
14
  private readonly _logger: ReturnType<typeof consola.withTag>;
12
15
 
13
16
  constructor(logger: ReturnType<typeof consola.withTag>) {
@@ -157,7 +157,10 @@ async function collectSearchRoots(projectRoot: string): Promise<string[]> {
157
157
  * @param projectRoot - 프로젝트 루트 경로
158
158
  * @param replaceDeps - sd.config.ts의 replaceDeps 설정
159
159
  */
160
- export async function setupReplaceDeps(projectRoot: string, replaceDeps: Record<string, string>): Promise<void> {
160
+ export async function setupReplaceDeps(
161
+ projectRoot: string,
162
+ replaceDeps: Record<string, string>,
163
+ ): Promise<void> {
161
164
  const logger = consola.withTag("sd:cli:replace-deps");
162
165
 
163
166
  // 1. Workspace 패키지 경로 목록 수집
@@ -348,7 +351,9 @@ export async function watchReplaceDeps(
348
351
  logger.info(`삭제: ${relativePath} (${e.targetName})`);
349
352
  }
350
353
  } catch (err) {
351
- logger.error(`복사 실패 (${e.targetName}/${relativePath}): ${err instanceof Error ? err.message : err}`);
354
+ logger.error(
355
+ `복사 실패 (${e.targetName}/${relativePath}): ${err instanceof Error ? err.message : err}`,
356
+ );
352
357
  }
353
358
  }
354
359
  }
@@ -9,7 +9,11 @@ import type { SdConfig } from "../sd-config.types";
9
9
  * @returns SdConfig 객체
10
10
  * @throws sd.config.ts가 없거나 형식이 잘못된 경우
11
11
  */
12
- export async function loadSdConfig(params: { cwd: string; dev: boolean; opt: string[] }): Promise<SdConfig> {
12
+ export async function loadSdConfig(params: {
13
+ cwd: string;
14
+ dev: boolean;
15
+ opt: string[];
16
+ }): Promise<SdConfig> {
13
17
  const sdConfigPath = path.resolve(params.cwd, "sd.config.ts");
14
18
 
15
19
  if (!(await fsExists(sdConfigPath))) {
@@ -72,7 +72,9 @@ export async function spawn(cmd: string, args: string[], options?: SpawnOptions)
72
72
  if (code === 0) {
73
73
  resolve(output);
74
74
  } else {
75
- reject(new Error(`명령어 실패 (${cmd} ${args.join(" ")})\n종료 코드: ${code}\n출력:\n${output}`));
75
+ reject(
76
+ new Error(`명령어 실패 (${cmd} ${args.join(" ")})\n종료 코드: ${code}\n출력:\n${output}`),
77
+ );
76
78
  }
77
79
  });
78
80
  });
@@ -31,7 +31,12 @@ export async function renderTemplateDir(
31
31
  if (stat.isDirectory()) {
32
32
  // 디렉토리 이름 치환 적용
33
33
  const destName = dirReplacements?.[entry] ?? entry;
34
- await renderTemplateDir(path.join(srcDir, entry), path.join(destDir, destName), context, dirReplacements);
34
+ await renderTemplateDir(
35
+ path.join(srcDir, entry),
36
+ path.join(destDir, destName),
37
+ context,
38
+ dirReplacements,
39
+ );
35
40
  } else if (entry.endsWith(".hbs")) {
36
41
  // Handlebars 템플릿 렌더링
37
42
  const source = await fsRead(srcPath);
@@ -18,7 +18,9 @@ export async function getTypesFromPackageJson(packageDir: string): Promise<strin
18
18
  return [];
19
19
  }
20
20
 
21
- const packageJson = await fsReadJson<{ devDependencies?: Record<string, string> }>(packageJsonPath);
21
+ const packageJson = await fsReadJson<{ devDependencies?: Record<string, string> }>(
22
+ packageJsonPath,
23
+ );
22
24
  const devDeps = packageJson.devDependencies ?? {};
23
25
 
24
26
  return Object.keys(devDeps)
@@ -104,7 +106,10 @@ export function parseRootTsconfig(cwd: string): ts.ParsedCommandLine {
104
106
  /**
105
107
  * 패키지의 소스 파일 목록 가져오기 (tsconfig 기반)
106
108
  */
107
- export function getPackageSourceFiles(pkgDir: string, parsedConfig: ts.ParsedCommandLine): string[] {
109
+ export function getPackageSourceFiles(
110
+ pkgDir: string,
111
+ parsedConfig: ts.ParsedCommandLine,
112
+ ): string[] {
108
113
  // 경로 구분자까지 포함하여 비교 (packages/core와 packages/core-common 구분)
109
114
  const pkgSrcPrefix = path.join(pkgDir, "src") + path.sep;
110
115
  return parsedConfig.fileNames.filter((f) => f.startsWith(pkgSrcPrefix));
@@ -43,7 +43,8 @@ export function serializeDiagnostic(diagnostic: ts.Diagnostic): SerializedDiagno
43
43
  function getScriptKind(fileName: string): ts.ScriptKind {
44
44
  if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
45
45
  if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
46
- if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs")) return ts.ScriptKind.JS;
46
+ if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs"))
47
+ return ts.ScriptKind.JS;
47
48
  return ts.ScriptKind.TS;
48
49
  }
49
50
 
@@ -54,7 +55,10 @@ function getScriptKind(fileName: string): ts.ScriptKind {
54
55
  * @param fileCache 파일 내용 캐시 (동일 파일 중복 읽기 방지)
55
56
  * @returns 복원된 ts.Diagnostic 객체
56
57
  */
57
- export function deserializeDiagnostic(serialized: SerializedDiagnostic, fileCache: Map<string, string>): ts.Diagnostic {
58
+ export function deserializeDiagnostic(
59
+ serialized: SerializedDiagnostic,
60
+ fileCache: Map<string, string>,
61
+ ): ts.Diagnostic {
58
62
  let file: ts.SourceFile | undefined;
59
63
  if (serialized.file != null) {
60
64
  const fileName = serialized.file.fileName;
@@ -170,7 +170,8 @@ function sdScopeWatchPlugin(pkgDir: string, scopes: string[], onScopeRebuild?: (
170
170
  // 같은 scope 내 패키지는 이미 excluded이므로 제외
171
171
  if (scopes.some((s) => dep.startsWith(`${s}/`))) continue;
172
172
  // SolidJS 관련 패키지는 solid 플러그인 transform이 필요하므로 pre-bundling 불가
173
- if (dep === "solid-js" || dep.startsWith("@solidjs/") || dep.startsWith("solid-")) continue;
173
+ if (dep === "solid-js" || dep.startsWith("@solidjs/") || dep.startsWith("solid-"))
174
+ continue;
174
175
  // PostCSS/빌드 도구는 브라우저 pre-bundling 대상 아님
175
176
  if (dep === "tailwindcss") continue;
176
177
 
@@ -185,7 +186,13 @@ function sdScopeWatchPlugin(pkgDir: string, scopes: string[], onScopeRebuild?: (
185
186
 
186
187
  // workspace 패키지는 realpath가 소스 디렉토리로 해석되어 .pnpm 구조가 아님
187
188
  // symlink 경로의 node_modules에서 fallback 시도
188
- const depPkgJsonFallback = path.join(scopeDir, name, "node_modules", dep, "package.json");
189
+ const depPkgJsonFallback = path.join(
190
+ scopeDir,
191
+ name,
192
+ "node_modules",
193
+ dep,
194
+ "package.json",
195
+ );
189
196
  if (isSubpathOnlyPackage(depPkgJsonFallback)) {
190
197
  continue;
191
198
  }
@@ -200,32 +207,47 @@ function sdScopeWatchPlugin(pkgDir: string, scopes: string[], onScopeRebuild?: (
200
207
 
201
208
  return {
202
209
  optimizeDeps: {
210
+ force: true,
203
211
  exclude: excluded,
204
212
  include: [...new Set(nestedDepsToInclude)],
205
213
  },
206
214
  };
207
215
  },
208
216
  async configureServer(server) {
209
- const distDirs: string[] = [];
217
+ const watchPaths: string[] = [];
210
218
 
211
219
  for (const scope of scopes) {
212
220
  const scopeDir = path.join(pkgDir, "node_modules", scope);
213
221
  if (!fs.existsSync(scopeDir)) continue;
214
222
 
215
223
  for (const pkgName of fs.readdirSync(scopeDir)) {
216
- const distDir = path.join(scopeDir, pkgName, "dist");
224
+ const pkgRoot = path.join(scopeDir, pkgName);
225
+
226
+ // dist 디렉토리 watch (JS/TS 빌드 결과물)
227
+ const distDir = path.join(pkgRoot, "dist");
217
228
  if (fs.existsSync(distDir)) {
218
- distDirs.push(distDir);
229
+ watchPaths.push(distDir);
230
+ }
231
+
232
+ // 패키지 루트의 CSS/config 파일 watch (tailwind.css, tailwind.config.ts 등)
233
+ for (const file of fs.readdirSync(pkgRoot)) {
234
+ if (
235
+ file.endsWith(".css") ||
236
+ file === "tailwind.config.ts" ||
237
+ file === "tailwind.config.js"
238
+ ) {
239
+ watchPaths.push(path.join(pkgRoot, file));
240
+ }
219
241
  }
220
242
  }
221
243
  }
222
244
 
223
- if (distDirs.length === 0) return;
245
+ if (watchPaths.length === 0) return;
224
246
 
225
247
  // Vite의 기본 watcher는 **/node_modules/**를 ignore하고
226
248
  // server.watcher.add()로는 이 패턴을 override할 수 없다.
227
249
  // 별도의 FsWatcher로 scope 패키지의 dist 디렉토리를 감시한다.
228
- const scopeWatcher = await FsWatcher.watch(distDirs);
250
+ const scopeWatcher = await FsWatcher.watch(watchPaths);
229
251
  scopeWatcher.onChange({ delay: 300 }, (changeInfos) => {
230
252
  for (const { path: changedPath } of changeInfos) {
231
253
  // pnpm symlink → real path 변환 (Vite module graph은 real path 사용)
@@ -275,7 +297,8 @@ export interface ViteConfigOptions {
275
297
  * - dev 모드: dev server (define으로 env 치환, server 설정)
276
298
  */
277
299
  export function createViteConfig(options: ViteConfigOptions): ViteUserConfig {
278
- const { pkgDir, name, tsconfigPath, compilerOptions, env, mode, serverPort, watchScopes } = options;
300
+ const { pkgDir, name, tsconfigPath, compilerOptions, env, mode, serverPort, watchScopes } =
301
+ options;
279
302
 
280
303
  // Read package.json to extract app name for PWA manifest
281
304
  const pkgJsonPath = path.join(pkgDir, "package.json");
@@ -62,7 +62,10 @@ export interface WorkerEventHandlerOptions {
62
62
  * @param rebuildManager 리빌드 매니저
63
63
  * @returns completeTask 함수 (결과를 저장하고 빌드 완료를 알림)
64
64
  */
65
- export function registerWorkerEventHandlers<TEvents extends Record<string, any[]>, T extends BaseWorkerInfo<TEvents>>(
65
+ export function registerWorkerEventHandlers<
66
+ TEvents extends Record<string, any[]>,
67
+ T extends BaseWorkerInfo<TEvents>,
68
+ >(
66
69
  workerInfo: T,
67
70
  opts: WorkerEventHandlerOptions,
68
71
  results: Map<string, PackageResult>,
@@ -10,7 +10,10 @@ import type { ConsolaInstance } from "consola";
10
10
  * @param cleanup - Async cleanup function to execute on shutdown
11
11
  * @param logger - Consola logger instance for error logging
12
12
  */
13
- export function registerCleanupHandlers(cleanup: () => Promise<void>, logger: ConsolaInstance): void {
13
+ export function registerCleanupHandlers(
14
+ cleanup: () => Promise<void>,
15
+ logger: ConsolaInstance,
16
+ ): void {
14
17
  const handleSignal = () => {
15
18
  cleanup()
16
19
  .catch((err) => {
@@ -115,7 +115,11 @@ async function build(info: ClientBuildInfo): Promise<ClientBuildResult> {
115
115
  const tsconfigPath = path.join(info.cwd, "tsconfig.json");
116
116
 
117
117
  // browser 타겟용 compilerOptions 생성
118
- const compilerOptions = await getCompilerOptionsForPackage(parsedConfig.options, "browser", info.pkgDir);
118
+ const compilerOptions = await getCompilerOptionsForPackage(
119
+ parsedConfig.options,
120
+ "browser",
121
+ info.pkgDir,
122
+ );
119
123
 
120
124
  // Vite 설정 생성 및 빌드
121
125
  const viteConfig = createViteConfig({
@@ -162,7 +166,11 @@ async function startWatch(info: ClientWatchInfo): Promise<void> {
162
166
  const tsconfigPath = path.join(info.cwd, "tsconfig.json");
163
167
 
164
168
  // browser 타겟용 compilerOptions 생성
165
- const compilerOptions = await getCompilerOptionsForPackage(parsedConfig.options, "browser", info.pkgDir);
169
+ const compilerOptions = await getCompilerOptionsForPackage(
170
+ parsedConfig.options,
171
+ "browser",
172
+ info.pkgDir,
173
+ );
166
174
 
167
175
  // server가 0이면 자동 포트 할당 (서버 연결 클라이언트)
168
176
  // server가 숫자면 해당 포트로 고정 (standalone 클라이언트)
@@ -192,7 +200,8 @@ async function startWatch(info: ClientWatchInfo): Promise<void> {
192
200
 
193
201
  // 실제 할당된 포트 반환 (config.server.port는 설정값이므로 httpServer에서 실제 포트를 가져옴)
194
202
  const address = viteServer.httpServer?.address();
195
- const actualPort = typeof address === "object" && address != null ? address.port : viteServer.config.server.port;
203
+ const actualPort =
204
+ typeof address === "object" && address != null ? address.port : viteServer.config.server.port;
196
205
 
197
206
  sender.send("serverReady", { port: actualPort });
198
207
  } catch (err) {
@@ -78,7 +78,9 @@ export interface DtsWorkerEvents extends Record<string, unknown> {
78
78
  const logger = consola.withTag("sd:cli:dts:worker");
79
79
 
80
80
  /** tsc watch program (정리 대상) */
81
- let tscWatchProgram: ts.WatchOfFilesAndCompilerOptions<ts.EmitAndSemanticDiagnosticsBuilderProgram> | undefined;
81
+ let tscWatchProgram:
82
+ | ts.WatchOfFilesAndCompilerOptions<ts.EmitAndSemanticDiagnosticsBuilderProgram>
83
+ | undefined;
82
84
 
83
85
  /**
84
86
  * 리소스 정리
@@ -141,7 +143,9 @@ function adjustDtsMapSources(content: string, originalDir: string, newDir: strin
141
143
  *
142
144
  * @returns (fileName, content) => [newPath, newContent] | null (null이면 쓰기 무시)
143
145
  */
144
- function createDtsPathRewriter(pkgDir: string): (fileName: string, content: string) => [string, string] | null {
146
+ function createDtsPathRewriter(
147
+ pkgDir: string,
148
+ ): (fileName: string, content: string) => [string, string] | null {
145
149
  const pkgName = path.basename(pkgDir);
146
150
  const distDir = path.join(pkgDir, "dist");
147
151
  const distPrefix = distDir + path.sep;
@@ -276,8 +280,12 @@ async function buildDts(info: DtsBuildInfo): Promise<DtsBuildResult> {
276
280
  const filteredDiagnostics = allDiagnostics.filter(diagnosticFilter);
277
281
 
278
282
  const serializedDiagnostics = filteredDiagnostics.map(serializeDiagnostic);
279
- const errorCount = filteredDiagnostics.filter((d) => d.category === ts.DiagnosticCategory.Error).length;
280
- const warningCount = filteredDiagnostics.filter((d) => d.category === ts.DiagnosticCategory.Warning).length;
283
+ const errorCount = filteredDiagnostics.filter(
284
+ (d) => d.category === ts.DiagnosticCategory.Error,
285
+ ).length;
286
+ const warningCount = filteredDiagnostics.filter(
287
+ (d) => d.category === ts.DiagnosticCategory.Warning,
288
+ ).length;
281
289
 
282
290
  // 에러 메시지 문자열 배열 (하위 호환용)
283
291
  const errors = filteredDiagnostics
@@ -330,7 +338,11 @@ async function startDtsWatch(info: DtsWatchInfo): Promise<void> {
330
338
  try {
331
339
  const parsedConfig = parseRootTsconfig(info.cwd);
332
340
  const rootFiles = getPackageSourceFiles(info.pkgDir, parsedConfig);
333
- const baseOptions = await getCompilerOptionsForPackage(parsedConfig.options, info.env, info.pkgDir);
341
+ const baseOptions = await getCompilerOptionsForPackage(
342
+ parsedConfig.options,
343
+ info.env,
344
+ info.pkgDir,
345
+ );
334
346
 
335
347
  // 해당 패키지 경로 (필터링용)
336
348
  const pkgSrcPrefix = path.join(info.pkgDir, "src") + path.sep;
@@ -362,7 +374,9 @@ async function startDtsWatch(info: DtsWatchInfo): Promise<void> {
362
374
 
363
375
  // 파일 위치 정보가 있으면 포함 (절대경로:라인:컬럼 형식 - IDE 링크 지원)
364
376
  if (diagnostic.file != null && diagnostic.start != null) {
365
- const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
377
+ const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
378
+ diagnostic.start,
379
+ );
366
380
  collectedErrors.push(
367
381
  `${diagnostic.file.fileName}:${line + 1}:${character + 1}: TS${diagnostic.code}: ${message}`,
368
382
  );
@@ -424,7 +438,10 @@ async function startDtsWatch(info: DtsWatchInfo): Promise<void> {
424
438
  }
425
439
  }
426
440
 
427
- const sender = createWorker<{ startDtsWatch: typeof startDtsWatch; buildDts: typeof buildDts }, DtsWorkerEvents>({
441
+ const sender = createWorker<
442
+ { startDtsWatch: typeof startDtsWatch; buildDts: typeof buildDts },
443
+ DtsWorkerEvents
444
+ >({
428
445
  startDtsWatch,
429
446
  buildDts,
430
447
  });