@simplysm/sd-cli 12.16.24 → 12.16.29

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.
@@ -16,6 +16,7 @@ export class SdCliCapacitor {
16
16
  this._logger.debug(`실행 명령: ${cmd + " " + args.join(" ")}`);
17
17
  const msg = await SdProcess.spawnAsync(cmd, args, {
18
18
  cwd,
19
+ shell: true,
19
20
  env: {
20
21
  FORCE_COLOR: "1", // chalk, supports-color 계열
21
22
  CLICOLOR_FORCE: "1", // 일부 Unix 도구
@@ -11,6 +11,12 @@ export declare class SdCliProject {
11
11
  options?: string[];
12
12
  packages?: string[];
13
13
  }): Promise<void>;
14
+ static checkAsync(opt: {
15
+ config: string;
16
+ options?: string[];
17
+ path?: string;
18
+ type?: "lint" | "typecheck";
19
+ }): Promise<void>;
14
20
  static publishAsync(opt: {
15
21
  config: string;
16
22
  options?: string[];
@@ -82,6 +82,69 @@ export class SdCliProject {
82
82
  });
83
83
  this._logging(messages, logger);
84
84
  }
85
+ static async checkAsync(opt) {
86
+ const logger = SdLogger.get(["simplysm", "sd-cli", "SdCliProject", "checkAsync"]);
87
+ logger.debug("프로젝트 설정 가져오기...");
88
+ const projConf = await loadProjConfAsync(process.cwd(), true, opt);
89
+ logger.debug("프로젝트 package.json 가져오기...");
90
+ const projNpmConf = (await FsUtils.readJsonAsync(path.resolve(process.cwd(), "package.json")));
91
+ logger.debug("패키지 목록 구성...");
92
+ if (!projNpmConf.workspaces) {
93
+ throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
94
+ }
95
+ const allPkgPaths = (await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtils.globAsync(item)))
96
+ .filter((item) => !item.includes("."))
97
+ .map((item) => PathUtils.norm(item));
98
+ logger.debug("패키지 존재 확인...");
99
+ const notExistsPkgs = Object.keys(projConf.packages).filter((pkgConfKey) => allPkgPaths.every((pkgPath) => path.basename(pkgPath) !== pkgConfKey));
100
+ if (notExistsPkgs.length > 0) {
101
+ throw new Error("패키지를 찾을 수 없습니다. (" + notExistsPkgs.join(", ") + ")");
102
+ }
103
+ // path 해석 → 대상 패키지 결정
104
+ let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
105
+ let filterFilePath;
106
+ if (opt.path != null) {
107
+ const inputPath = PathUtils.norm(path.resolve(process.cwd(), opt.path));
108
+ // 패키지 디렉토리인지 확인
109
+ const matchedPkg = pkgPaths.find((pkgPath) => inputPath === pkgPath);
110
+ if (matchedPkg) {
111
+ pkgPaths = [matchedPkg];
112
+ }
113
+ else {
114
+ // 파일 경로로 간주 → 해당 파일이 속한 패키지 찾기
115
+ const containingPkg = pkgPaths.find((pkgPath) => inputPath.startsWith(pkgPath + path.sep));
116
+ if (containingPkg) {
117
+ pkgPaths = [containingPkg];
118
+ filterFilePath = inputPath;
119
+ }
120
+ else {
121
+ throw new Error(`경로에 해당하는 패키지를 찾을 수 없습니다. (${opt.path})`);
122
+ }
123
+ }
124
+ }
125
+ logger.debug("체크 프로세스 시작...");
126
+ let messages = await SdProjectBuildRunner.buildAsync({
127
+ allPkgPaths,
128
+ pkgPaths,
129
+ projConf,
130
+ noEmit: true,
131
+ });
132
+ // --type 필터링
133
+ if (opt.type === "lint") {
134
+ messages = messages.filter((m) => m.type === "lint");
135
+ }
136
+ else if (opt.type === "typecheck") {
137
+ messages = messages.filter((m) => m.type === "compile");
138
+ }
139
+ // 파일 경로 필터링
140
+ if (filterFilePath != null) {
141
+ messages = messages.filter((m) => m.filePath === filterFilePath);
142
+ }
143
+ this._logging(messages, logger);
144
+ if (messages.some((m) => m.severity === "error")) {
145
+ process.exit(1);
146
+ }
147
+ }
85
148
  static async publishAsync(opt) {
86
149
  const logger = SdLogger.get(["simplysm", "sd-cli", "SdCliProject", "publishAsync"]);
87
150
  logger.debug("프로젝트 설정 가져오기...");
@@ -19,6 +19,7 @@ export declare class SdProjectBuildRunner {
19
19
  allPkgPaths: TNormPath[];
20
20
  pkgPaths: TNormPath[];
21
21
  projConf: ISdProjectConfig;
22
+ noEmit?: boolean;
22
23
  }): Promise<ISdBuildMessage[]>;
23
24
  private static _getScopePathSetAsync;
24
25
  private static _restartServerAsync;
@@ -209,7 +209,11 @@ export class SdProjectBuildRunner {
209
209
  });
210
210
  await worker.run("initialize", [
211
211
  {
212
- options: { pkgPath, scopePathSet },
212
+ options: {
213
+ pkgPath,
214
+ scopePathSet,
215
+ ...(opt.noEmit ? { watch: { dev: true, emitOnly: false, noEmit: true } } : {}),
216
+ },
213
217
  pkgConf,
214
218
  },
215
219
  ]);
@@ -104,6 +104,31 @@ await yargs(hideBin(process.argv))
104
104
  }), async (argv) => {
105
105
  await SdCliProject.buildAsync(argv);
106
106
  })
107
+ .command("check [path]", "타입체크 및 린트를 수행합니다.", (cmd) => cmd
108
+ .version(false)
109
+ .hide("help")
110
+ .hide("debug")
111
+ .positional("path", {
112
+ type: "string",
113
+ describe: "패키지 경로 또는 파일 경로",
114
+ })
115
+ .options({
116
+ config: {
117
+ type: "string",
118
+ describe: "설정 파일 경로",
119
+ default: "simplysm.js",
120
+ },
121
+ options: {
122
+ type: "string",
123
+ array: true,
124
+ describe: "옵션 설정",
125
+ },
126
+ type: {
127
+ type: "string",
128
+ choices: ["lint", "typecheck"],
129
+ describe: "체크 종류 (미지정 시 둘 다)",
130
+ },
131
+ }), async (argv) => await SdCliProject.checkAsync(argv))
107
132
  .command("publish", "프로젝트의 각 패키지를 배포합니다.", (cmd) => cmd
108
133
  .version(false)
109
134
  .hide("help")
@@ -35,7 +35,7 @@ export class SdCliPerformanceTimer {
35
35
  };
36
36
  const res = fn();
37
37
  if (res instanceof Promise) {
38
- return res.then(realRes => finish(realRes, startTime));
38
+ return res.then((realRes) => finish(realRes, startTime));
39
39
  }
40
40
  return finish(res, startTime);
41
41
  }
@@ -13,12 +13,10 @@ window.onload = () => {
13
13
 
14
14
  if (process.env["NODE_ENV"] === "production") {
15
15
  script.src = "cordova.js";
16
- }
17
- else {
16
+ } else {
18
17
  if (navigator.userAgent.toLowerCase().indexOf("android") !== -1) {
19
18
  script.src = "cordova-android/cordova.js";
20
- }
21
- else {
19
+ } else {
22
20
  script.src = "cordova-browser/cordova.js";
23
21
  }
24
22
  }
@@ -26,4 +24,4 @@ window.onload = () => {
26
24
  script.defer = true;
27
25
 
28
26
  document.body.appendChild(script);
29
- };
27
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/sd-cli",
3
- "version": "12.16.24",
3
+ "version": "12.16.29",
4
4
  "description": "심플리즘 패키지 - CLI",
5
5
  "author": "김석래",
6
6
  "repository": {
@@ -12,27 +12,27 @@
12
12
  "bin": "./dist/sd-cli.js",
13
13
  "type": "module",
14
14
  "dependencies": {
15
- "@angular/build": "^20.3.13",
16
- "@angular/compiler": "^20.3.15",
17
- "@angular/compiler-cli": "^20.3.15",
18
- "@anthropic-ai/sdk": "^0.71.2",
19
- "@electron/rebuild": "^4.0.2",
20
- "@simplysm/sd-core-common": "12.16.24",
21
- "@simplysm/sd-core-node": "12.16.24",
22
- "@simplysm/sd-service-server": "12.16.24",
23
- "@simplysm/sd-storage": "12.16.24",
15
+ "@angular/build": "^20.3.20",
16
+ "@angular/compiler": "^20.3.18",
17
+ "@angular/compiler-cli": "^20.3.18",
18
+ "@anthropic-ai/sdk": "^0.78.0",
19
+ "@electron/rebuild": "^4.0.3",
20
+ "@simplysm/sd-core-common": "12.16.29",
21
+ "@simplysm/sd-core-node": "12.16.29",
22
+ "@simplysm/sd-service-server": "12.16.29",
23
+ "@simplysm/sd-storage": "12.16.29",
24
24
  "browserslist": "^4.28.1",
25
25
  "cordova": "^13.0.0",
26
26
  "electron": "^33.4.11",
27
27
  "electron-builder": "^25.1.8",
28
28
  "esbuild": "0.25.9",
29
- "esbuild-sass-plugin": "^3.3.1",
30
- "eslint": "^9.39.2",
31
- "glob": "^13.0.0",
29
+ "esbuild-sass-plugin": "^3.7.0",
30
+ "eslint": "^9.39.4",
31
+ "glob": "^13.0.6",
32
32
  "node-stdlib-browser": "^1.3.1",
33
33
  "rxjs": "^7.8.2",
34
- "sass-embedded": "^1.97.0",
35
- "semver": "^7.7.3",
34
+ "sass-embedded": "^1.98.0",
35
+ "semver": "^7.7.4",
36
36
  "sharp": "^0.34.5",
37
37
  "specifier-resolution-node": "^1.1.4",
38
38
  "ts-morph": "^27.0.2",
@@ -26,6 +26,7 @@ export class SdCliCapacitor {
26
26
  this._logger.debug(`실행 명령: ${cmd + " " + args.join(" ")}`);
27
27
  const msg = await SdProcess.spawnAsync(cmd, args, {
28
28
  cwd,
29
+ shell: true,
29
30
  env: {
30
31
  FORCE_COLOR: "1", // chalk, supports-color 계열
31
32
  CLICOLOR_FORCE: "1", // 일부 Unix 도구
@@ -52,7 +52,11 @@ export class SdCliElectron {
52
52
  });
53
53
  }
54
54
 
55
- private static async _loadDevConfig(opt: { package: string; config: string; options?: string[] }) {
55
+ private static async _loadDevConfig(opt: {
56
+ package: string;
57
+ config: string;
58
+ options?: string[];
59
+ }) {
56
60
  const projConf = await loadProjConfAsync(process.cwd(), true, opt);
57
61
  const pkgConf = projConf.packages[opt.package];
58
62
  if (pkgConf?.type !== "client" || pkgConf.builder?.electron === undefined) {
@@ -122,6 +122,90 @@ export class SdCliProject {
122
122
  this._logging(messages, logger);
123
123
  }
124
124
 
125
+ static async checkAsync(opt: {
126
+ config: string;
127
+ options?: string[];
128
+ path?: string;
129
+ type?: "lint" | "typecheck";
130
+ }): Promise<void> {
131
+ const logger = SdLogger.get(["simplysm", "sd-cli", "SdCliProject", "checkAsync"]);
132
+
133
+ logger.debug("프로젝트 설정 가져오기...");
134
+ const projConf = await loadProjConfAsync(process.cwd(), true, opt);
135
+
136
+ logger.debug("프로젝트 package.json 가져오기...");
137
+ const projNpmConf = (await FsUtils.readJsonAsync(
138
+ path.resolve(process.cwd(), "package.json"),
139
+ )) as INpmConfig;
140
+
141
+ logger.debug("패키지 목록 구성...");
142
+ if (!projNpmConf.workspaces) {
143
+ throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
144
+ }
145
+ const allPkgPaths = (
146
+ await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtils.globAsync(item))
147
+ )
148
+ .filter((item) => !item.includes("."))
149
+ .map((item) => PathUtils.norm(item));
150
+
151
+ logger.debug("패키지 존재 확인...");
152
+ const notExistsPkgs = Object.keys(projConf.packages).filter((pkgConfKey) =>
153
+ allPkgPaths.every((pkgPath) => path.basename(pkgPath) !== pkgConfKey),
154
+ );
155
+ if (notExistsPkgs.length > 0) {
156
+ throw new Error("패키지를 찾을 수 없습니다. (" + notExistsPkgs.join(", ") + ")");
157
+ }
158
+
159
+ // path 해석 → 대상 패키지 결정
160
+ let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
161
+ let filterFilePath: string | undefined;
162
+
163
+ if (opt.path != null) {
164
+ const inputPath = PathUtils.norm(path.resolve(process.cwd(), opt.path));
165
+
166
+ // 패키지 디렉토리인지 확인
167
+ const matchedPkg = pkgPaths.find((pkgPath) => inputPath === pkgPath);
168
+ if (matchedPkg) {
169
+ pkgPaths = [matchedPkg];
170
+ } else {
171
+ // 파일 경로로 간주 → 해당 파일이 속한 패키지 찾기
172
+ const containingPkg = pkgPaths.find((pkgPath) => inputPath.startsWith(pkgPath + path.sep));
173
+ if (containingPkg) {
174
+ pkgPaths = [containingPkg];
175
+ filterFilePath = inputPath;
176
+ } else {
177
+ throw new Error(`경로에 해당하는 패키지를 찾을 수 없습니다. (${opt.path})`);
178
+ }
179
+ }
180
+ }
181
+
182
+ logger.debug("체크 프로세스 시작...");
183
+ let messages = await SdProjectBuildRunner.buildAsync({
184
+ allPkgPaths,
185
+ pkgPaths,
186
+ projConf,
187
+ noEmit: true,
188
+ });
189
+
190
+ // --type 필터링
191
+ if (opt.type === "lint") {
192
+ messages = messages.filter((m) => m.type === "lint");
193
+ } else if (opt.type === "typecheck") {
194
+ messages = messages.filter((m) => m.type === "compile");
195
+ }
196
+
197
+ // 파일 경로 필터링
198
+ if (filterFilePath != null) {
199
+ messages = messages.filter((m) => m.filePath === filterFilePath);
200
+ }
201
+
202
+ this._logging(messages, logger);
203
+
204
+ if (messages.some((m) => m.severity === "error")) {
205
+ process.exit(1);
206
+ }
207
+ }
208
+
125
209
  static async publishAsync(opt: {
126
210
  config: string;
127
211
  options?: string[];
@@ -300,6 +300,7 @@ export class SdProjectBuildRunner {
300
300
  allPkgPaths: TNormPath[];
301
301
  pkgPaths: TNormPath[];
302
302
  projConf: ISdProjectConfig;
303
+ noEmit?: boolean;
303
304
  }) {
304
305
  const scopePathSet = await this._getScopePathSetAsync(
305
306
  opt.allPkgPaths,
@@ -322,7 +323,11 @@ export class SdProjectBuildRunner {
322
323
 
323
324
  await worker.run("initialize", [
324
325
  {
325
- options: { pkgPath, scopePathSet },
326
+ options: {
327
+ pkgPath,
328
+ scopePathSet,
329
+ ...(opt.noEmit ? { watch: { dev: true, emitOnly: false, noEmit: true } } : {}),
330
+ },
326
331
  pkgConf,
327
332
  },
328
333
  ]);
@@ -15,7 +15,7 @@ export class SdClientBuildRunner extends SdBuildRunnerBase<"client"> {
15
15
 
16
16
  private _ngBundlers?: SdNgBundler[];
17
17
  private _cordova?: SdCliCordova;
18
- private _capacitor?: SdCliCapacitor
18
+ private _capacitor?: SdCliCapacitor;
19
19
 
20
20
  protected override async _runAsync(modifiedFileSet?: Set<TNormPath>): Promise<ISdBuildResult> {
21
21
  // 최초 한번
@@ -125,6 +125,37 @@ await yargs(hideBin(process.argv))
125
125
  await SdCliProject.buildAsync(argv);
126
126
  },
127
127
  )
128
+ .command(
129
+ "check [path]",
130
+ "타입체크 및 린트를 수행합니다.",
131
+ (cmd) =>
132
+ cmd
133
+ .version(false)
134
+ .hide("help")
135
+ .hide("debug")
136
+ .positional("path", {
137
+ type: "string",
138
+ describe: "패키지 경로 또는 파일 경로",
139
+ })
140
+ .options({
141
+ config: {
142
+ type: "string",
143
+ describe: "설정 파일 경로",
144
+ default: "simplysm.js",
145
+ },
146
+ options: {
147
+ type: "string",
148
+ array: true,
149
+ describe: "옵션 설정",
150
+ },
151
+ type: {
152
+ type: "string",
153
+ choices: ["lint", "typecheck"] as const,
154
+ describe: "체크 종류 (미지정 시 둘 다)",
155
+ },
156
+ }),
157
+ async (argv) => await SdCliProject.checkAsync(argv),
158
+ )
128
159
  .command(
129
160
  "publish",
130
161
  "프로젝트의 각 패키지를 배포합니다.",
@@ -103,7 +103,10 @@ export class SdTsCompiler {
103
103
  };
104
104
  }
105
105
 
106
- private _createCompilerHost(compilerOptions: ts.CompilerOptions, modifiedFileSet: Set<TNormPath>) {
106
+ private _createCompilerHost(
107
+ compilerOptions: ts.CompilerOptions,
108
+ modifiedFileSet: Set<TNormPath>,
109
+ ) {
107
110
  // 지식: SourceFile은 하나의 파일에만 국한된 정적 정보객체임, 변경된 파일의 SourceFile만 다시 생성하면됨
108
111
 
109
112
  const compilerHost = ts.createCompilerHost(compilerOptions);
@@ -2,8 +2,7 @@ export class SdCliPerformanceTimer {
2
2
  private readonly _startingMap = new Map<string, { time: number; cpu: NodeJS.CpuUsage }>();
3
3
  private readonly _resultMap = new Map<string, { time: number; cpu: number }>();
4
4
 
5
- constructor(private readonly _name: string) {
6
- }
5
+ constructor(private readonly _name: string) {}
7
6
 
8
7
  start(name: string) {
9
8
  this._startingMap.set(name, {
@@ -43,7 +42,7 @@ export class SdCliPerformanceTimer {
43
42
 
44
43
  const res = fn();
45
44
  if (res instanceof Promise) {
46
- return res.then(realRes => finish(realRes, startTime)) as R;
45
+ return res.then((realRes) => finish(realRes, startTime)) as R;
47
46
  }
48
47
 
49
48
  return finish(res, startTime);
@@ -53,10 +52,8 @@ export class SdCliPerformanceTimer {
53
52
  return `${this._name} 성능 보고서
54
53
  ------------------------------------
55
54
  ${Array.from(this._resultMap.entries())
56
- .map(([key, val]) =>
57
- `${key}: ${val.time.toLocaleString()}ms (${val.cpu.toLocaleString()}ms CPU)`,
58
- )
59
- .join("\n")}
55
+ .map(([key, val]) => `${key}: ${val.time.toLocaleString()}ms (${val.cpu.toLocaleString()}ms CPU)`)
56
+ .join("\n")}
60
57
  ------------------------------------`;
61
58
  }
62
59
  }
package/vitest.config.js CHANGED
@@ -12,4 +12,4 @@ export default defineConfig({
12
12
  environment: "node",
13
13
  include: ["tests/**/*.spec.ts"],
14
14
  },
15
- });
15
+ });