@simplysm/sd-cli 7.0.22 → 7.0.26

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 (33) hide show
  1. package/dist/bin/sd-cli.d.ts +1 -1
  2. package/dist/bin/sd-cli.mjs +52 -15
  3. package/dist/builder/SdCliServerBuilder.d.ts +22 -0
  4. package/dist/builder/SdCliServerBuilder.mjs +323 -0
  5. package/dist/builder/SdCliTsLibBuilder.mjs +2 -2
  6. package/dist/commons.d.ts +5 -4
  7. package/dist/entry-points/SdCliLocalUpdate.d.ts +7 -4
  8. package/dist/entry-points/SdCliLocalUpdate.mjs +6 -7
  9. package/dist/entry-points/SdCliNpm.d.ts +6 -0
  10. package/dist/entry-points/SdCliNpm.mjs +18 -0
  11. package/dist/entry-points/SdCliPrepare.d.ts +5 -0
  12. package/dist/entry-points/SdCliPrepare.mjs +54 -0
  13. package/dist/entry-points/SdCliWorkspace.d.ts +13 -5
  14. package/dist/entry-points/SdCliWorkspace.mjs +49 -13
  15. package/dist/packages/SdCliPackage.d.ts +6 -2
  16. package/dist/packages/SdCliPackage.mjs +34 -23
  17. package/dist/utils/SdCliBuildResultUtil.d.ts +4 -1
  18. package/dist/utils/SdCliBuildResultUtil.mjs +24 -2
  19. package/dist/utils/SdCliConfigUtil.d.ts +1 -1
  20. package/dist/utils/SdCliConfigUtil.mjs +5 -5
  21. package/package.json +13 -5
  22. package/src/bin/sd-cli.ts +59 -14
  23. package/src/builder/SdCliServerBuilder.ts +359 -0
  24. package/src/builder/SdCliTsLibBuilder.ts +1 -1
  25. package/src/commons.ts +3 -5
  26. package/src/entry-points/SdCliLocalUpdate.ts +5 -6
  27. package/src/entry-points/SdCliNpm.ts +22 -0
  28. package/src/entry-points/SdCliPrepare.ts +54 -0
  29. package/src/entry-points/SdCliWorkspace.ts +65 -15
  30. package/src/packages/SdCliPackage.ts +37 -26
  31. package/src/utils/SdCliBuildResultUtil.ts +30 -1
  32. package/src/utils/SdCliConfigUtil.ts +4 -4
  33. package/tsconfig.json +4 -1
@@ -0,0 +1,54 @@
1
+ import { FsUtil, Logger } from "@simplysm/sd-core-node";
2
+
3
+ export class SdCliPrepare {
4
+ private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
5
+
6
+ public async prepareAsync(): Promise<void> {
7
+ // 타입체크 속도 지연 메시지 추가
8
+ const r1 = await this._modifyTypescriptCodeForTypeCheckPerformanceWarning();
9
+ this._logger.log(`[모듈수정] 타입체크 속도 지연 메시지 추가 (${r1 ? "O" : "X"})`);
10
+ }
11
+
12
+ private async _modifyTypescriptCodeForTypeCheckPerformanceWarning(): Promise<boolean> {
13
+ const fileUrl = await import.meta.resolve!("typescript");
14
+ const filePath = fileUrl.replace(/^file:\/\/\//, "");
15
+ const fileContent = await FsUtil.readFileAsync(filePath);
16
+ const modifiedFileContent = fileContent.replace(`
17
+ function checkSourceElement(node) {
18
+ if (node) {
19
+ var saveCurrentNode = currentNode;
20
+ currentNode = node;
21
+ instantiationCount = 0;
22
+ checkSourceElementWorker(node);
23
+ currentNode = saveCurrentNode;
24
+ }
25
+ }`, `
26
+ function checkSourceElement(node) {
27
+ if (node) {
28
+ const prevUsage = process.cpuUsage();
29
+
30
+ var saveCurrentNode = currentNode;
31
+ currentNode = node;
32
+ instantiationCount = 0;
33
+ checkSourceElementWorker(node);
34
+ currentNode = saveCurrentNode;
35
+
36
+ const usage = process.cpuUsage(prevUsage);
37
+ if (node.hasChildPerformanceWarning) {
38
+ node.parent.hasChildPerformanceWarning = true;
39
+ }
40
+ else if (usage.user + usage.system > 2000 * 1000 && node.kind !== 253) {
41
+ error(node, {
42
+ code: 9000,
43
+ category: ts.DiagnosticCategory.Warning,
44
+ key: "simplysm_check_source_element_performance_slow",
45
+ message: "소스코드 타입분석에 너무 오랜 시간이 소요됩니다. [" + Math.round((usage.user + usage.system) / 1000) + "ms/cpu, KIND: " + node.kind + "]"
46
+ });
47
+ node.parent.hasChildPerformanceWarning = true;
48
+ }
49
+ }
50
+ }`);
51
+ await FsUtil.writeFileAsync(filePath, modifiedFileContent);
52
+ return fileContent !== modifiedFileContent;
53
+ }
54
+ }
@@ -2,28 +2,31 @@ import { FsUtil, Logger, SdProcess } from "@simplysm/sd-core-node";
2
2
  import path from "path";
3
3
  import { INpmConfig, ISdCliConfig, ISdCliPackageBuildResult } from "../commons";
4
4
  import { SdCliPackage } from "../packages/SdCliPackage";
5
- import { Wait } from "@simplysm/sd-core-common";
5
+ import { Uuid, Wait } from "@simplysm/sd-core-common";
6
6
  import os from "os";
7
7
  import { SdCliBuildResultUtil } from "../utils/SdCliBuildResultUtil";
8
8
  import semver from "semver/preload";
9
+ import { SdCliConfigUtil } from "../utils/SdCliConfigUtil";
10
+ import { SdServiceServer } from "@simplysm/sd-service/server";
11
+ import { SdCliNpm } from "./SdCliNpm";
9
12
 
10
13
  export class SdCliWorkspace {
11
14
  private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
12
15
 
13
16
  private readonly _npmConfig: INpmConfig;
14
- private readonly _config: ISdCliConfig;
17
+ private readonly _serverInfoMap = new Map<string, IServerInfo>();
15
18
 
16
- public constructor(private readonly _rootPath: string,
17
- private readonly _configFilePath?: string) {
19
+ public constructor(private readonly _rootPath: string) {
18
20
  const npmConfigFilePath = path.resolve(this._rootPath, "package.json");
19
21
  this._npmConfig = FsUtil.readJson(npmConfigFilePath);
20
-
21
- this._config = FsUtil.readJson(path.resolve(this._rootPath, this._configFilePath ?? "simplysm.json"));
22
22
  }
23
23
 
24
- public async watchAsync(): Promise<void> {
24
+ public async watchAsync(opt: { confFileRelPath: string; optNames: string[] }): Promise<void> {
25
+ this._logger.debug("프로젝트 설정 가져오기...");
26
+ const config = await SdCliConfigUtil.loadConfigAsync(path.resolve(this._rootPath, opt.confFileRelPath), true, opt.optNames);
27
+
25
28
  this._logger.debug("패키지 목록 구성...");
26
- const pkgs = await this._getPackagesAsync();
29
+ const pkgs = await this._getPackagesAsync(config);
27
30
 
28
31
  this._logger.debug("패키지 이벤트 설정...");
29
32
  let changeCount = 0;
@@ -37,7 +40,15 @@ export class SdCliWorkspace {
37
40
  changeCount++;
38
41
  this._logger.debug(`[${pkg.name}] 빌드를 시작합니다...`);
39
42
  })
40
- .on("complete", (results) => {
43
+ .on("complete", async (results) => {
44
+ if (pkg.type === "server") {
45
+ if (results.some((item) => item.severity === "error")) {
46
+ return;
47
+ }
48
+
49
+ await this._restartServerAsync(pkg);
50
+ }
51
+
41
52
  this._logger.debug(`[${pkg.name}] 빌드가 완료되었습니다.`);
42
53
  totalResultMap.set(pkg.name, results);
43
54
 
@@ -61,9 +72,38 @@ export class SdCliWorkspace {
61
72
  });
62
73
  }
63
74
 
64
- public async buildAsync(): Promise<void> {
75
+ private async _restartServerAsync(pkg: SdCliPackage): Promise<void> {
76
+ const entryFileRelPath = pkg.main;
77
+ if (entryFileRelPath === undefined) {
78
+ this._logger.error(`서버패키지(${pkg.name})의 'package.json'에서 'main'필드를 찾을 수 없습니다.`);
79
+ return;
80
+ }
81
+ const entryFilePath = path.resolve(pkg.rootPath, entryFileRelPath);
82
+
83
+ this._logger.log(`서버(${pkg.name}) 재시작...`);
84
+ const serverInfo = this._serverInfoMap.getOrCreate(pkg.name, {});
85
+ if (serverInfo.server) {
86
+ await serverInfo.server.closeAsync();
87
+ delete serverInfo.server;
88
+ }
89
+
90
+ serverInfo.server = (await import("file:///" + entryFilePath + "?update=" + Uuid.new().toString())).default as SdServiceServer | undefined;
91
+ if (!serverInfo.server) {
92
+ this._logger.error(`${entryFilePath}(0, 0): 'SdServiceServer'를 'export'해야 합니다.`);
93
+ return;
94
+ }
95
+
96
+ await Wait.until(() => serverInfo.server!.isOpen);
97
+
98
+ this._logger.log(`서버(${pkg.name}) 재시작 완료`);
99
+ }
100
+
101
+ public async buildAsync(opt: { confFileRelPath: string; optNames: string[] }): Promise<void> {
102
+ this._logger.debug("프로젝트 설정 가져오기...");
103
+ const config = await SdCliConfigUtil.loadConfigAsync(path.resolve(this._rootPath, opt.confFileRelPath), false, opt.optNames);
104
+
65
105
  this._logger.debug("패키지 목록 구성...");
66
- const pkgs = await this._getPackagesAsync();
106
+ const pkgs = await this._getPackagesAsync(config);
67
107
 
68
108
  this._logger.debug("프로젝트 및 패키지 버전 설정...");
69
109
  await this._upgradeVersionAsync(pkgs);
@@ -96,7 +136,10 @@ export class SdCliWorkspace {
96
136
  }
97
137
  }
98
138
 
99
- public async publishAsync(opt: { noBuild: boolean }): Promise<void> {
139
+ public async publishAsync(opt: { noBuild: boolean; confFileRelPath: string; optNames: string[] }): Promise<void> {
140
+ this._logger.debug("프로젝트 설정 가져오기...");
141
+ const config = await SdCliConfigUtil.loadConfigAsync(path.resolve(this._rootPath, opt.confFileRelPath), false, opt.optNames);
142
+
100
143
  if (opt.noBuild) {
101
144
  this._logger.warn("빌드하지 않고, 배포하는것은 상당히 위험합니다.");
102
145
  await this._waitSecMessageAsync("프로세스를 중지하려면, 'CTRL+C'를 누르세요.", 5);
@@ -112,11 +155,14 @@ export class SdCliWorkspace {
112
155
  }
113
156
 
114
157
  this._logger.debug("패키지 목록 구성...");
115
- const pkgs = await this._getPackagesAsync();
158
+ const pkgs = await this._getPackagesAsync(config);
116
159
 
117
160
  this._logger.debug("프로젝트 및 패키지 버전 설정...");
118
161
  await this._upgradeVersionAsync(pkgs);
119
162
 
163
+ this._logger.debug("노드패키지 업데이트...");
164
+ await new SdCliNpm(this._rootPath).updateAsync();
165
+
120
166
  // 빌드
121
167
  if (!opt.noBuild) {
122
168
  this._logger.debug("빌드를 시작합니다...");
@@ -182,14 +228,14 @@ export class SdCliWorkspace {
182
228
  process.stdout.clearLine(0);
183
229
  }
184
230
 
185
- private async _getPackagesAsync(): Promise<SdCliPackage[]> {
231
+ private async _getPackagesAsync(conf: ISdCliConfig): Promise<SdCliPackage[]> {
186
232
  const pkgRootPaths = await this._npmConfig.workspaces?.mapManyAsync(async (item) => await FsUtil.globAsync(item));
187
233
  if (!pkgRootPaths) {
188
234
  throw new Error("최상위 'package.json'에서 'workspaces'를 찾을 수 없습니다.");
189
235
  }
190
236
 
191
237
  return pkgRootPaths.map((pkgRootPath) => {
192
- const pkgConfig = this._config.packages[path.basename(pkgRootPath)];
238
+ const pkgConfig = conf.packages[path.basename(pkgRootPath)];
193
239
  return pkgConfig ? new SdCliPackage(this._rootPath, pkgRootPath, pkgConfig) : undefined;
194
240
  }).filterExists();
195
241
  }
@@ -227,3 +273,7 @@ export class SdCliWorkspace {
227
273
  }
228
274
  }
229
275
  }
276
+
277
+ interface IServerInfo {
278
+ server?: SdServiceServer;
279
+ }
@@ -5,29 +5,42 @@ import { EventEmitter } from "events";
5
5
  import { ObjectUtil } from "@simplysm/sd-core-common";
6
6
  import { SdCliTsLibBuilder } from "../builder/SdCliTsLibBuilder";
7
7
  import { SdCliJsLibBuilder } from "../builder/SdCliJsLibBuilder";
8
+ import { SdCliServerBuilder } from "../builder/SdCliServerBuilder";
8
9
 
9
10
  export class SdCliPackage extends EventEmitter {
10
11
  private readonly _npmConfig: INpmConfig;
11
12
 
13
+ public get basename(): string {
14
+ return path.basename(this._workspaceRootPath);
15
+ }
16
+
12
17
  public get name(): string {
13
18
  return this._npmConfig.name;
14
19
  }
15
20
 
21
+ public get main(): string | undefined {
22
+ return this._npmConfig.main;
23
+ }
24
+
25
+ public get type(): TSdCliPackageConfig["type"] {
26
+ return this._config.type;
27
+ }
28
+
16
29
  public get allDependencies(): string[] {
17
30
  return [
18
31
  ...Object.keys(this._npmConfig.dependencies ?? {}),
19
32
  ...Object.keys(this._npmConfig.optionalDependencies ?? {}),
20
- ...Object.keys(this._npmConfig.devDependencies ?? {}),
33
+ // ...Object.keys(this._npmConfig.devDependencies ?? {}),
21
34
  ...Object.keys(this._npmConfig.peerDependencies ?? {})
22
35
  ].distinct();
23
36
  }
24
37
 
25
38
  public constructor(private readonly _workspaceRootPath: string,
26
- private readonly _rootPath: string,
39
+ public readonly rootPath: string,
27
40
  private readonly _config: TSdCliPackageConfig) {
28
41
  super();
29
42
 
30
- const npmConfigFilePath = path.resolve(this._rootPath, "package.json");
43
+ const npmConfigFilePath = path.resolve(this.rootPath, "package.json");
31
44
  this._npmConfig = FsUtil.readJson(npmConfigFilePath);
32
45
  }
33
46
 
@@ -53,21 +66,12 @@ export class SdCliPackage extends EventEmitter {
53
66
  updateDepVersion(this._npmConfig.devDependencies);
54
67
  updateDepVersion(this._npmConfig.peerDependencies);
55
68
 
56
- const npmConfigFilePath = path.resolve(this._rootPath, "package.json");
69
+ const npmConfigFilePath = path.resolve(this.rootPath, "package.json");
57
70
  await FsUtil.writeJsonAsync(npmConfigFilePath, this._npmConfig, { space: 2 });
58
71
  }
59
72
 
60
73
  public async watchAsync(): Promise<void> {
61
- const isTs = FsUtil.exists(path.resolve(this._rootPath, "tsconfig.json"));
62
-
63
- if (isTs) {
64
- await this._genBuildTsconfigAsync();
65
- }
66
-
67
- const isAngular = isTs && this.allDependencies.includes("@angular/core");
68
- const builder = !isTs ? new SdCliJsLibBuilder(this._rootPath) : new SdCliTsLibBuilder(this._rootPath, isAngular);
69
-
70
- await builder
74
+ await (await this._createBuilderAsync())
71
75
  .on("change", () => {
72
76
  this.emit("change");
73
77
  })
@@ -78,26 +82,33 @@ export class SdCliPackage extends EventEmitter {
78
82
  }
79
83
 
80
84
  public async buildAsync(): Promise<ISdCliPackageBuildResult[]> {
81
- const isTs = FsUtil.exists(path.resolve(this._rootPath, "tsconfig.json"));
85
+ return await (await this._createBuilderAsync()).buildAsync();
86
+ }
82
87
 
83
- if (isTs) {
84
- await this._genBuildTsconfigAsync();
88
+ public async publishAsync(): Promise<void> {
89
+ if (this._config.type === "library" && this._config.publish === "npm") {
90
+ await SdProcess.execAsync("npm publish --quiet --access public", { cwd: this.rootPath });
85
91
  }
92
+ }
86
93
 
87
- const isAngular = isTs && this.allDependencies.includes("@angular/core");
88
- const builder = isTs ? new SdCliTsLibBuilder(this._rootPath, isAngular) : new SdCliJsLibBuilder(this._rootPath);
94
+ private async _createBuilderAsync(): Promise<SdCliJsLibBuilder | SdCliTsLibBuilder | SdCliServerBuilder> {
95
+ const isTs = FsUtil.exists(path.resolve(this.rootPath, "tsconfig.json"));
89
96
 
90
- return await builder.buildAsync();
91
- }
97
+ if (isTs) {
98
+ await this._genBuildTsconfigAsync();
99
+ }
92
100
 
93
- public async publishAsync(): Promise<void> {
94
- if (this._config.type === "library" && this._config.publish === "npm") {
95
- await SdProcess.execAsync("npm publish --quiet --access public", { cwd: this._rootPath });
101
+ if (this._config.type === "library") {
102
+ const isAngular = isTs && this.allDependencies.includes("@angular/core");
103
+ return isTs ? new SdCliTsLibBuilder(this.rootPath, isAngular) : new SdCliJsLibBuilder(this.rootPath);
104
+ }
105
+ else {
106
+ return new SdCliServerBuilder(this.rootPath, this._config, this._workspaceRootPath);
96
107
  }
97
108
  }
98
109
 
99
110
  private async _genBuildTsconfigAsync(): Promise<void> {
100
- const baseTsconfigFilePath = path.resolve(this._rootPath, "tsconfig.json");
111
+ const baseTsconfigFilePath = path.resolve(this.rootPath, "tsconfig.json");
101
112
  const baseTsconfig: ITsconfig = await FsUtil.readJsonAsync(baseTsconfigFilePath);
102
113
 
103
114
  const buildTsconfig: ITsconfig = ObjectUtil.clone(baseTsconfig);
@@ -105,7 +116,7 @@ export class SdCliPackage extends EventEmitter {
105
116
  delete buildTsconfig.compilerOptions.baseUrl;
106
117
  delete buildTsconfig.compilerOptions.paths;
107
118
 
108
- const buildTsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
119
+ const buildTsconfigFilePath = path.resolve(this.rootPath, "tsconfig-build.json");
109
120
  await FsUtil.writeJsonAsync(buildTsconfigFilePath, buildTsconfig, { space: 2 });
110
121
  }
111
122
  }
@@ -2,9 +2,10 @@ import ts from "typescript";
2
2
  import * as os from "os";
3
3
  import * as path from "path";
4
4
  import { ISdCliPackageBuildResult } from "../commons";
5
+ import webpack from "webpack";
5
6
 
6
7
  export class SdCliBuildResultUtil {
7
- public static convertDiagsToResult(diag: ts.Diagnostic): ISdCliPackageBuildResult | undefined {
8
+ public static convertFromTsDiag(diag: ts.Diagnostic): ISdCliPackageBuildResult | undefined {
8
9
  const severity = diag.category === ts.DiagnosticCategory.Error ? "error"
9
10
  : diag.category === ts.DiagnosticCategory.Warning ? "warning"
10
11
  : undefined;
@@ -29,6 +30,34 @@ export class SdCliBuildResultUtil {
29
30
  };
30
31
  }
31
32
 
33
+ public static convertFromWebpackStats(stats: webpack.Stats): ISdCliPackageBuildResult[] {
34
+ const results: ISdCliPackageBuildResult[] = [];
35
+
36
+ for (const statWarn of stats.compilation.warnings) {
37
+ if (stats.compilation.options.ignoreWarnings?.some((ignoreWarning) => ignoreWarning(statWarn, stats.compilation))) continue;
38
+
39
+ results.push(this._convertFromWebpackError("warning", statWarn));
40
+ }
41
+
42
+ for (const statErr of stats.compilation.errors) {
43
+ results.push(this._convertFromWebpackError("error", statErr));
44
+ }
45
+
46
+ return results;
47
+ }
48
+
49
+ private static _convertFromWebpackError(severity: "warning" | "error", err: webpack.WebpackError): ISdCliPackageBuildResult {
50
+ return {
51
+ filePath: err.file,
52
+ line: err.loc["start"].line,
53
+ char: err.loc["start"].column,
54
+ code: err.name,
55
+ severity,
56
+ message: err.message
57
+ };
58
+ }
59
+
60
+
32
61
  public static getMessage(result: ISdCliPackageBuildResult): string {
33
62
  let str = "";
34
63
  if (result.filePath !== undefined) {
@@ -4,15 +4,15 @@ import { ObjectUtil } from "@simplysm/sd-core-common";
4
4
  import path from "path";
5
5
 
6
6
  export class SdCliConfigUtil {
7
- public static loadConfig(confFilePath: string, isDev: boolean, opts?: string[]): ISdCliConfig {
8
- const confFileCont = FsUtil.readJson(confFilePath);
7
+ public static async loadConfigAsync(confFilePath: string, isDev: boolean, opts?: string[]): Promise<ISdCliConfig> {
8
+ const confFileCont = await FsUtil.readJsonAsync(confFilePath);
9
9
  let conf = this._getConfigFromFileContent(confFileCont, isDev, opts);
10
10
 
11
11
  // extends
12
12
  if (conf.extends) {
13
13
  for (const extConfFilePath of conf.extends) {
14
- const extConf = this.loadConfig(path.resolve(path.dirname(confFilePath), extConfFilePath), isDev, opts);
15
- conf = this._mergeObj(conf, extConf);
14
+ const extConf = await this.loadConfigAsync(path.resolve(path.dirname(confFilePath), extConfFilePath), isDev, opts);
15
+ conf = this._mergeObj(extConf, conf);
16
16
  }
17
17
  }
18
18
  delete conf["extends"];
package/tsconfig.json CHANGED
@@ -12,7 +12,10 @@
12
12
  ],
13
13
  "@simplysm/sd-core-common": [
14
14
  "../sd-core-common/src/index.ts"
15
+ ],
16
+ "@simplysm/sd-service/server": [
17
+ "../sd-service/src/server.ts"
15
18
  ]
16
19
  }
17
20
  }
18
- }
21
+ }