@simplysm/sd-cli 13.0.66 → 13.0.68

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 (133) hide show
  1. package/dist/builders/BaseBuilder.d.ts.map +1 -1
  2. package/dist/builders/BaseBuilder.js +2 -7
  3. package/dist/builders/BaseBuilder.js.map +1 -1
  4. package/dist/builders/DtsBuilder.d.ts.map +1 -1
  5. package/dist/builders/DtsBuilder.js +4 -3
  6. package/dist/builders/DtsBuilder.js.map +1 -1
  7. package/dist/builders/LibraryBuilder.d.ts.map +1 -1
  8. package/dist/builders/LibraryBuilder.js +2 -1
  9. package/dist/builders/LibraryBuilder.js.map +1 -1
  10. package/dist/capacitor/capacitor.js +2 -2
  11. package/dist/capacitor/capacitor.js.map +1 -1
  12. package/dist/commands/add-client.js +2 -2
  13. package/dist/commands/add-server.js +2 -2
  14. package/dist/commands/build.d.ts +2 -10
  15. package/dist/commands/build.d.ts.map +1 -1
  16. package/dist/commands/build.js +1 -5
  17. package/dist/commands/build.js.map +1 -1
  18. package/dist/commands/check.d.ts.map +1 -1
  19. package/dist/commands/check.js +26 -37
  20. package/dist/commands/check.js.map +1 -1
  21. package/dist/commands/dev.d.ts +2 -9
  22. package/dist/commands/dev.d.ts.map +1 -1
  23. package/dist/commands/dev.js +1 -5
  24. package/dist/commands/dev.js.map +1 -1
  25. package/dist/commands/init.js +5 -5
  26. package/dist/commands/publish.js +16 -16
  27. package/dist/commands/publish.js.map +1 -1
  28. package/dist/commands/typecheck.d.ts +0 -1
  29. package/dist/commands/typecheck.d.ts.map +1 -1
  30. package/dist/commands/typecheck.js +5 -5
  31. package/dist/commands/typecheck.js.map +1 -1
  32. package/dist/commands/watch.d.ts +2 -8
  33. package/dist/commands/watch.d.ts.map +1 -1
  34. package/dist/commands/watch.js +1 -5
  35. package/dist/commands/watch.js.map +1 -1
  36. package/dist/electron/electron.js +2 -2
  37. package/dist/electron/electron.js.map +1 -1
  38. package/dist/infra/ResultCollector.d.ts +0 -21
  39. package/dist/infra/ResultCollector.d.ts.map +1 -1
  40. package/dist/infra/ResultCollector.js +0 -31
  41. package/dist/infra/ResultCollector.js.map +1 -1
  42. package/dist/orchestrators/BuildOrchestrator.d.ts +0 -1
  43. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  44. package/dist/orchestrators/BuildOrchestrator.js +10 -19
  45. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  46. package/dist/orchestrators/DevOrchestrator.d.ts +12 -0
  47. package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
  48. package/dist/orchestrators/DevOrchestrator.js +178 -167
  49. package/dist/orchestrators/DevOrchestrator.js.map +1 -1
  50. package/dist/sd-cli-entry.js +14 -14
  51. package/dist/sd-cli-entry.js.map +1 -1
  52. package/dist/sd-cli.js +8 -13
  53. package/dist/sd-cli.js.map +1 -1
  54. package/dist/utils/output-utils.d.ts +4 -7
  55. package/dist/utils/output-utils.d.ts.map +1 -1
  56. package/dist/utils/output-utils.js +13 -5
  57. package/dist/utils/output-utils.js.map +1 -1
  58. package/dist/utils/package-utils.d.ts +0 -11
  59. package/dist/utils/package-utils.d.ts.map +1 -1
  60. package/dist/utils/package-utils.js.map +1 -1
  61. package/dist/utils/replace-deps.d.ts.map +1 -1
  62. package/dist/utils/replace-deps.js +21 -48
  63. package/dist/utils/replace-deps.js.map +1 -1
  64. package/dist/utils/sd-config.d.ts +2 -6
  65. package/dist/utils/sd-config.d.ts.map +1 -1
  66. package/dist/utils/sd-config.js.map +1 -1
  67. package/dist/utils/vite-config.d.ts.map +1 -1
  68. package/dist/utils/vite-config.js +7 -1
  69. package/dist/utils/vite-config.js.map +1 -1
  70. package/dist/utils/worker-events.d.ts +5 -5
  71. package/dist/utils/worker-events.d.ts.map +1 -1
  72. package/dist/utils/worker-events.js +14 -17
  73. package/dist/utils/worker-events.js.map +1 -1
  74. package/dist/utils/worker-utils.d.ts +7 -0
  75. package/dist/utils/worker-utils.d.ts.map +1 -1
  76. package/dist/utils/worker-utils.js +10 -0
  77. package/dist/utils/worker-utils.js.map +1 -1
  78. package/dist/workers/client.worker.d.ts.map +1 -1
  79. package/dist/workers/client.worker.js +11 -9
  80. package/dist/workers/client.worker.js.map +1 -1
  81. package/dist/workers/dts.worker.d.ts +4 -4
  82. package/dist/workers/dts.worker.d.ts.map +1 -1
  83. package/dist/workers/dts.worker.js +10 -11
  84. package/dist/workers/dts.worker.js.map +1 -1
  85. package/dist/workers/library.worker.d.ts.map +1 -1
  86. package/dist/workers/library.worker.js +7 -9
  87. package/dist/workers/library.worker.js.map +1 -1
  88. package/dist/workers/server-runtime.worker.d.ts.map +1 -1
  89. package/dist/workers/server-runtime.worker.js +4 -3
  90. package/dist/workers/server-runtime.worker.js.map +1 -1
  91. package/dist/workers/server.worker.d.ts.map +1 -1
  92. package/dist/workers/server.worker.js +10 -13
  93. package/dist/workers/server.worker.js.map +1 -1
  94. package/package.json +5 -4
  95. package/src/builders/BaseBuilder.ts +2 -7
  96. package/src/builders/DtsBuilder.ts +4 -3
  97. package/src/builders/LibraryBuilder.ts +2 -1
  98. package/src/capacitor/capacitor.ts +2 -2
  99. package/src/commands/add-client.ts +2 -2
  100. package/src/commands/add-server.ts +2 -2
  101. package/src/commands/build.ts +2 -17
  102. package/src/commands/check.ts +31 -44
  103. package/src/commands/dev.ts +2 -16
  104. package/src/commands/init.ts +5 -5
  105. package/src/commands/publish.ts +16 -16
  106. package/src/commands/typecheck.ts +5 -5
  107. package/src/commands/watch.ts +2 -15
  108. package/src/electron/electron.ts +2 -2
  109. package/src/infra/ResultCollector.ts +0 -36
  110. package/src/orchestrators/BuildOrchestrator.ts +12 -21
  111. package/src/orchestrators/DevOrchestrator.ts +221 -201
  112. package/src/sd-cli-entry.ts +14 -14
  113. package/src/sd-cli.ts +9 -14
  114. package/src/utils/output-utils.ts +15 -11
  115. package/src/utils/package-utils.ts +0 -12
  116. package/src/utils/replace-deps.ts +61 -88
  117. package/src/utils/sd-config.ts +2 -6
  118. package/src/utils/vite-config.ts +9 -1
  119. package/src/utils/worker-events.ts +22 -25
  120. package/src/utils/worker-utils.ts +16 -0
  121. package/src/workers/client.worker.ts +12 -11
  122. package/src/workers/dts.worker.ts +13 -15
  123. package/src/workers/library.worker.ts +7 -10
  124. package/src/workers/server-runtime.worker.ts +4 -3
  125. package/src/workers/server.worker.ts +10 -14
  126. package/templates/add-client/__CLIENT__/package.json.hbs +1 -1
  127. package/templates/add-server/__SERVER__/package.json.hbs +2 -2
  128. package/templates/init/package.json.hbs +3 -3
  129. package/dist/utils/spawn.d.ts +0 -26
  130. package/dist/utils/spawn.d.ts.map +0 -1
  131. package/dist/utils/spawn.js +0 -50
  132. package/dist/utils/spawn.js.map +0 -6
  133. package/src/utils/spawn.ts +0 -80
@@ -1,6 +1,7 @@
1
1
  import path from "path";
2
2
  import esbuild from "esbuild";
3
3
  import { createWorker, FsWatcher } from "@simplysm/core-node";
4
+ import { errorMessage } from "@simplysm/core-common";
4
5
  import { consola } from "consola";
5
6
  import type { SdBuildPackageConfig } from "../sd-config.types";
6
7
  import {
@@ -13,7 +14,7 @@ import {
13
14
  getTypecheckEnvFromTarget,
14
15
  writeChangedOutputFiles,
15
16
  } from "../utils/esbuild-config";
16
- import { registerCleanupHandlers } from "../utils/worker-utils";
17
+ import { registerCleanupHandlers, createOnceGuard } from "../utils/worker-utils";
17
18
 
18
19
  //#region Types
19
20
 
@@ -146,13 +147,12 @@ async function build(info: LibraryBuildInfo): Promise<LibraryBuildResult> {
146
147
  } catch (err) {
147
148
  return {
148
149
  success: false,
149
- errors: [err instanceof Error ? err.message : String(err)],
150
+ errors: [errorMessage(err)],
150
151
  };
151
152
  }
152
153
  }
153
154
 
154
- /** startWatch 호출 여부 플래그 */
155
- let isWatchStarted = false;
155
+ const guardStartWatch = createOnceGuard("startWatch");
156
156
 
157
157
  /**
158
158
  * esbuild context 생성 및 초기 빌드 수행
@@ -232,10 +232,7 @@ async function createAndBuildContext(
232
232
  * @throws 이미 watch가 시작된 경우
233
233
  */
234
234
  async function startWatch(info: LibraryWatchInfo): Promise<void> {
235
- if (isWatchStarted) {
236
- throw new Error("startWatch는 Worker당 한 번만 호출할 수 있습니다.");
237
- }
238
- isWatchStarted = true;
235
+ guardStartWatch();
239
236
 
240
237
  try {
241
238
  // 첫 번째 빌드 완료 대기를 위한 Promise
@@ -284,13 +281,13 @@ async function startWatch(info: LibraryWatchInfo): Promise<void> {
284
281
  }
285
282
  } catch (err) {
286
283
  sender.send("error", {
287
- message: err instanceof Error ? err.message : String(err),
284
+ message: errorMessage(err),
288
285
  });
289
286
  }
290
287
  });
291
288
  } catch (err) {
292
289
  sender.send("error", {
293
- message: err instanceof Error ? err.message : String(err),
290
+ message: errorMessage(err),
294
291
  });
295
292
  }
296
293
  }
@@ -1,5 +1,6 @@
1
1
  import proxy from "@fastify/http-proxy";
2
2
  import { createWorker } from "@simplysm/core-node";
3
+ import { errorMessage } from "@simplysm/core-common";
3
4
  import { consola } from "consola";
4
5
  import net from "net";
5
6
  import { pathToFileURL } from "url";
@@ -60,14 +61,14 @@ async function cleanup(): Promise<void> {
60
61
  process.on("uncaughtException", (err) => {
61
62
  logger.error("Server Runtime 미처리 오류", err);
62
63
  sender.send("error", {
63
- message: err instanceof Error ? err.message : String(err),
64
+ message: errorMessage(err),
64
65
  });
65
66
  });
66
67
 
67
68
  process.on("unhandledRejection", (reason) => {
68
69
  logger.error("Server Runtime 미처리 Promise 거부", reason);
69
70
  sender.send("error", {
70
- message: reason instanceof Error ? reason.message : String(reason),
71
+ message: errorMessage(reason),
71
72
  });
72
73
  });
73
74
 
@@ -144,7 +145,7 @@ async function start(info: ServerRuntimeStartInfo): Promise<void> {
144
145
  } catch (err) {
145
146
  logger.error("Server Runtime 시작 실패", err);
146
147
  sender.send("error", {
147
- message: err instanceof Error ? err.message : String(err),
148
+ message: errorMessage(err),
148
149
  });
149
150
  }
150
151
  }
@@ -1,8 +1,9 @@
1
1
  import path from "path";
2
2
  import fs from "fs";
3
- import cp from "child_process";
3
+ import { execaSync } from "execa";
4
4
  import esbuild from "esbuild";
5
5
  import { createWorker, FsWatcher, pathNorm } from "@simplysm/core-node";
6
+ import { errorMessage } from "@simplysm/core-common";
6
7
  import { consola } from "consola";
7
8
  import {
8
9
  parseRootTsconfig,
@@ -15,7 +16,7 @@ import {
15
16
  collectNativeModuleExternals,
16
17
  writeChangedOutputFiles,
17
18
  } from "../utils/esbuild-config";
18
- import { registerCleanupHandlers } from "../utils/worker-utils";
19
+ import { registerCleanupHandlers, createOnceGuard } from "../utils/worker-utils";
19
20
  import { collectDeps } from "../utils/package-utils";
20
21
  import { copyPublicFiles, watchPublicFiles } from "../utils/copy-public";
21
22
 
@@ -196,7 +197,7 @@ function generateProductionFiles(info: ServerBuildInfo, externals: string[]): vo
196
197
  distPkgJson["dependencies"] = deps;
197
198
  }
198
199
  if (info.packageManager === "volta") {
199
- const nodeVersion = cp.execSync("node -v").toString().trim();
200
+ const nodeVersion = execaSync("node", ["-v"]).stdout.trim();
200
201
  distPkgJson["volta"] = { node: nodeVersion };
201
202
  }
202
203
  fs.writeFileSync(path.join(distDir, "package.json"), JSON.stringify(distPkgJson, undefined, 2));
@@ -268,8 +269,7 @@ function generateProductionFiles(info: ServerBuildInfo, externals: string[]): vo
268
269
  : ` interpreter: cp.execSync("mise which node").toString().trim(),\n`;
269
270
 
270
271
  const pm2Config = [
271
- `const cp = require("child_process");`,
272
- ``,
272
+ ...(info.packageManager !== "volta" ? [`const cp = require("child_process");`, ``] : []),
273
273
  `module.exports = {`,
274
274
  ` name: ${JSON.stringify(pm2Name)},`,
275
275
  ` script: "main.js",`,
@@ -353,13 +353,12 @@ async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
353
353
  return {
354
354
  success: false,
355
355
  mainJsPath,
356
- errors: [err instanceof Error ? err.message : String(err)],
356
+ errors: [errorMessage(err)],
357
357
  };
358
358
  }
359
359
  }
360
360
 
361
- /** startWatch 호출 여부 플래그 */
362
- let isWatchStarted = false;
361
+ const guardStartWatch = createOnceGuard("startWatch");
363
362
 
364
363
  /**
365
364
  * esbuild context 생성 및 초기 빌드 수행
@@ -455,10 +454,7 @@ async function createAndBuildContext(
455
454
  * @throws 이미 watch가 시작된 경우
456
455
  */
457
456
  async function startWatch(info: ServerWatchInfo): Promise<void> {
458
- if (isWatchStarted) {
459
- throw new Error("startWatch는 Worker당 한 번만 호출할 수 있습니다.");
460
- }
461
- isWatchStarted = true;
457
+ guardStartWatch();
462
458
 
463
459
  try {
464
460
  // 첫 번째 빌드 완료 대기를 위한 Promise
@@ -543,13 +539,13 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
543
539
  }
544
540
  } catch (err) {
545
541
  sender.send("error", {
546
- message: err instanceof Error ? err.message : String(err),
542
+ message: errorMessage(err),
547
543
  });
548
544
  }
549
545
  });
550
546
  } catch (err) {
551
547
  sender.send("error", {
552
- message: err instanceof Error ? err.message : String(err),
548
+ message: errorMessage(err),
553
549
  });
554
550
  }
555
551
  }
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "dependencies": {
7
- "@simplysm/solid": "~13.0.66",
7
+ "@simplysm/solid": "~13.0.68",
8
8
  {{#if router}}
9
9
  "@solidjs/router": "^0.15.4",
10
10
  {{/if}}
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "dependencies": {
7
- "@simplysm/core-common": "~13.0.66",
8
- "@simplysm/service-server": "~13.0.66"
7
+ "@simplysm/core-common": "~13.0.68",
8
+ "@simplysm/service-server": "~13.0.68"
9
9
  }
10
10
  }
@@ -15,9 +15,9 @@
15
15
  "vitest": "vitest"
16
16
  },
17
17
  "devDependencies": {
18
- "@simplysm/sd-cli": "~13.0.66",
19
- "@simplysm/sd-claude": "~13.0.66",
20
- "@simplysm/lint": "~13.0.66",
18
+ "@simplysm/sd-cli": "~13.0.68",
19
+ "@simplysm/sd-claude": "~13.0.68",
20
+ "@simplysm/lint": "~13.0.68",
21
21
  "@types/node": "^20.19.33",
22
22
  "eslint": "^9.39.2",
23
23
  "prettier": "^3.8.1",
@@ -1,26 +0,0 @@
1
- /**
2
- * spawn 옵션
3
- */
4
- export interface SpawnOptions {
5
- /** 작업 디렉토리 */
6
- cwd?: string;
7
- /** 환경변수 (process.env와 병합) */
8
- env?: Record<string, string | undefined>;
9
- /** 색상 출력 강제 여부 (기본값: NO_COLOR 환경변수 존중) */
10
- forceColor?: boolean;
11
- }
12
- /**
13
- * child_process.spawn을 Promise로 래핑한 함수.
14
- *
15
- * - stdout/stderr를 캡처하여 반환
16
- * - NO_COLOR 환경변수 존중 (forceColor로 오버라이드 가능)
17
- * - 종료 코드가 0이 아니면 에러 throw
18
- * - Windows에서는 shell: true (.cmd 파일 실행 필요), 그 외는 shell: false
19
- *
20
- * @param cmd - 실행할 명령어
21
- * @param args - 명령어 인자
22
- * @param options - 실행 옵션
23
- * @returns stdout 출력 (stderr는 stdout에 병합됨)
24
- */
25
- export declare function spawn(cmd: string, args: string[], options?: SpawnOptions): Promise<string>;
26
- //# sourceMappingURL=spawn.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["..\\..\\src\\utils\\spawn.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,cAAc;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAoDhG"}
@@ -1,50 +0,0 @@
1
- import { spawn as cpSpawn } from "child_process";
2
- async function spawn(cmd, args, options) {
3
- return new Promise((resolve, reject) => {
4
- const noColor = process.env["NO_COLOR"] != null;
5
- const useColor = options?.forceColor ?? !noColor;
6
- const colorEnv = useColor ? {
7
- FORCE_COLOR: "1",
8
- CLICOLOR_FORCE: "1",
9
- COLORTERM: "truecolor"
10
- } : {};
11
- const spawnOptions = {
12
- cwd: options?.cwd,
13
- env: {
14
- ...process.env,
15
- ...colorEnv,
16
- ...options?.env
17
- },
18
- // Windows에서 npm, pnpm 등은 .cmd 파일이므로 shell을 통해 실행해야 함
19
- shell: process.platform === "win32",
20
- stdio: ["ignore", "pipe", "pipe"]
21
- };
22
- const child = cpSpawn(cmd, args, spawnOptions);
23
- let output = "";
24
- child.stdout?.on("data", (data) => {
25
- output += data.toString();
26
- });
27
- child.stderr?.on("data", (data) => {
28
- output += data.toString();
29
- });
30
- child.on("error", (err) => {
31
- reject(new Error(`spawn \uC2E4\uD328 (${cmd}): ${err.message}`));
32
- });
33
- child.on("close", (code) => {
34
- if (code === 0) {
35
- resolve(output);
36
- } else {
37
- reject(
38
- new Error(`\uBA85\uB839\uC5B4 \uC2E4\uD328 (${cmd} ${args.join(" ")})
39
- \uC885\uB8CC \uCF54\uB4DC: ${code}
40
- \uCD9C\uB825:
41
- ${output}`)
42
- );
43
- }
44
- });
45
- });
46
- }
47
- export {
48
- spawn
49
- };
50
- //# sourceMappingURL=spawn.js.map
@@ -1,6 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/utils/spawn.ts"],
4
- "mappings": "AAAA,SAAS,SAAS,eAAoD;AA2BtE,eAAsB,MAAM,KAAa,MAAgB,SAAyC;AAChG,SAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAE9C,UAAM,UAAU,QAAQ,IAAI,UAAU,KAAK;AAC3C,UAAM,WAAW,SAAS,cAAc,CAAC;AAEzC,UAAM,WAAW,WACb;AAAA,MACE,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,WAAW;AAAA,IACb,IACA,CAAC;AAEL,UAAM,eAA+B;AAAA,MACnC,KAAK,SAAS;AAAA,MACd,KAAK;AAAA,QACH,GAAG,QAAQ;AAAA,QACX,GAAG;AAAA,QACH,GAAG,SAAS;AAAA,MACd;AAAA;AAAA,MAEA,OAAO,QAAQ,aAAa;AAAA,MAC5B,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,IAClC;AAEA,UAAM,QAAQ,QAAQ,KAAK,MAAM,YAAY;AAE7C,QAAI,SAAS;AAEb,UAAM,QAAQ,GAAG,QAAQ,CAAC,SAAiB;AACzC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,UAAM,QAAQ,GAAG,QAAQ,CAAC,SAAiB;AACzC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,aAAO,IAAI,MAAM,uBAAa,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC;AAAA,IACvD,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,UAAI,SAAS,GAAG;AACd,gBAAQ,MAAM;AAAA,MAChB,OAAO;AACL;AAAA,UACE,IAAI,MAAM,oCAAW,GAAG,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,6BAAa,IAAI;AAAA;AAAA,EAAU,MAAM,EAAE;AAAA,QAC/E;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;",
5
- "names": []
6
- }
@@ -1,80 +0,0 @@
1
- import { spawn as cpSpawn, type SpawnOptions as CpSpawnOptions } from "child_process";
2
-
3
- /**
4
- * spawn 옵션
5
- */
6
- export interface SpawnOptions {
7
- /** 작업 디렉토리 */
8
- cwd?: string;
9
- /** 환경변수 (process.env와 병합) */
10
- env?: Record<string, string | undefined>;
11
- /** 색상 출력 강제 여부 (기본값: NO_COLOR 환경변수 존중) */
12
- forceColor?: boolean;
13
- }
14
-
15
- /**
16
- * child_process.spawn을 Promise로 래핑한 함수.
17
- *
18
- * - stdout/stderr를 캡처하여 반환
19
- * - NO_COLOR 환경변수 존중 (forceColor로 오버라이드 가능)
20
- * - 종료 코드가 0이 아니면 에러 throw
21
- * - Windows에서는 shell: true (.cmd 파일 실행 필요), 그 외는 shell: false
22
- *
23
- * @param cmd - 실행할 명령어
24
- * @param args - 명령어 인자
25
- * @param options - 실행 옵션
26
- * @returns stdout 출력 (stderr는 stdout에 병합됨)
27
- */
28
- export async function spawn(cmd: string, args: string[], options?: SpawnOptions): Promise<string> {
29
- return new Promise<string>((resolve, reject) => {
30
- // NO_COLOR 환경변수 존중 (https://no-color.org/)
31
- const noColor = process.env["NO_COLOR"] != null;
32
- const useColor = options?.forceColor ?? !noColor;
33
-
34
- const colorEnv = useColor
35
- ? {
36
- FORCE_COLOR: "1",
37
- CLICOLOR_FORCE: "1",
38
- COLORTERM: "truecolor",
39
- }
40
- : {};
41
-
42
- const spawnOptions: CpSpawnOptions = {
43
- cwd: options?.cwd,
44
- env: {
45
- ...process.env,
46
- ...colorEnv,
47
- ...options?.env,
48
- },
49
- // Windows에서 npm, pnpm 등은 .cmd 파일이므로 shell을 통해 실행해야 함
50
- shell: process.platform === "win32",
51
- stdio: ["ignore", "pipe", "pipe"],
52
- };
53
-
54
- const child = cpSpawn(cmd, args, spawnOptions);
55
-
56
- let output = "";
57
-
58
- child.stdout?.on("data", (data: Buffer) => {
59
- output += data.toString();
60
- });
61
-
62
- child.stderr?.on("data", (data: Buffer) => {
63
- output += data.toString();
64
- });
65
-
66
- child.on("error", (err) => {
67
- reject(new Error(`spawn 실패 (${cmd}): ${err.message}`));
68
- });
69
-
70
- child.on("close", (code) => {
71
- if (code === 0) {
72
- resolve(output);
73
- } else {
74
- reject(
75
- new Error(`명령어 실패 (${cmd} ${args.join(" ")})\n종료 코드: ${code}\n출력:\n${output}`),
76
- );
77
- }
78
- });
79
- });
80
- }