@simplysm/sd-cli 13.0.28 → 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.
- package/dist/capacitor/capacitor.d.ts.map +1 -1
- package/dist/capacitor/capacitor.js +55 -10
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/add-client.d.ts.map +1 -1
- package/dist/commands/add-client.js +5 -1
- package/dist/commands/add-client.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +3 -1
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +9 -3
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +3 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +17 -4
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +16 -4
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +3 -1
- package/dist/commands/watch.js.map +1 -1
- package/dist/electron/electron.d.ts.map +1 -1
- package/dist/electron/electron.js +8 -2
- package/dist/electron/electron.js.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
- package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevOrchestrator.js +23 -6
- package/dist/orchestrators/DevOrchestrator.js.map +1 -1
- package/dist/orchestrators/WatchOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/WatchOrchestrator.js +4 -1
- package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.js +6 -1
- package/dist/sd-cli.js.map +1 -1
- package/dist/utils/config-editor.d.ts.map +1 -1
- package/dist/utils/config-editor.js +3 -1
- package/dist/utils/config-editor.js.map +1 -1
- package/dist/utils/copy-public.d.ts.map +1 -1
- package/dist/utils/copy-public.js.map +1 -1
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/esbuild-config.js +6 -2
- package/dist/utils/esbuild-config.js.map +1 -1
- package/dist/utils/output-utils.d.ts.map +1 -1
- package/dist/utils/output-utils.js.map +1 -1
- package/dist/utils/rebuild-manager.d.ts.map +1 -1
- package/dist/utils/rebuild-manager.js.map +1 -1
- package/dist/utils/replace-deps.d.ts.map +1 -1
- package/dist/utils/replace-deps.js +3 -1
- package/dist/utils/replace-deps.js.map +1 -1
- package/dist/utils/sd-config.d.ts.map +1 -1
- package/dist/utils/sd-config.js.map +1 -1
- package/dist/utils/spawn.d.ts.map +1 -1
- package/dist/utils/spawn.js +4 -2
- package/dist/utils/spawn.js.map +1 -1
- package/dist/utils/template.d.ts.map +1 -1
- package/dist/utils/template.js +6 -1
- package/dist/utils/template.js.map +1 -1
- package/dist/utils/tsconfig.d.ts.map +1 -1
- package/dist/utils/tsconfig.js +3 -1
- package/dist/utils/tsconfig.js.map +1 -1
- package/dist/utils/typecheck-serialization.d.ts.map +1 -1
- package/dist/utils/typecheck-serialization.js +2 -1
- package/dist/utils/typecheck-serialization.js.map +1 -1
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +21 -7
- package/dist/utils/vite-config.js.map +1 -1
- package/dist/utils/worker-events.d.ts.map +1 -1
- package/dist/utils/worker-events.js.map +1 -1
- package/dist/utils/worker-utils.d.ts.map +1 -1
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +10 -2
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/dts.worker.d.ts.map +1 -1
- package/dist/workers/dts.worker.js +14 -4
- package/dist/workers/dts.worker.js.map +1 -1
- package/dist/workers/library.worker.d.ts.map +1 -1
- package/dist/workers/library.worker.js +17 -3
- 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 +3 -1
- 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 +20 -4
- package/dist/workers/server.worker.js.map +1 -1
- package/package.json +5 -5
- package/src/capacitor/capacitor.ts +78 -18
- package/src/commands/add-client.ts +5 -1
- package/src/commands/build.ts +4 -1
- package/src/commands/device.ts +9 -3
- package/src/commands/init.ts +3 -1
- package/src/commands/lint.ts +22 -5
- package/src/commands/publish.ts +22 -6
- package/src/commands/typecheck.ts +5 -1
- package/src/commands/watch.ts +4 -1
- package/src/electron/electron.ts +14 -3
- package/src/orchestrators/BuildOrchestrator.ts +15 -4
- package/src/orchestrators/DevOrchestrator.ts +41 -11
- package/src/orchestrators/WatchOrchestrator.ts +5 -1
- package/src/sd-cli-entry.ts +4 -1
- package/src/sd-cli.ts +6 -1
- package/src/utils/config-editor.ts +15 -5
- package/src/utils/copy-public.ts +4 -1
- package/src/utils/esbuild-config.ts +18 -4
- package/src/utils/output-utils.ts +4 -1
- package/src/utils/rebuild-manager.ts +4 -1
- package/src/utils/replace-deps.ts +7 -2
- package/src/utils/sd-config.ts +5 -1
- package/src/utils/spawn.ts +3 -1
- package/src/utils/template.ts +6 -1
- package/src/utils/tsconfig.ts +7 -2
- package/src/utils/typecheck-serialization.ts +6 -2
- package/src/utils/vite-config.ts +31 -8
- package/src/utils/worker-events.ts +4 -1
- package/src/utils/worker-utils.ts +4 -1
- package/src/workers/client.worker.ts +12 -3
- package/src/workers/dts.worker.ts +24 -7
- package/src/workers/library.worker.ts +17 -3
- package/src/workers/server-runtime.worker.ts +3 -1
- package/src/workers/server.worker.ts +23 -5
- 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
|
@@ -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 {
|
|
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 {
|
|
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<
|
|
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({
|
|
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" ||
|
|
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<
|
|
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, {
|
|
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<
|
|
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 =
|
|
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(
|
|
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(
|
|
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 {
|
|
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";
|
package/src/sd-cli-entry.ts
CHANGED
|
@@ -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 (
|
|
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
|
-
[
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
119
|
+
const defaultExport = sourceFile.getFirstDescendantByKindOrThrow(
|
|
120
|
+
SyntaxKind.ArrayLiteralExpression,
|
|
121
|
+
);
|
|
112
122
|
|
|
113
123
|
// tailwindcss 설정이 이미 있는지 확인
|
|
114
124
|
const text = defaultExport.getText();
|
package/src/utils/copy-public.ts
CHANGED
|
@@ -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(
|
|
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: {
|
|
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: {
|
|
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(
|
|
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(
|
|
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(
|
|
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<
|
|
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(
|
|
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(
|
|
354
|
+
logger.error(
|
|
355
|
+
`복사 실패 (${e.targetName}/${relativePath}): ${err instanceof Error ? err.message : err}`,
|
|
356
|
+
);
|
|
352
357
|
}
|
|
353
358
|
}
|
|
354
359
|
}
|
package/src/utils/sd-config.ts
CHANGED
|
@@ -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: {
|
|
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))) {
|
package/src/utils/spawn.ts
CHANGED
|
@@ -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(
|
|
75
|
+
reject(
|
|
76
|
+
new Error(`명령어 실패 (${cmd} ${args.join(" ")})\n종료 코드: ${code}\n출력:\n${output}`),
|
|
77
|
+
);
|
|
76
78
|
}
|
|
77
79
|
});
|
|
78
80
|
});
|
package/src/utils/template.ts
CHANGED
|
@@ -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(
|
|
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);
|
package/src/utils/tsconfig.ts
CHANGED
|
@@ -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> }>(
|
|
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(
|
|
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"))
|
|
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(
|
|
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;
|
package/src/utils/vite-config.ts
CHANGED
|
@@ -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-"))
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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(
|
|
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 } =
|
|
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<
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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:
|
|
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(
|
|
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(
|
|
280
|
-
|
|
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(
|
|
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(
|
|
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<
|
|
441
|
+
const sender = createWorker<
|
|
442
|
+
{ startDtsWatch: typeof startDtsWatch; buildDts: typeof buildDts },
|
|
443
|
+
DtsWorkerEvents
|
|
444
|
+
>({
|
|
428
445
|
startDtsWatch,
|
|
429
446
|
buildDts,
|
|
430
447
|
});
|