@simplysm/sd-cli 13.0.28 → 13.0.30
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 +30 -28
- 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 +24 -7
- 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/package-utils.d.ts +4 -3
- package/dist/utils/package-utils.d.ts.map +1 -1
- package/dist/utils/package-utils.js +10 -2
- package/dist/utils/package-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 +24 -17
- 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 +42 -12
- 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/package-utils.ts +16 -4
- 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 +36 -20
- 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
package/src/commands/lint.ts
CHANGED
|
@@ -26,7 +26,12 @@ export interface LintOptions {
|
|
|
26
26
|
//#region Utilities
|
|
27
27
|
|
|
28
28
|
/** ESLint 설정 파일 탐색 순서 */
|
|
29
|
-
const ESLINT_CONFIG_FILES = [
|
|
29
|
+
const ESLINT_CONFIG_FILES = [
|
|
30
|
+
"eslint.config.ts",
|
|
31
|
+
"eslint.config.mts",
|
|
32
|
+
"eslint.config.js",
|
|
33
|
+
"eslint.config.mjs",
|
|
34
|
+
] as const;
|
|
30
35
|
|
|
31
36
|
/** Stylelint 설정 파일 탐색 순서 */
|
|
32
37
|
const STYLELINT_CONFIG_FILES = [
|
|
@@ -66,7 +71,9 @@ export async function loadIgnorePatterns(cwd: string): Promise<string[]> {
|
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
if (configPath == null) {
|
|
69
|
-
throw new SdError(
|
|
74
|
+
throw new SdError(
|
|
75
|
+
`ESLint 설정 파일을 찾을 수 없습니다 (cwd: ${cwd}): ${ESLINT_CONFIG_FILES.join(", ")}`,
|
|
76
|
+
);
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
const jiti = createJiti(import.meta.url);
|
|
@@ -75,7 +82,11 @@ export async function loadIgnorePatterns(cwd: string): Promise<string[]> {
|
|
|
75
82
|
let configs: unknown;
|
|
76
83
|
if (Array.isArray(configModule)) {
|
|
77
84
|
configs = configModule;
|
|
78
|
-
} else if (
|
|
85
|
+
} else if (
|
|
86
|
+
configModule != null &&
|
|
87
|
+
typeof configModule === "object" &&
|
|
88
|
+
"default" in configModule
|
|
89
|
+
) {
|
|
79
90
|
configs = configModule.default;
|
|
80
91
|
} else {
|
|
81
92
|
throw new SdError(`ESLint 설정 파일이 올바른 형식이 아닙니다: ${configPath}`);
|
|
@@ -240,14 +251,20 @@ export async function runLint(options: LintOptions): Promise<void> {
|
|
|
240
251
|
);
|
|
241
252
|
|
|
242
253
|
if (stylelintErrorCount > 0) {
|
|
243
|
-
logger.error("Stylelint 에러 발생", {
|
|
254
|
+
logger.error("Stylelint 에러 발생", {
|
|
255
|
+
errorCount: stylelintErrorCount,
|
|
256
|
+
warningCount: stylelintWarningCount,
|
|
257
|
+
});
|
|
244
258
|
} else if (stylelintWarningCount > 0) {
|
|
245
259
|
logger.info("Stylelint 완료 (경고 있음)", {
|
|
246
260
|
errorCount: stylelintErrorCount,
|
|
247
261
|
warningCount: stylelintWarningCount,
|
|
248
262
|
});
|
|
249
263
|
} else {
|
|
250
|
-
logger.info("Stylelint 완료", {
|
|
264
|
+
logger.info("Stylelint 완료", {
|
|
265
|
+
errorCount: stylelintErrorCount,
|
|
266
|
+
warningCount: stylelintWarningCount,
|
|
267
|
+
});
|
|
251
268
|
}
|
|
252
269
|
|
|
253
270
|
// Stylelint formatter 출력
|
package/src/commands/publish.ts
CHANGED
|
@@ -279,7 +279,9 @@ async function upgradeVersion(
|
|
|
279
279
|
|
|
280
280
|
// prerelease 여부에 따라 증가 방식 결정
|
|
281
281
|
const newVersion =
|
|
282
|
-
prereleaseInfo !== null
|
|
282
|
+
prereleaseInfo !== null
|
|
283
|
+
? semver.inc(currentVersion, "prerelease")!
|
|
284
|
+
: semver.inc(currentVersion, "patch")!;
|
|
283
285
|
|
|
284
286
|
if (dryRun) {
|
|
285
287
|
// dry-run: 파일 수정 없이 새 버전만 반환
|
|
@@ -368,7 +370,9 @@ async function publishPackage(
|
|
|
368
370
|
const remotePath = publishConfig.path ?? "/";
|
|
369
371
|
|
|
370
372
|
if (dryRun) {
|
|
371
|
-
logger.info(
|
|
373
|
+
logger.info(
|
|
374
|
+
`[DRY-RUN] [${pkgName}] ${publishConfig.type} 업로드: ${distPath} → ${remotePath}`,
|
|
375
|
+
);
|
|
372
376
|
} else {
|
|
373
377
|
logger.debug(`[${pkgName}] ${publishConfig.type} 업로드: ${distPath} → ${remotePath}`);
|
|
374
378
|
await StorageFactory.connect(
|
|
@@ -499,7 +503,9 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
499
503
|
workspaceGlobs.push(...parseWorkspaceGlobs(yamlContent));
|
|
500
504
|
}
|
|
501
505
|
|
|
502
|
-
const allPkgPaths = (
|
|
506
|
+
const allPkgPaths = (
|
|
507
|
+
await Promise.all(workspaceGlobs.map((item) => fsGlob(path.resolve(cwd, item))))
|
|
508
|
+
)
|
|
503
509
|
.flat()
|
|
504
510
|
.filter((item) => !path.basename(item).includes("."));
|
|
505
511
|
|
|
@@ -587,7 +593,13 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
587
593
|
if (diff.trim() !== "" || stagedDiff.trim() !== "") {
|
|
588
594
|
logger.info("커밋되지 않은 변경사항 감지. claude 자동 커밋 시도...");
|
|
589
595
|
try {
|
|
590
|
-
await spawn("claude", [
|
|
596
|
+
await spawn("claude", [
|
|
597
|
+
"-p",
|
|
598
|
+
"/sd-commit all",
|
|
599
|
+
"--dangerously-skip-permissions",
|
|
600
|
+
"--model",
|
|
601
|
+
"haiku",
|
|
602
|
+
]);
|
|
591
603
|
} catch (e) {
|
|
592
604
|
throw new Error(
|
|
593
605
|
"자동 커밋에 실패했습니다. 수동으로 커밋 후 다시 시도하세요.\n" +
|
|
@@ -599,7 +611,9 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
599
611
|
const recheckDiff = await spawn("git", ["diff", "--name-only"]);
|
|
600
612
|
const recheckStaged = await spawn("git", ["diff", "--cached", "--name-only"]);
|
|
601
613
|
if (recheckDiff.trim() !== "" || recheckStaged.trim() !== "") {
|
|
602
|
-
throw new Error(
|
|
614
|
+
throw new Error(
|
|
615
|
+
"자동 커밋 후에도 미커밋 변경사항이 남아있습니다.\n" + recheckDiff + recheckStaged,
|
|
616
|
+
);
|
|
603
617
|
}
|
|
604
618
|
logger.info("자동 커밋 완료.");
|
|
605
619
|
}
|
|
@@ -801,7 +815,9 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
801
815
|
}
|
|
802
816
|
} catch (err) {
|
|
803
817
|
// postPublish 실패 시 경고만 출력 (배포 롤백 불가)
|
|
804
|
-
logger.warn(
|
|
818
|
+
logger.warn(
|
|
819
|
+
`postPublish 스크립트 실패 (계속 진행): ${err instanceof Error ? err.message : err}`,
|
|
820
|
+
);
|
|
805
821
|
}
|
|
806
822
|
}
|
|
807
823
|
}
|
|
@@ -68,7 +68,11 @@ function toTypecheckEnvs(target: string | undefined): TypecheckEnv[] {
|
|
|
68
68
|
* scripts 타겟 패키지는 제외합니다.
|
|
69
69
|
* @internal 테스트용으로 export
|
|
70
70
|
*/
|
|
71
|
-
export function extractPackages(
|
|
71
|
+
export function extractPackages(
|
|
72
|
+
fileNames: string[],
|
|
73
|
+
cwd: string,
|
|
74
|
+
config: SdConfig,
|
|
75
|
+
): Map<string, PackageInfo> {
|
|
72
76
|
const packages = new Map<string, PackageInfo>();
|
|
73
77
|
|
|
74
78
|
for (const fileName of fileNames) {
|
package/src/commands/watch.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// packages/cli/src/commands/watch.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
WatchOrchestrator,
|
|
4
|
+
type WatchOrchestratorOptions,
|
|
5
|
+
} from "../orchestrators/WatchOrchestrator";
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Watch 명령 옵션 (하위 호환성)
|
package/src/electron/electron.ts
CHANGED
|
@@ -61,7 +61,12 @@ export class Electron {
|
|
|
61
61
|
/**
|
|
62
62
|
* 명령어 실행 (로깅 포함)
|
|
63
63
|
*/
|
|
64
|
-
private async _exec(
|
|
64
|
+
private async _exec(
|
|
65
|
+
cmd: string,
|
|
66
|
+
args: string[],
|
|
67
|
+
cwd: string,
|
|
68
|
+
env?: Record<string, string>,
|
|
69
|
+
): Promise<string> {
|
|
65
70
|
Electron._logger.debug(`실행 명령: ${cmd} ${args.join(" ")}`);
|
|
66
71
|
const result = await spawn(cmd, args, { cwd, env });
|
|
67
72
|
Electron._logger.debug(`실행 결과: ${result}`);
|
|
@@ -263,7 +268,9 @@ export class Electron {
|
|
|
263
268
|
*/
|
|
264
269
|
private async _runElectronBuilder(srcPath: string): Promise<void> {
|
|
265
270
|
if (!Electron._canCreateSymlink()) {
|
|
266
|
-
throw new Error(
|
|
271
|
+
throw new Error(
|
|
272
|
+
"Electron 빌드를 위해서는 Symlink 생성 권한이 필요합니다. 윈도우의 개발자모드를 활성화하세요.",
|
|
273
|
+
);
|
|
267
274
|
}
|
|
268
275
|
|
|
269
276
|
const distPath = path.resolve(this._electronPath, "dist");
|
|
@@ -292,7 +299,11 @@ export class Electron {
|
|
|
292
299
|
const configFilePath = path.resolve(this._electronPath, "builder-config.json");
|
|
293
300
|
await fsWriteJson(configFilePath, builderConfig, { space: 2 });
|
|
294
301
|
|
|
295
|
-
await this._exec(
|
|
302
|
+
await this._exec(
|
|
303
|
+
"npx",
|
|
304
|
+
["electron-builder", "--win", "--config", configFilePath],
|
|
305
|
+
this._pkgPath,
|
|
306
|
+
);
|
|
296
307
|
}
|
|
297
308
|
|
|
298
309
|
/**
|
|
@@ -3,7 +3,12 @@ import ts from "typescript";
|
|
|
3
3
|
import { Worker, type WorkerProxy, fsRm } from "@simplysm/core-node";
|
|
4
4
|
import "@simplysm/core-common";
|
|
5
5
|
import { consola } from "consola";
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
SdConfig,
|
|
8
|
+
SdBuildPackageConfig,
|
|
9
|
+
SdClientPackageConfig,
|
|
10
|
+
SdServerPackageConfig,
|
|
11
|
+
} from "../sd-config.types";
|
|
7
12
|
import { loadSdConfig } from "../utils/sd-config";
|
|
8
13
|
import { getVersion } from "../utils/build-env";
|
|
9
14
|
import { setupReplaceDeps } from "../utils/replace-deps";
|
|
@@ -69,7 +74,11 @@ interface ClassifiedPackages {
|
|
|
69
74
|
function classifyPackages(
|
|
70
75
|
packages: Record<
|
|
71
76
|
string,
|
|
72
|
-
|
|
77
|
+
| SdBuildPackageConfig
|
|
78
|
+
| SdClientPackageConfig
|
|
79
|
+
| SdServerPackageConfig
|
|
80
|
+
| { target: "scripts" }
|
|
81
|
+
| undefined
|
|
73
82
|
>,
|
|
74
83
|
targets: string[],
|
|
75
84
|
): ClassifiedPackages {
|
|
@@ -260,7 +269,8 @@ export class BuildOrchestrator {
|
|
|
260
269
|
// JS 빌드와 DTS 생성을 병렬 실행
|
|
261
270
|
const libraryWorker: WorkerProxy<typeof LibraryWorkerModule> =
|
|
262
271
|
Worker.create<typeof LibraryWorkerModule>(libraryWorkerPath);
|
|
263
|
-
const dtsWorker: WorkerProxy<typeof DtsWorkerModule> =
|
|
272
|
+
const dtsWorker: WorkerProxy<typeof DtsWorkerModule> =
|
|
273
|
+
Worker.create<typeof DtsWorkerModule>(dtsWorkerPath);
|
|
264
274
|
|
|
265
275
|
try {
|
|
266
276
|
const [buildResult, dtsResult] = await Promise.all([
|
|
@@ -313,7 +323,8 @@ export class BuildOrchestrator {
|
|
|
313
323
|
// Vite 빌드와 타입체크를 병렬 실행
|
|
314
324
|
const clientWorker: WorkerProxy<typeof ClientWorkerModule> =
|
|
315
325
|
Worker.create<typeof ClientWorkerModule>(clientWorkerPath);
|
|
316
|
-
const dtsWorker: WorkerProxy<typeof DtsWorkerModule> =
|
|
326
|
+
const dtsWorker: WorkerProxy<typeof DtsWorkerModule> =
|
|
327
|
+
Worker.create<typeof DtsWorkerModule>(dtsWorkerPath);
|
|
317
328
|
|
|
318
329
|
try {
|
|
319
330
|
const clientConfig: SdClientPackageConfig = {
|
|
@@ -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}`);
|
|
@@ -146,7 +161,7 @@ export class DevOrchestrator {
|
|
|
146
161
|
// watchScopes 생성 (루트 package.json에서 scope 추출)
|
|
147
162
|
const rootPkgJsonPath = path.join(this._cwd, "package.json");
|
|
148
163
|
const rootPkgName = JSON.parse(fs.readFileSync(rootPkgJsonPath, "utf-8")).name as string;
|
|
149
|
-
this._watchScopes = getWatchScopes(rootPkgName);
|
|
164
|
+
this._watchScopes = getWatchScopes(rootPkgName, this._sdConfig.replaceDeps);
|
|
150
165
|
|
|
151
166
|
// targets 필터링
|
|
152
167
|
const allPackages = filterPackagesByTargets(this._sdConfig.packages, targets);
|
|
@@ -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
|
|
|
@@ -16,18 +16,30 @@ export function findPackageRoot(startDir: string): string {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* 패키지명과 replaceDeps 설정에서 watch scope 목록을 생성한다.
|
|
20
20
|
* - 패키지명의 scope (예: "@myapp/root" → "@myapp")
|
|
21
|
-
* - @simplysm
|
|
21
|
+
* - replaceDeps 키의 scope (예: "@simplysm/*" → "@simplysm")
|
|
22
22
|
* @param packageName 루트 package.json의 name 필드
|
|
23
|
+
* @param replaceDeps sd.config.ts의 replaceDeps 설정 (키: glob 패턴, 값: 소스 경로)
|
|
23
24
|
* @returns scope 배열 (중복 제거)
|
|
24
25
|
*/
|
|
25
|
-
export function getWatchScopes(
|
|
26
|
-
|
|
26
|
+
export function getWatchScopes(
|
|
27
|
+
packageName: string,
|
|
28
|
+
replaceDeps?: Record<string, string>,
|
|
29
|
+
): string[] {
|
|
30
|
+
const scopes = new Set<string>();
|
|
27
31
|
const match = packageName.match(/^(@[^/]+)\//);
|
|
28
32
|
if (match != null) {
|
|
29
33
|
scopes.add(match[1]);
|
|
30
34
|
}
|
|
35
|
+
if (replaceDeps != null) {
|
|
36
|
+
for (const pattern of Object.keys(replaceDeps)) {
|
|
37
|
+
const depMatch = pattern.match(/^(@[^/]+)\//);
|
|
38
|
+
if (depMatch != null) {
|
|
39
|
+
scopes.add(depMatch[1]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
31
43
|
return [...scopes];
|
|
32
44
|
}
|
|
33
45
|
|
|
@@ -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);
|