@simplysm/sd-cli 13.0.0-beta.46 → 13.0.0-beta.47

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 (102) hide show
  1. package/README.md +3 -3
  2. package/dist/builders/BaseBuilder.js.map +0 -1
  3. package/dist/builders/DtsBuilder.js.map +0 -1
  4. package/dist/builders/LibraryBuilder.js.map +0 -1
  5. package/dist/builders/index.js.map +0 -1
  6. package/dist/builders/types.js.map +0 -1
  7. package/dist/capacitor/capacitor.js.map +0 -1
  8. package/dist/commands/add-client.js.map +0 -1
  9. package/dist/commands/add-server.js.map +0 -1
  10. package/dist/commands/build.js.map +0 -1
  11. package/dist/commands/dev.js.map +0 -1
  12. package/dist/commands/device.js.map +0 -1
  13. package/dist/commands/init.js.map +0 -1
  14. package/dist/commands/lint.js.map +0 -1
  15. package/dist/commands/publish.js.map +0 -1
  16. package/dist/commands/typecheck.js.map +0 -1
  17. package/dist/commands/watch.js.map +0 -1
  18. package/dist/electron/electron.js.map +0 -1
  19. package/dist/index.js.map +0 -1
  20. package/dist/infra/ResultCollector.js.map +0 -1
  21. package/dist/infra/SignalHandler.js.map +0 -1
  22. package/dist/infra/WorkerManager.js.map +0 -1
  23. package/dist/infra/index.js.map +0 -1
  24. package/dist/orchestrators/WatchOrchestrator.js.map +0 -1
  25. package/dist/orchestrators/index.js.map +0 -1
  26. package/dist/sd-cli.js.map +0 -1
  27. package/dist/sd-config.types.js.map +0 -1
  28. package/dist/utils/build-env.js.map +0 -1
  29. package/dist/utils/config-editor.js.map +0 -1
  30. package/dist/utils/copy-src.js.map +0 -1
  31. package/dist/utils/esbuild-config.d.ts +1 -0
  32. package/dist/utils/esbuild-config.d.ts.map +1 -1
  33. package/dist/utils/esbuild-config.js +2 -1
  34. package/dist/utils/esbuild-config.js.map +1 -2
  35. package/dist/utils/listr-manager.js.map +0 -1
  36. package/dist/utils/output-utils.js.map +0 -1
  37. package/dist/utils/package-utils.js.map +0 -1
  38. package/dist/utils/replace-deps.js.map +0 -1
  39. package/dist/utils/sd-config.js.map +0 -1
  40. package/dist/utils/spawn.js.map +0 -1
  41. package/dist/utils/tailwind-config-deps.js.map +0 -1
  42. package/dist/utils/template.js.map +0 -1
  43. package/dist/utils/tsconfig.js.map +0 -1
  44. package/dist/utils/typecheck-serialization.js.map +0 -1
  45. package/dist/utils/vite-config.js.map +0 -1
  46. package/dist/utils/worker-events.js.map +0 -1
  47. package/dist/workers/client.worker.js.map +0 -1
  48. package/dist/workers/dts.worker.js.map +0 -1
  49. package/dist/workers/library.worker.js.map +0 -1
  50. package/dist/workers/server-runtime.worker.js.map +0 -1
  51. package/dist/workers/server.worker.js.map +0 -1
  52. package/package.json +5 -4
  53. package/src/builders/BaseBuilder.ts +141 -0
  54. package/src/builders/DtsBuilder.ts +138 -0
  55. package/src/builders/LibraryBuilder.ts +161 -0
  56. package/src/builders/index.ts +4 -0
  57. package/src/builders/types.ts +55 -0
  58. package/src/capacitor/capacitor.ts +827 -0
  59. package/src/commands/add-client.ts +135 -0
  60. package/src/commands/add-server.ts +150 -0
  61. package/src/commands/build.ts +475 -0
  62. package/src/commands/dev.ts +602 -0
  63. package/src/commands/device.ts +151 -0
  64. package/src/commands/init.ts +104 -0
  65. package/src/commands/lint.ts +216 -0
  66. package/src/commands/publish.ts +836 -0
  67. package/src/commands/typecheck.ts +329 -0
  68. package/src/commands/watch.ts +38 -0
  69. package/src/electron/electron.ts +329 -0
  70. package/src/index.ts +1 -0
  71. package/src/infra/ResultCollector.ts +81 -0
  72. package/src/infra/SignalHandler.ts +52 -0
  73. package/src/infra/WorkerManager.ts +65 -0
  74. package/src/infra/index.ts +3 -0
  75. package/src/orchestrators/WatchOrchestrator.ts +211 -0
  76. package/src/orchestrators/index.ts +1 -0
  77. package/src/sd-cli.ts +307 -0
  78. package/src/sd-config.types.ts +271 -0
  79. package/src/utils/build-env.ts +12 -0
  80. package/src/utils/config-editor.ts +131 -0
  81. package/src/utils/copy-src.ts +60 -0
  82. package/src/utils/esbuild-config.ts +263 -0
  83. package/src/utils/listr-manager.ts +89 -0
  84. package/src/utils/output-utils.ts +61 -0
  85. package/src/utils/package-utils.ts +63 -0
  86. package/src/utils/replace-deps.ts +163 -0
  87. package/src/utils/sd-config.ts +44 -0
  88. package/src/utils/spawn.ts +79 -0
  89. package/src/utils/tailwind-config-deps.ts +95 -0
  90. package/src/utils/template.ts +51 -0
  91. package/src/utils/tsconfig.ts +111 -0
  92. package/src/utils/typecheck-serialization.ts +82 -0
  93. package/src/utils/vite-config.ts +184 -0
  94. package/src/utils/worker-events.ts +102 -0
  95. package/src/workers/client.worker.ts +236 -0
  96. package/src/workers/dts.worker.ts +416 -0
  97. package/src/workers/library.worker.ts +245 -0
  98. package/src/workers/server-runtime.worker.ts +154 -0
  99. package/src/workers/server.worker.ts +435 -0
  100. package/templates/add-client/__CLIENT__/package.json.hbs +1 -1
  101. package/templates/add-server/__SERVER__/package.json.hbs +2 -2
  102. package/templates/init/package.json.hbs +3 -3
@@ -0,0 +1,135 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { input, confirm } from "@inquirer/prompts";
4
+ import { consola } from "consola";
5
+ import { renderTemplateDir } from "../utils/template";
6
+ import { addPackageToSdConfig, addTailwindToEslintConfig } from "../utils/config-editor";
7
+ import { spawn } from "../utils/spawn";
8
+
9
+ //#region Types
10
+
11
+ /**
12
+ * Add-client 명령 옵션
13
+ */
14
+ export interface AddClientOptions {}
15
+
16
+ //#endregion
17
+
18
+ //#region Utilities
19
+
20
+ /**
21
+ * import.meta.dirname에서 상위로 올라가며 package.json을 찾아 패키지 루트를 반환한다.
22
+ */
23
+ function findPackageRoot(startDir: string): string {
24
+ let dir = startDir;
25
+ while (!fs.existsSync(path.join(dir, "package.json"))) {
26
+ const parent = path.dirname(dir);
27
+ if (parent === dir) throw new Error("package.json을 찾을 수 없습니다.");
28
+ dir = parent;
29
+ }
30
+ return dir;
31
+ }
32
+
33
+ //#endregion
34
+
35
+ //#region Main
36
+
37
+ /**
38
+ * 클라이언트 패키지를 프로젝트에 추가한다.
39
+ *
40
+ * 1. 프로젝트 루트 확인 (sd.config.ts 존재)
41
+ * 2. 대화형 프롬프트 (이름 접미사, 라우터 사용 여부)
42
+ * 3. 패키지 디렉토리 중복 확인
43
+ * 4. Handlebars 템플릿 렌더링
44
+ * 5. sd.config.ts에 패키지 항목 추가 (ts-morph)
45
+ * 6. eslint.config.ts에 tailwind 설정 추가 (첫 클라이언트인 경우)
46
+ * 7. pnpm install
47
+ */
48
+ export async function runAddClient(_options: AddClientOptions): Promise<void> {
49
+ const cwd = process.cwd();
50
+ const logger = consola.withTag("sd:cli:add-client");
51
+
52
+ // 1. 프로젝트 루트 확인
53
+ if (!fs.existsSync(path.join(cwd, "sd.config.ts"))) {
54
+ consola.error("sd.config.ts를 찾을 수 없습니다. 프로젝트 루트에서 실행해주세요.");
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+
59
+ // 프로젝트명
60
+ const projectName = path.basename(cwd);
61
+
62
+ // 2. 대화형 프롬프트
63
+ const clientSuffix = await input({
64
+ message: "클라이언트 이름을 입력하세요 (client-___):",
65
+ validate: (value) => {
66
+ if (!value.trim()) return "이름을 입력해주세요.";
67
+ if (!/^[a-z][a-z0-9-]*$/.test(value)) return "소문자, 숫자, 하이픈만 사용 가능합니다.";
68
+ return true;
69
+ },
70
+ });
71
+
72
+ const useRouter = await confirm({
73
+ message: "라우터를 사용하시겠습니까?",
74
+ default: true,
75
+ });
76
+
77
+ const clientName = `client-${clientSuffix}`;
78
+
79
+ // 3. 패키지 디렉토리 중복 확인
80
+ const packageDir = path.join(cwd, "packages", clientName);
81
+ if (fs.existsSync(packageDir)) {
82
+ consola.error(`packages/${clientName} 디렉토리가 이미 존재합니다.`);
83
+ process.exitCode = 1;
84
+ return;
85
+ }
86
+
87
+ // 4. 템플릿 렌더링
88
+ const pkgRoot = findPackageRoot(import.meta.dirname);
89
+ const templateDir = path.join(pkgRoot, "templates", "add-client");
90
+
91
+ const context = {
92
+ projectName,
93
+ clientSuffix,
94
+ clientName,
95
+ router: useRouter,
96
+ };
97
+
98
+ const dirReplacements = {
99
+ __CLIENT__: clientName,
100
+ };
101
+
102
+ logger.info(`${clientName} 패키지 생성 중...`);
103
+ await renderTemplateDir(templateDir, path.join(cwd, "packages"), context, dirReplacements);
104
+ logger.success(`packages/${clientName} 생성 완료`);
105
+
106
+ // 5. sd.config.ts 업데이트
107
+ const sdConfigPath = path.join(cwd, "sd.config.ts");
108
+ const added = addPackageToSdConfig(sdConfigPath, clientName, { target: "client" });
109
+ if (added) {
110
+ logger.success("sd.config.ts 업데이트 완료");
111
+ } else {
112
+ consola.warn(`"${clientName}"이(가) sd.config.ts에 이미 존재합니다.`);
113
+ }
114
+
115
+ // 6. eslint.config.ts tailwind 설정 추가 (첫 클라이언트인 경우)
116
+ const eslintConfigPath = path.join(cwd, "eslint.config.ts");
117
+ if (fs.existsSync(eslintConfigPath)) {
118
+ const tailwindAdded = addTailwindToEslintConfig(eslintConfigPath, clientName);
119
+ if (tailwindAdded) {
120
+ logger.success("eslint.config.ts에 tailwind 설정 추가");
121
+ }
122
+ }
123
+
124
+ // 7. pnpm install
125
+ logger.info("pnpm install 실행 중...");
126
+ await spawn("pnpm", ["install"], { cwd });
127
+ logger.success("pnpm install 완료");
128
+
129
+ // 완료
130
+ consola.box(
131
+ [`클라이언트 "${clientName}"이(가) 추가되었습니다!`, "", ` pnpm dev ${clientName} 개발 서버 실행`].join("\n"),
132
+ );
133
+ }
134
+
135
+ //#endregion
@@ -0,0 +1,150 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { input, checkbox } from "@inquirer/prompts";
4
+ import { consola } from "consola";
5
+ import { renderTemplateDir } from "../utils/template";
6
+ import { addPackageToSdConfig, setClientServerInSdConfig } from "../utils/config-editor";
7
+ import { spawn } from "../utils/spawn";
8
+
9
+ //#region Types
10
+
11
+ /**
12
+ * Add-server 명령 옵션
13
+ */
14
+ export interface AddServerOptions {}
15
+
16
+ //#endregion
17
+
18
+ //#region Utilities
19
+
20
+ /**
21
+ * import.meta.dirname에서 상위로 올라가며 package.json을 찾아 패키지 루트를 반환한다.
22
+ */
23
+ function findPackageRoot(startDir: string): string {
24
+ let dir = startDir;
25
+ while (!fs.existsSync(path.join(dir, "package.json"))) {
26
+ const parent = path.dirname(dir);
27
+ if (parent === dir) throw new Error("package.json을 찾을 수 없습니다.");
28
+ dir = parent;
29
+ }
30
+ return dir;
31
+ }
32
+
33
+ /**
34
+ * sd.config.ts를 읽어서 target이 "client"인 패키지명 목록을 반환한다.
35
+ */
36
+ function findClientPackages(sdConfigPath: string): string[] {
37
+ const content = fs.readFileSync(sdConfigPath, "utf-8");
38
+ const clients: string[] = [];
39
+
40
+ // 간단한 패턴 매칭으로 client 패키지 찾기
41
+ const regex = /"([^"]+)":\s*\{[^}]*target:\s*"client"/g;
42
+ let match: RegExpExecArray | null;
43
+ while ((match = regex.exec(content)) != null) {
44
+ clients.push(match[1]);
45
+ }
46
+ return clients;
47
+ }
48
+
49
+ //#endregion
50
+
51
+ //#region Main
52
+
53
+ /**
54
+ * 서버 패키지를 프로젝트에 추가한다.
55
+ *
56
+ * 1. 프로젝트 루트 확인
57
+ * 2. 대화형 프롬프트 (이름 접미사, 클라이언트 선택)
58
+ * 3. 패키지 디렉토리 중복 확인
59
+ * 4. Handlebars 템플릿 렌더링
60
+ * 5. sd.config.ts에 서버 패키지 항목 추가
61
+ * 6. 선택된 클라이언트의 server 필드 업데이트
62
+ * 7. pnpm install
63
+ */
64
+ export async function runAddServer(_options: AddServerOptions): Promise<void> {
65
+ const cwd = process.cwd();
66
+ const logger = consola.withTag("sd:cli:add-server");
67
+
68
+ // 1. 프로젝트 루트 확인
69
+ const sdConfigPath = path.join(cwd, "sd.config.ts");
70
+ if (!fs.existsSync(sdConfigPath)) {
71
+ consola.error("sd.config.ts를 찾을 수 없습니다. 프로젝트 루트에서 실행해주세요.");
72
+ process.exitCode = 1;
73
+ return;
74
+ }
75
+
76
+ const projectName = path.basename(cwd);
77
+
78
+ // 2. 대화형 프롬프트
79
+ const serverSuffix = await input({
80
+ message: '서버 이름 접미사 (비워두면 "server"):',
81
+ validate: (value) => {
82
+ if (value.trim() === "") return true; // 빈 값 허용
83
+ if (!/^[a-z][a-z0-9-]*$/.test(value)) return "소문자, 숫자, 하이픈만 사용 가능합니다.";
84
+ return true;
85
+ },
86
+ });
87
+
88
+ const serverName = serverSuffix.trim() === "" ? "server" : `server-${serverSuffix}`;
89
+
90
+ // 클라이언트 선택 (기존 클라이언트가 있는 경우)
91
+ const clientPackages = findClientPackages(sdConfigPath);
92
+ let selectedClients: string[] = [];
93
+
94
+ if (clientPackages.length > 0) {
95
+ selectedClients = await checkbox({
96
+ message: "이 서버가 서비스할 클라이언트를 선택하세요:",
97
+ choices: clientPackages.map((name) => ({ name, value: name })),
98
+ });
99
+ }
100
+
101
+ // 3. 패키지 디렉토리 중복 확인
102
+ const packageDir = path.join(cwd, "packages", serverName);
103
+ if (fs.existsSync(packageDir)) {
104
+ consola.error(`packages/${serverName} 디렉토리가 이미 존재합니다.`);
105
+ process.exitCode = 1;
106
+ return;
107
+ }
108
+
109
+ // 4. 템플릿 렌더링
110
+ const pkgRoot = findPackageRoot(import.meta.dirname);
111
+ const templateDir = path.join(pkgRoot, "templates", "add-server");
112
+
113
+ const context = {
114
+ projectName,
115
+ serverName,
116
+ port: 3000,
117
+ };
118
+
119
+ const dirReplacements = {
120
+ __SERVER__: serverName,
121
+ };
122
+
123
+ logger.info(`${serverName} 패키지 생성 중...`);
124
+ await renderTemplateDir(templateDir, path.join(cwd, "packages"), context, dirReplacements);
125
+ logger.success(`packages/${serverName} 생성 완료`);
126
+
127
+ // 5. sd.config.ts에 서버 패키지 추가
128
+ const added = addPackageToSdConfig(sdConfigPath, serverName, { target: "server" });
129
+ if (added) {
130
+ logger.success("sd.config.ts에 서버 패키지 추가 완료");
131
+ } else {
132
+ consola.warn(`"${serverName}"이(가) sd.config.ts에 이미 존재합니다.`);
133
+ }
134
+
135
+ // 6. 선택된 클라이언트의 server 필드 업데이트
136
+ for (const clientName of selectedClients) {
137
+ setClientServerInSdConfig(sdConfigPath, clientName, serverName);
138
+ logger.info(`${clientName}의 서버를 "${serverName}"으로 설정`);
139
+ }
140
+
141
+ // 7. pnpm install
142
+ logger.info("pnpm install 실행 중...");
143
+ await spawn("pnpm", ["install"], { cwd });
144
+ logger.success("pnpm install 완료");
145
+
146
+ // 완료
147
+ consola.box(`서버 "${serverName}"이(가) 추가되었습니다!`);
148
+ }
149
+
150
+ //#endregion