@simplysm/sd-cli 13.0.8 → 13.0.11

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.
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env node
2
+
3
+ // side-effect: Map/Array 프로토타입 확장 (getOrCreate 등)
4
+ import "@simplysm/core-common";
5
+ import yargs, { type Argv } from "yargs";
6
+ import { hideBin } from "yargs/helpers";
7
+ import { runLint } from "./commands/lint";
8
+ import { runTypecheck } from "./commands/typecheck";
9
+ import { runWatch } from "./commands/watch";
10
+ import { runDev } from "./commands/dev";
11
+ import { runBuild } from "./commands/build";
12
+ import { runPublish } from "./commands/publish";
13
+ import { runReplaceDeps } from "./commands/replace-deps";
14
+ import { runDevice } from "./commands/device";
15
+ import path from "path";
16
+ import fs from "fs";
17
+ import { fileURLToPath } from "url";
18
+ import { EventEmitter } from "node:events";
19
+ import { consola, LogLevels } from "consola";
20
+
21
+ Error.stackTraceLimit = Infinity;
22
+ EventEmitter.defaultMaxListeners = 100;
23
+
24
+ /**
25
+ * CLI 파서를 생성한다.
26
+ * @internal 테스트용으로 export
27
+ */
28
+ export function createCliParser(argv: string[]): Argv {
29
+ return yargs(argv)
30
+ .help("help", "도움말")
31
+ .alias("help", "h")
32
+ .option("debug", {
33
+ type: "boolean",
34
+ describe: "debug 로그 출력",
35
+ default: false,
36
+ global: true,
37
+ })
38
+ .middleware((args) => {
39
+ if (args.debug) consola.level = LogLevels.debug;
40
+ })
41
+ .command(
42
+ "lint [targets..]",
43
+ "ESLint + Stylelint를 실행한다.",
44
+ (cmd) =>
45
+ cmd
46
+ .version(false)
47
+ .hide("help")
48
+ .positional("targets", {
49
+ type: "string",
50
+ array: true,
51
+ describe: "린트할 경로 (예: packages/core-common, tests/orm)",
52
+ default: [],
53
+ })
54
+ .options({
55
+ fix: {
56
+ type: "boolean",
57
+ describe: "자동 수정",
58
+ default: false,
59
+ },
60
+ timing: {
61
+ type: "boolean",
62
+ describe: "규칙별 실행 시간 출력",
63
+ default: false,
64
+ },
65
+ }),
66
+ async (args) => {
67
+ await runLint({
68
+ targets: args.targets,
69
+ fix: args.fix,
70
+ timing: args.timing,
71
+ });
72
+ },
73
+ )
74
+ .command(
75
+ "typecheck [targets..]",
76
+ "TypeScript 타입체크를 실행한다.",
77
+ (cmd) =>
78
+ cmd
79
+ .version(false)
80
+ .hide("help")
81
+ .positional("targets", {
82
+ type: "string",
83
+ array: true,
84
+ describe: "타입체크할 경로 (예: packages/core-common, tests/orm)",
85
+ default: [],
86
+ })
87
+ .options({
88
+ options: {
89
+ type: "string",
90
+ array: true,
91
+ alias: "o",
92
+ description: "sd.config.ts에 전달할 옵션 (예: -o key=value)",
93
+ default: [] as string[],
94
+ },
95
+ }),
96
+ async (args) => {
97
+ await runTypecheck({
98
+ targets: args.targets,
99
+ options: args.options,
100
+ });
101
+ },
102
+ )
103
+ .command(
104
+ "watch [targets..]",
105
+ "패키지를 watch 모드로 빌드한다.",
106
+ (cmd) =>
107
+ cmd
108
+ .version(false)
109
+ .hide("help")
110
+ .positional("targets", {
111
+ type: "string",
112
+ array: true,
113
+ describe: "watch할 패키지 (예: solid, solid-demo)",
114
+ default: [],
115
+ })
116
+ .options({
117
+ options: {
118
+ type: "string",
119
+ array: true,
120
+ alias: "o",
121
+ description: "sd.config.ts에 전달할 옵션 (예: -o key=value)",
122
+ default: [] as string[],
123
+ },
124
+ }),
125
+ async (args) => {
126
+ await runWatch({
127
+ targets: args.targets,
128
+ options: args.options,
129
+ });
130
+ },
131
+ )
132
+ .command(
133
+ "dev [targets..]",
134
+ "Client와 Server 패키지를 개발 모드로 실행한다.",
135
+ (cmd) =>
136
+ cmd
137
+ .version(false)
138
+ .hide("help")
139
+ .positional("targets", {
140
+ type: "string",
141
+ array: true,
142
+ describe: "실행할 패키지 (예: solid-demo)",
143
+ default: [],
144
+ })
145
+ .options({
146
+ options: {
147
+ type: "string",
148
+ array: true,
149
+ alias: "o",
150
+ description: "sd.config.ts에 전달할 옵션 (예: -o key=value)",
151
+ default: [] as string[],
152
+ },
153
+ }),
154
+ async (args) => {
155
+ await runDev({
156
+ targets: args.targets,
157
+ options: args.options,
158
+ });
159
+ },
160
+ )
161
+ .command(
162
+ "build [targets..]",
163
+ "프로덕션 빌드를 실행한다.",
164
+ (cmd) =>
165
+ cmd
166
+ .version(false)
167
+ .hide("help")
168
+ .positional("targets", {
169
+ type: "string",
170
+ array: true,
171
+ describe: "빌드할 패키지 (예: solid, core-common)",
172
+ default: [],
173
+ })
174
+ .options({
175
+ options: {
176
+ type: "string",
177
+ array: true,
178
+ alias: "o",
179
+ description: "sd.config.ts에 전달할 옵션 (예: -o key=value)",
180
+ default: [] as string[],
181
+ },
182
+ }),
183
+ async (args) => {
184
+ await runBuild({
185
+ targets: args.targets,
186
+ options: args.options,
187
+ });
188
+ },
189
+ )
190
+ .command(
191
+ "device",
192
+ "Android 디바이스에서 앱을 실행한다.",
193
+ (cmd) =>
194
+ cmd
195
+ .version(false)
196
+ .hide("help")
197
+ .options({
198
+ package: {
199
+ type: "string",
200
+ alias: "p",
201
+ describe: "패키지 이름",
202
+ demandOption: true,
203
+ },
204
+ url: {
205
+ type: "string",
206
+ alias: "u",
207
+ describe: "개발 서버 URL (미지정 시 sd.config.ts의 server 설정 사용)",
208
+ },
209
+ options: {
210
+ type: "string",
211
+ array: true,
212
+ alias: "o",
213
+ description: "sd.config.ts에 전달할 옵션 (예: -o key=value)",
214
+ default: [] as string[],
215
+ },
216
+ }),
217
+ async (args) => {
218
+ await runDevice({
219
+ package: args.package,
220
+ url: args.url,
221
+ options: args.options,
222
+ });
223
+ },
224
+ )
225
+ .command(
226
+ "init",
227
+ "새 프로젝트를 초기화한다.",
228
+ (cmd) => cmd.version(false).hide("help"),
229
+ async () => {
230
+ const { runInit } = await import("./commands/init.js");
231
+ await runInit({});
232
+ },
233
+ )
234
+ .command("add", "프로젝트에 패키지를 추가한다.", (cmd) =>
235
+ cmd
236
+ .version(false)
237
+ .hide("help")
238
+ .command(
239
+ "client",
240
+ "클라이언트 패키지를 추가한다.",
241
+ (subCmd) => subCmd.version(false).hide("help"),
242
+ async () => {
243
+ const { runAddClient } = await import("./commands/add-client.js");
244
+ await runAddClient({});
245
+ },
246
+ )
247
+ .command(
248
+ "server",
249
+ "서버 패키지를 추가한다.",
250
+ (subCmd) => subCmd.version(false).hide("help"),
251
+ async () => {
252
+ const { runAddServer } = await import("./commands/add-server.js");
253
+ await runAddServer({});
254
+ },
255
+ )
256
+ .demandCommand(1, "패키지 타입을 지정해주세요. (client, server)"),
257
+ )
258
+ .command(
259
+ "publish [targets..]",
260
+ "패키지를 배포한다.",
261
+ (cmd) =>
262
+ cmd
263
+ .version(false)
264
+ .hide("help")
265
+ .positional("targets", {
266
+ type: "string",
267
+ array: true,
268
+ describe: "배포할 패키지 (예: solid, core-common)",
269
+ default: [],
270
+ })
271
+ .options({
272
+ "build": {
273
+ type: "boolean",
274
+ describe: "빌드 실행 (--no-build로 스킵)",
275
+ default: true,
276
+ },
277
+ "dry-run": {
278
+ type: "boolean",
279
+ describe: "실제 배포 없이 시뮬레이션",
280
+ default: false,
281
+ },
282
+ "options": {
283
+ type: "string",
284
+ array: true,
285
+ alias: "o",
286
+ description: "sd.config.ts에 전달할 옵션 (예: -o key=value)",
287
+ default: [] as string[],
288
+ },
289
+ }),
290
+ async (args) => {
291
+ await runPublish({
292
+ targets: args.targets,
293
+ noBuild: !args.build,
294
+ dryRun: args.dryRun,
295
+ options: args.options,
296
+ });
297
+ },
298
+ )
299
+ .command(
300
+ "replace-deps",
301
+ "sd.config.ts의 replaceDeps 설정에 따라 node_modules 패키지를 로컬 소스로 symlink 교체한다.",
302
+ (cmd) =>
303
+ cmd
304
+ .version(false)
305
+ .hide("help")
306
+ .options({
307
+ options: {
308
+ type: "string",
309
+ array: true,
310
+ alias: "o",
311
+ description: "sd.config.ts에 전달할 옵션 (예: -o key=value)",
312
+ default: [] as string[],
313
+ },
314
+ }),
315
+ async (args) => {
316
+ await runReplaceDeps({
317
+ options: args.options,
318
+ });
319
+ },
320
+ )
321
+ .demandCommand(1, "명령어를 지정해주세요.")
322
+ .strict();
323
+ }
324
+
325
+ // CLI로 직접 실행될 때만 파싱 수행
326
+ // ESM에서 메인 모듈 판별: import.meta.url과 process.argv[1]을 정규화하여 비교
327
+ const cliEntryPath = process.argv.at(1);
328
+ if (cliEntryPath != null && fileURLToPath(import.meta.url) === fs.realpathSync(path.resolve(cliEntryPath))) {
329
+ await createCliParser(hideBin(process.argv)).parse();
330
+ }