@simplysm/sd-cli 12.9.1 → 12.9.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,232 @@
1
+ #!/usr/bin/env node
2
+
3
+ import yargs from "yargs";
4
+ import { hideBin } from "yargs/helpers";
5
+ import { SdCliProject } from "./entry/sd-cli-project";
6
+ import { SdLogger, SdLoggerSeverity } from "@simplysm/sd-core-node";
7
+ import { EventEmitter } from "events";
8
+ import { SdCliElectron } from "./entry/sd-cli-electron";
9
+ import { SdCliLocalUpdate } from "./entry/sd-cli-local-update";
10
+ import { SdCliCordova } from "./entry/sd-cli-cordova";
11
+ import { SdCliAiCommand } from "./entry/sd-cli-ai-command";
12
+ import { SdCliPostinstall } from "./entry/sd-cli-postinstall";
13
+
14
+ Error.stackTraceLimit = Infinity;
15
+ EventEmitter.defaultMaxListeners = 0;
16
+
17
+ const argv = (await yargs(hideBin(process.argv))
18
+ .help("help", "도움말")
19
+ .alias("help", "h")
20
+ .options({
21
+ debug: {
22
+ type: "boolean",
23
+ describe: "디버그 로그를 표시할 것인지 여부",
24
+ default: false,
25
+ },
26
+ })
27
+ .command("local-update", "로컬 라이브러리 업데이트를 수행합니다.", (cmd) =>
28
+ cmd
29
+ .version(false)
30
+ .hide("help")
31
+ .hide("debug")
32
+ .options({
33
+ config: {
34
+ string: true,
35
+ describe: "simplysm.js 파일 경로",
36
+ },
37
+ options: {
38
+ string: true,
39
+ array: true,
40
+ describe: "옵션 설정",
41
+ },
42
+ }),
43
+ )
44
+ .command("watch", "프로젝트의 각 패키지에 대한 변경감지 빌드를 수행합니다.", (cmd) =>
45
+ cmd
46
+ .version(false)
47
+ .hide("help")
48
+ .hide("debug")
49
+ .options({
50
+ config: {
51
+ string: true,
52
+ describe: "simplysm.js 파일 경로",
53
+ },
54
+ options: {
55
+ string: true,
56
+ array: true,
57
+ describe: "옵션 설정",
58
+ },
59
+ packages: {
60
+ string: true,
61
+ array: true,
62
+ describe: "수행할 패키지 설정",
63
+ },
64
+ inspects: {
65
+ string: true,
66
+ array: true,
67
+ describe: "크롬 inspect를 수행할 패키지 설정",
68
+ },
69
+ }),
70
+ )
71
+ .command("build", "프로젝트의 각 패키지에 대한 빌드를 수행합니다.", (cmd) =>
72
+ cmd
73
+ .version(false)
74
+ .hide("help")
75
+ .hide("debug")
76
+ .options({
77
+ config: {
78
+ string: true,
79
+ describe: "simplysm.js 파일 경로",
80
+ },
81
+ options: {
82
+ string: true,
83
+ array: true,
84
+ describe: "옵션 설정",
85
+ },
86
+ packages: {
87
+ string: true,
88
+ array: true,
89
+ describe: "수행할 패키지 설정",
90
+ },
91
+ }),
92
+ )
93
+ .command("publish", "프로젝트의 각 패키지를 배포합니다.", (cmd) =>
94
+ cmd
95
+ .version(false)
96
+ .hide("help")
97
+ .hide("debug")
98
+ .options({
99
+ noBuild: {
100
+ type: "boolean",
101
+ describe: "빌드를 하지않고 배포합니다.",
102
+ default: false,
103
+ },
104
+ config: {
105
+ type: "string",
106
+ describe: "simplysm.js 파일 경로",
107
+ },
108
+ options: {
109
+ type: "string",
110
+ array: true,
111
+ describe: "옵션 설정",
112
+ },
113
+ packages: {
114
+ type: "string",
115
+ array: true,
116
+ describe: "수행할 패키지 설정",
117
+ },
118
+ }),
119
+ )
120
+ .command("run-electron <package>", "변경감지중인 플랫폼을 ELECTRON 앱 형태로 띄웁니다.", (cmd) =>
121
+ cmd
122
+ .positional("package", {
123
+ type: "string",
124
+ describe: "패키지명",
125
+ demandOption: true,
126
+ })
127
+ .options({
128
+ config: {
129
+ type: "string",
130
+ describe: "simplysm.js 파일 경로",
131
+ },
132
+ options: {
133
+ type: "string",
134
+ array: true,
135
+ describe: "옵션 설정",
136
+ },
137
+ }),
138
+ )
139
+ .command("build-electron-for-dev <package>", "변경감지중인 플랫폼을 ELECTRON 앱 형태로 띄웁니다.", (cmd) =>
140
+ cmd
141
+ .positional("package", {
142
+ type: "string",
143
+ describe: "패키지명",
144
+ demandOption: true,
145
+ })
146
+ .options({
147
+ config: {
148
+ type: "string",
149
+ describe: "simplysm.js 파일 경로",
150
+ },
151
+ options: {
152
+ type: "string",
153
+ array: true,
154
+ describe: "옵션 설정",
155
+ },
156
+ }),
157
+ )
158
+ .command(
159
+ "run-cordova <platform> <package> [url]",
160
+ "변경감지중인 플랫폼을 코도바 디바이스에 앱 형태로 띄웁니다.",
161
+ (cmd) =>
162
+ cmd
163
+ .positional("platform", {
164
+ type: "string",
165
+ describe: "빌드 플랫폼(android,...)",
166
+ demandOption: true,
167
+ })
168
+ .positional("package", {
169
+ type: "string",
170
+ describe: "패키지명",
171
+ demandOption: true,
172
+ })
173
+ .positional("url", {
174
+ type: "string",
175
+ describe: "Webview로 오픈할 URL",
176
+ demandOption: true,
177
+ }),
178
+ )
179
+ .command(
180
+ "commit",
181
+ "AI를 통해 변경사항에 대한 커밋 메시지를 작성하여, 커밋 및 푸쉬를 수행합니다.",
182
+ )
183
+ .command(
184
+ "postinstall",
185
+ "설치후 자동실행할 작업",
186
+ )
187
+ .parseAsync()) as any;
188
+
189
+ if (Boolean(argv.debug)) {
190
+ process.env["SD_DEBUG"] = "true";
191
+ SdLogger.setConfig({
192
+ console: {
193
+ level: SdLoggerSeverity.debug,
194
+ },
195
+ });
196
+ }
197
+ else {
198
+ SdLogger.setConfig({
199
+ dot: true,
200
+ });
201
+ }
202
+
203
+ if (argv._[0] === "local-update") {
204
+ await SdCliLocalUpdate.runAsync(argv);
205
+ }
206
+ else if (argv._[0] === "watch") {
207
+ await SdCliProject.watchAsync(argv);
208
+ }
209
+ else if (argv._[0] === "build") {
210
+ await SdCliProject.buildAsync(argv);
211
+ }
212
+ else if (argv._[0] === "publish") {
213
+ await SdCliProject.publishAsync(argv);
214
+ }
215
+ else if (argv._[0] === "run-electron") {
216
+ await SdCliElectron.runAsync(argv);
217
+ }
218
+ else if (argv._[0] === "build-electron-for-dev") {
219
+ await SdCliElectron.buildForDevAsync(argv);
220
+ }
221
+ else if (argv._[0] === "run-cordova") {
222
+ await SdCliCordova.runWebviewOnDeviceAsync(argv);
223
+ }
224
+ else if (argv._[0] === "commit") {
225
+ await SdCliAiCommand.commitAsync();
226
+ }
227
+ else if (argv._[0] === "postinstall") {
228
+ SdCliPostinstall.run();
229
+ }
230
+ else {
231
+ throw new Error(`명령어가 잘못 되었습니다.\n\t${argv._[0]}\n`);
232
+ }
package/src/sd-cli.ts CHANGED
@@ -1,232 +1,68 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import yargs from "yargs";
4
- import { hideBin } from "yargs/helpers";
5
- import { SdCliProject } from "./entry/sd-cli-project";
6
- import { SdLogger, SdLoggerSeverity } from "@simplysm/sd-core-node";
7
- import { EventEmitter } from "events";
8
- import { SdCliElectron } from "./entry/sd-cli-electron";
9
- import { SdCliLocalUpdate } from "./entry/sd-cli-local-update";
10
- import { SdCliCordova } from "./entry/sd-cli-cordova";
11
- import { SdCliAiCommand } from "./entry/sd-cli-ai-command";
12
- import { SdCliPostinstall } from "./entry/sd-cli-postinstall";
3
+ // import { spawn } from "node:child_process";
4
+ // import { fileURLToPath } from "node:url";
5
+ // import { dirname, join } from "node:path";
6
+ //
7
+ // const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ // const cliPath = join(__dirname, "../dist/sd-cli.js");
9
+ //
10
+ // spawn("node", ["--import=specifier-resolution-node/register", cliPath, ...process.argv.slice(2)], {
11
+ // stdio: "inherit"
12
+ // });
13
13
 
14
- Error.stackTraceLimit = Infinity;
15
- EventEmitter.defaultMaxListeners = 0;
14
+ import { exec, spawn } from "child_process";
15
+ import { fileURLToPath } from "node:url";
16
+ import os from "os";
17
+ import path from "path";
16
18
 
17
- const argv = (await yargs(hideBin(process.argv))
18
- .help("help", "도움말")
19
- .alias("help", "h")
20
- .options({
21
- debug: {
22
- type: "boolean",
23
- describe: "디버그 로그를 표시할 것인지 여부",
24
- default: false,
25
- },
26
- })
27
- .command("local-update", "로컬 라이브러리 업데이트를 수행합니다.", (cmd) =>
28
- cmd
29
- .version(false)
30
- .hide("help")
31
- .hide("debug")
32
- .options({
33
- config: {
34
- string: true,
35
- describe: "simplysm.js 파일 경로",
36
- },
37
- options: {
38
- string: true,
39
- array: true,
40
- describe: "옵션 설정",
41
- },
42
- }),
43
- )
44
- .command("watch", "프로젝트의 각 패키지에 대한 변경감지 빌드를 수행합니다.", (cmd) =>
45
- cmd
46
- .version(false)
47
- .hide("help")
48
- .hide("debug")
49
- .options({
50
- config: {
51
- string: true,
52
- describe: "simplysm.js 파일 경로",
53
- },
54
- options: {
55
- string: true,
56
- array: true,
57
- describe: "옵션 설정",
58
- },
59
- packages: {
60
- string: true,
61
- array: true,
62
- describe: "수행할 패키지 설정",
63
- },
64
- inspects: {
65
- string: true,
66
- array: true,
67
- describe: "크롬 inspect를 수행할 패키지 설정",
68
- },
69
- }),
70
- )
71
- .command("build", "프로젝트의 각 패키지에 대한 빌드를 수행합니다.", (cmd) =>
72
- cmd
73
- .version(false)
74
- .hide("help")
75
- .hide("debug")
76
- .options({
77
- config: {
78
- string: true,
79
- describe: "simplysm.js 파일 경로",
80
- },
81
- options: {
82
- string: true,
83
- array: true,
84
- describe: "옵션 설정",
85
- },
86
- packages: {
87
- string: true,
88
- array: true,
89
- describe: "수행할 패키지 설정",
90
- },
91
- }),
92
- )
93
- .command("publish", "프로젝트의 각 패키지를 배포합니다.", (cmd) =>
94
- cmd
95
- .version(false)
96
- .hide("help")
97
- .hide("debug")
98
- .options({
99
- noBuild: {
100
- type: "boolean",
101
- describe: "빌드를 하지않고 배포합니다.",
102
- default: false,
103
- },
104
- config: {
105
- type: "string",
106
- describe: "simplysm.js 파일 경로",
107
- },
108
- options: {
109
- type: "string",
110
- array: true,
111
- describe: "옵션 설정",
112
- },
113
- packages: {
114
- type: "string",
115
- array: true,
116
- describe: "수행할 패키지 설정",
117
- },
118
- }),
119
- )
120
- .command("run-electron <package>", "변경감지중인 플랫폼을 ELECTRON 앱 형태로 띄웁니다.", (cmd) =>
121
- cmd
122
- .positional("package", {
123
- type: "string",
124
- describe: "패키지명",
125
- demandOption: true,
126
- })
127
- .options({
128
- config: {
129
- type: "string",
130
- describe: "simplysm.js 파일 경로",
131
- },
132
- options: {
133
- type: "string",
134
- array: true,
135
- describe: "옵션 설정",
136
- },
137
- }),
138
- )
139
- .command("build-electron-for-dev <package>", "변경감지중인 플랫폼을 ELECTRON 앱 형태로 띄웁니다.", (cmd) =>
140
- cmd
141
- .positional("package", {
142
- type: "string",
143
- describe: "패키지명",
144
- demandOption: true,
145
- })
146
- .options({
147
- config: {
148
- type: "string",
149
- describe: "simplysm.js 파일 경로",
150
- },
151
- options: {
152
- type: "string",
153
- array: true,
154
- describe: "옵션 설정",
155
- },
156
- }),
157
- )
158
- .command(
159
- "run-cordova <platform> <package> [url]",
160
- "변경감지중인 플랫폼을 코도바 디바이스에 앱 형태로 띄웁니다.",
161
- (cmd) =>
162
- cmd
163
- .positional("platform", {
164
- type: "string",
165
- describe: "빌드 플랫폼(android,...)",
166
- demandOption: true,
167
- })
168
- .positional("package", {
169
- type: "string",
170
- describe: "패키지명",
171
- demandOption: true,
172
- })
173
- .positional("url", {
174
- type: "string",
175
- describe: "Webview로 오픈할 URL",
176
- demandOption: true,
177
- }),
178
- )
179
- .command(
180
- "commit",
181
- "AI를 통해 변경사항에 대한 커밋 메시지를 작성하여, 커밋 및 푸쉬를 수행합니다.",
182
- )
183
- .command(
184
- "postinstall",
185
- "설치후 자동실행할 작업",
186
- )
187
- .parseAsync()) as any;
188
19
 
189
- if (Boolean(argv.debug)) {
190
- process.env["SD_DEBUG"] = "true";
191
- SdLogger.setConfig({
192
- console: {
193
- level: SdLoggerSeverity.debug,
194
- },
20
+ const cpuCount = os.cpus().length; // 논리 CPU 수
21
+ const affinityMaskDecimal = Math.pow(2, cpuCount) - 2; // 전체 - 1
22
+ const affinityMask = "0x" + affinityMaskDecimal.toString(16).toUpperCase();
23
+
24
+ // console.log(`Logical CPU Count: ${cpuCount}`);
25
+ // console.log(`Affinity Mask (전체 - 1): ${affinityMask}`);
26
+
27
+ // CLI 실행
28
+ const cliPath = import.meta.resolve("./sd-cli-entry");
29
+
30
+ if (path.extname(cliPath) === ".ts") {
31
+ const pid = process.pid;
32
+ const command = `powershell -Command "$p = Get-Process -Id ${pid}; $p.ProcessorAffinity = ${affinityMask}"`;
33
+ exec(command, (err) => {
34
+ if (err) {
35
+ console.error("Affinity 설정 실패:", err.message);
36
+ }
37
+ else {
38
+ console.log(`Affinity ${affinityMask} 적용됨 (PID: ${pid})`);
39
+ }
195
40
  });
41
+ await import(cliPath);
196
42
  }
197
43
  else {
198
- SdLogger.setConfig({
199
- dot: true,
44
+ const child = spawn(
45
+ "node",
46
+ ["--import=specifier-resolution-node/register", fileURLToPath(cliPath), ...process.argv.slice(2)],
47
+ );
48
+
49
+ child.on("spawn", () => {
50
+ const pid = child.pid;
51
+
52
+ const psCommand = `$p = Get-Process -Id ${pid}; $p.ProcessorAffinity = ${affinityMask}`;
53
+ const command = `powershell -Command "${psCommand}"`;
54
+
55
+ exec(command, (err) => {
56
+ if (err) {
57
+ console.error("Failed to set affinity:", err.message);
58
+ }
59
+ else {
60
+ console.log(`Affinity set to ${affinityMask} for PID ${pid}`);
61
+ }
62
+ });
200
63
  });
201
- }
202
64
 
203
- if (argv._[0] === "local-update") {
204
- await SdCliLocalUpdate.runAsync(argv);
205
- }
206
- else if (argv._[0] === "watch") {
207
- await SdCliProject.watchAsync(argv);
208
- }
209
- else if (argv._[0] === "build") {
210
- await SdCliProject.buildAsync(argv);
211
- }
212
- else if (argv._[0] === "publish") {
213
- await SdCliProject.publishAsync(argv);
214
- }
215
- else if (argv._[0] === "run-electron") {
216
- await SdCliElectron.runAsync(argv);
217
- }
218
- else if (argv._[0] === "build-electron-for-dev") {
219
- await SdCliElectron.buildForDevAsync(argv);
220
- }
221
- else if (argv._[0] === "run-cordova") {
222
- await SdCliCordova.runWebviewOnDeviceAsync(argv);
223
- }
224
- else if (argv._[0] === "commit") {
225
- await SdCliAiCommand.commitAsync();
226
- }
227
- else if (argv._[0] === "postinstall") {
228
- SdCliPostinstall.run();
229
- }
230
- else {
231
- throw new Error(`명령어가 잘못 되었습니다.\n\t${argv._[0]}\n`);
232
- }
65
+ child.on("exit", (code) => {
66
+ console.log(`CLI exited with code ${code}`);
67
+ });
68
+ }
@@ -225,7 +225,7 @@ export class SdTsCompiler {
225
225
  this._lintAsync(prepareResult),
226
226
  ]);
227
227
 
228
- this._debug(`빌드 완료됨`, this._perf.toString());
228
+ this._log(`빌드 완료됨`, this._perf.toString());
229
229
  this._debug(`영향 받은 파일: ${prepareResult.affectedFileSet.size}개`);
230
230
  this._debug(`감시 중인 파일: ${prepareResult.watchFileSet.size}개`);
231
231
 
@@ -586,6 +586,9 @@ ${affectedFileTree.map(item => getTreeText(item)).join("\n")}`.trim());
586
586
  private _debug(...msg: any[]): void {
587
587
  this._logger.debug(`[${path.basename(this._opt.pkgPath)}]`, ...msg);
588
588
  }
589
+ private _log(...msg: any[]): void {
590
+ this._logger.log(`[${path.basename(this._opt.pkgPath)}]`, ...msg);
591
+ }
589
592
  }
590
593
 
591
594
 
@@ -1,42 +1,62 @@
1
1
  export class SdCliPerformanceTimer {
2
- private _startingMap = new Map<string, number>();
3
- private _resultMap = new Map<string, number>();
2
+ private _startingMap = new Map<string, { time: number; cpu: NodeJS.CpuUsage }>();
3
+ private _resultMap = new Map<string, { time: number; cpu: number }>();
4
4
 
5
- constructor(private _name: string) {}
5
+ constructor(private _name: string) {
6
+ }
6
7
 
7
8
  start(name: string) {
8
- this._startingMap.set(name, new Date().getTime());
9
+ this._startingMap.set(name, {
10
+ time: new Date().getTime(),
11
+ cpu: process.cpuUsage(),
12
+ });
9
13
  }
10
14
 
11
15
  end(name: string) {
12
- const val = this._startingMap.get(name);
13
- if (val == null) throw new Error();
14
- this._resultMap.set(name, new Date().getTime() - val);
16
+ const start = this._startingMap.get(name);
17
+ if (start == null) throw new Error(`No start record for '${name}'`);
18
+
19
+ const time = new Date().getTime() - start.time;
20
+ const cpuUsage = process.cpuUsage(start.cpu);
21
+ const cpu = (cpuUsage.user + cpuUsage.system) / 1000; // μs -> ms
22
+
23
+ this._resultMap.set(name, { time, cpu });
15
24
  this._startingMap.delete(name);
16
25
  }
17
26
 
18
27
  run<R>(name: string, fn: () => R): R {
19
28
  const startTime = new Date().getTime();
20
- let res = fn();
29
+ const startCpu = process.cpuUsage();
30
+
31
+ const finish = (res: R, start: number) => {
32
+ const duration = new Date().getTime() - start;
33
+ const cpu = (process.cpuUsage(startCpu).user + process.cpuUsage(startCpu).system) / 1000;
34
+
35
+ const prev = this._resultMap.get(name);
36
+ this._resultMap.set(name, {
37
+ time: (prev?.time ?? 0) + duration,
38
+ cpu: (prev?.cpu ?? 0) + cpu,
39
+ });
40
+
41
+ return res;
42
+ };
43
+
44
+ const res = fn();
21
45
  if (res instanceof Promise) {
22
- return res.then((realRes) => {
23
- const duration = new Date().getTime() - startTime;
24
- this._resultMap.update(name, (v) => (v ?? 0) + duration);
25
- return realRes;
26
- }) as R;
46
+ return res.then(realRes => finish(realRes, startTime)) as R;
27
47
  }
28
48
 
29
- const duration = new Date().getTime() - startTime;
30
- this._resultMap.update(name, (v) => (v ?? 0) + duration);
31
- return res;
49
+ return finish(res, startTime);
32
50
  }
33
51
 
34
52
  toString() {
35
53
  return `${this._name} 성능 보고서
36
54
  ------------------------------------
37
55
  ${Array.from(this._resultMap.entries())
38
- .map((en) => `${en[0]}: ${en[1].toLocaleString()}ms`)
39
- .join("\n")}
56
+ .map(([key, val]) =>
57
+ `${key}: ${val.time.toLocaleString()}ms (${val.cpu.toLocaleString()}ms CPU)`,
58
+ )
59
+ .join("\n")}
40
60
  ------------------------------------`;
41
61
  }
42
62
  }
package/bin/sd-cli.js DELETED
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawn } from "node:child_process";
4
- import { fileURLToPath } from "node:url";
5
- import { dirname, join } from "node:path";
6
-
7
- const __dirname = dirname(fileURLToPath(import.meta.url));
8
- const cliPath = join(__dirname, "../dist/sd-cli.js");
9
-
10
- spawn("node", ["--import=specifier-resolution-node/register", cliPath, ...process.argv.slice(2)], {
11
- stdio: "inherit"
12
- });