@simplysm/sd-cli 7.0.247 → 7.0.263

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 (110) hide show
  1. package/README.md +48 -0
  2. package/dist/bin/sd-cli.mjs +171 -13
  3. package/dist/build-tool/SdCliElectron.mjs +3 -2
  4. package/dist/builder/SdCliClientBuilder.mjs +33 -35
  5. package/dist/builder/SdCliServerBuilder.d.ts +1 -0
  6. package/dist/builder/SdCliServerBuilder.mjs +58 -22
  7. package/dist/builder/SdCliTsLibBuilder.mjs +18 -12
  8. package/dist/commons.d.ts +9 -1
  9. package/dist/entry-points/SdCliProjectGenerator.d.ts +37 -0
  10. package/dist/entry-points/SdCliProjectGenerator.mjs +288 -0
  11. package/dist/entry-points/SdCliWorkspace.d.ts +6 -3
  12. package/dist/entry-points/SdCliWorkspace.mjs +147 -120
  13. package/dist/entry-points/file/base/fc_package_eslintrc.d.ts +3 -0
  14. package/dist/entry-points/file/base/fc_package_eslintrc.mjs +31 -0
  15. package/dist/entry-points/file/base/fc_package_npmconfig.d.ts +9 -0
  16. package/dist/entry-points/file/base/fc_package_npmconfig.mjs +32 -0
  17. package/dist/entry-points/file/base/fc_package_tsconfig.d.ts +4 -0
  18. package/dist/entry-points/file/base/fc_package_tsconfig.mjs +12 -0
  19. package/dist/entry-points/file/client/fc_package_AppModule.d.ts +1 -0
  20. package/dist/entry-points/file/client/fc_package_AppModule.mjs +37 -0
  21. package/dist/entry-points/file/client/fc_package_AppPage.d.ts +1 -0
  22. package/dist/entry-points/file/client/fc_package_AppPage.mjs +34 -0
  23. package/dist/entry-points/file/client/fc_package_appicons.d.ts +1 -0
  24. package/dist/entry-points/file/client/fc_package_appicons.mjs +6 -0
  25. package/dist/entry-points/file/client/fc_package_client_main.d.ts +1 -0
  26. package/dist/entry-points/file/client/fc_package_client_main.mjs +19 -0
  27. package/dist/entry-points/file/client/fc_package_index.d.ts +3 -0
  28. package/dist/entry-points/file/client/fc_package_index.mjs +88 -0
  29. package/dist/entry-points/file/client/fc_package_manifest.d.ts +5 -0
  30. package/dist/entry-points/file/client/fc_package_manifest.mjs +54 -0
  31. package/dist/entry-points/file/client/fc_package_polyfills.d.ts +1 -0
  32. package/dist/entry-points/file/client/fc_package_polyfills.mjs +11 -0
  33. package/dist/entry-points/file/client/fc_package_styles.d.ts +1 -0
  34. package/dist/entry-points/file/client/fc_package_styles.mjs +7 -0
  35. package/dist/entry-points/file/db/fc_package_DbContext.d.ts +3 -0
  36. package/dist/entry-points/file/db/fc_package_DbContext.mjs +14 -0
  37. package/dist/entry-points/file/db/fc_package_DbModel.d.ts +4 -0
  38. package/dist/entry-points/file/db/fc_package_DbModel.mjs +12 -0
  39. package/dist/entry-points/file/project/fc_project_editor_config.d.ts +1 -0
  40. package/dist/entry-points/file/project/fc_project_editor_config.mjs +22 -0
  41. package/dist/entry-points/file/project/fc_project_eslintrc.d.ts +1 -0
  42. package/dist/entry-points/file/project/fc_project_eslintrc.mjs +30 -0
  43. package/dist/entry-points/file/project/fc_project_gitattributes.d.ts +1 -0
  44. package/dist/entry-points/file/project/fc_project_gitattributes.mjs +6 -0
  45. package/dist/entry-points/file/project/fc_project_gitignore.d.ts +1 -0
  46. package/dist/entry-points/file/project/fc_project_gitignore.mjs +17 -0
  47. package/dist/entry-points/file/project/fc_project_npmconfig.d.ts +7 -0
  48. package/dist/entry-points/file/project/fc_project_npmconfig.mjs +35 -0
  49. package/dist/entry-points/file/project/fc_project_readme.d.ts +3 -0
  50. package/dist/entry-points/file/project/fc_project_readme.mjs +12 -0
  51. package/dist/entry-points/file/project/fc_project_simplysm.d.ts +1 -0
  52. package/dist/entry-points/file/project/fc_project_simplysm.mjs +2 -0
  53. package/dist/entry-points/file/project/fc_project_tsconfig.d.ts +1 -0
  54. package/dist/entry-points/file/project/fc_project_tsconfig.mjs +26 -0
  55. package/dist/entry-points/file/server/fc_package_server_main.d.ts +4 -0
  56. package/dist/entry-points/file/server/fc_package_server_main.mjs +60 -0
  57. package/dist/index.d.ts +23 -0
  58. package/dist/index.mjs +24 -1
  59. package/dist/packages/SdCliPackage.d.ts +1 -0
  60. package/dist/packages/SdCliPackage.mjs +68 -62
  61. package/dist/utils/SdCliBuildResultUtil.mjs +2 -2
  62. package/dist/worker/server-worker.d.ts +1 -0
  63. package/dist/worker/server-worker.mjs +67 -0
  64. package/package.json +10 -8
  65. package/src/bin/sd-cli.ts +198 -12
  66. package/src/build-tool/SdCliElectron.ts +2 -1
  67. package/src/builder/SdCliClientBuilder.ts +36 -36
  68. package/src/builder/SdCliJsLibBuilder.ts +70 -70
  69. package/src/builder/SdCliServerBuilder.ts +58 -20
  70. package/src/builder/SdCliTsLibBuilder.ts +450 -445
  71. package/src/commons.ts +7 -1
  72. package/src/entry-points/SdCliProjectGenerator.ts +353 -0
  73. package/src/entry-points/SdCliWorkspace.ts +171 -137
  74. package/src/entry-points/file/assets/assets/icons/icon-128x128.png +0 -0
  75. package/src/entry-points/file/assets/assets/icons/icon-144x144.png +0 -0
  76. package/src/entry-points/file/assets/assets/icons/icon-152x152.png +0 -0
  77. package/src/entry-points/file/assets/assets/icons/icon-192x192.png +0 -0
  78. package/src/entry-points/file/assets/assets/icons/icon-384x384.png +0 -0
  79. package/src/entry-points/file/assets/assets/icons/icon-512x512.png +0 -0
  80. package/src/entry-points/file/assets/assets/icons/icon-72x72.png +0 -0
  81. package/src/entry-points/file/assets/assets/icons/icon-96x96.png +0 -0
  82. package/src/entry-points/file/assets/favicon.ico +0 -0
  83. package/src/entry-points/file/base/fc_package_eslintrc.ts +30 -0
  84. package/src/entry-points/file/base/fc_package_npmconfig.ts +43 -0
  85. package/src/entry-points/file/base/fc_package_tsconfig.ts +11 -0
  86. package/src/entry-points/file/client/fc_package_AppModule.ts +36 -0
  87. package/src/entry-points/file/client/fc_package_AppPage.ts +33 -0
  88. package/src/entry-points/file/client/fc_package_appicons.ts +5 -0
  89. package/src/entry-points/file/client/fc_package_client_main.ts +18 -0
  90. package/src/entry-points/file/client/fc_package_index.ts +87 -0
  91. package/src/entry-points/file/client/fc_package_manifest.ts +53 -0
  92. package/src/entry-points/file/client/fc_package_polyfills.ts +10 -0
  93. package/src/entry-points/file/client/fc_package_styles.ts +6 -0
  94. package/src/entry-points/file/db/fc_package_DbContext.ts +14 -0
  95. package/src/entry-points/file/db/fc_package_DbModel.ts +11 -0
  96. package/src/entry-points/file/project/fc_project_editor_config.ts +21 -0
  97. package/src/entry-points/file/project/fc_project_eslintrc.ts +29 -0
  98. package/src/entry-points/file/project/fc_project_gitattributes.ts +5 -0
  99. package/src/entry-points/file/project/fc_project_gitignore.ts +16 -0
  100. package/src/entry-points/file/project/fc_project_npmconfig.ts +34 -0
  101. package/src/entry-points/file/project/fc_project_readme.ts +11 -0
  102. package/src/entry-points/file/project/fc_project_simplysm.ts +1 -0
  103. package/src/entry-points/file/project/fc_project_tsconfig.ts +25 -0
  104. package/src/entry-points/file/server/fc_package_server_main.ts +62 -0
  105. package/src/index.ts +23 -0
  106. package/src/packages/SdCliPackage.ts +75 -58
  107. package/src/utils/SdCliBuildResultUtil.ts +76 -76
  108. package/src/worker/server-worker.ts +72 -0
  109. package/tsconfig-build.json +2 -1
  110. package/tsconfig.json +2 -1
@@ -1,17 +1,15 @@
1
- import { FsUtil, Logger, PathUtil, SdProcess } from "@simplysm/sd-core-node";
1
+ 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 { Uuid, Wait } from "@simplysm/sd-core-common";
5
+ import { JsonConvert, 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
9
  import { SdCliConfigUtil } from "../utils/SdCliConfigUtil";
10
- import { SdServiceServer } from "@simplysm/sd-service-server";
11
10
  import { SdCliLocalUpdate } from "./SdCliLocalUpdate";
12
- import url from "url";
13
- import mime from "mime";
14
- import { NextHandleFunction } from "connect";
11
+ import { fileURLToPath } from "url";
12
+ import cp from "child_process";
15
13
 
16
14
  export class SdCliWorkspace {
17
15
  private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
@@ -34,7 +32,8 @@ export class SdCliWorkspace {
34
32
  }
35
33
 
36
34
  this._logger.debug("패키지 목록 구성...");
37
- const pkgs = await this._getPackagesAsync(config, opt.pkgs);
35
+ const allPkgs = await this._getPackagesAsync(config);
36
+ const pkgs = allPkgs.filter((pkg) => opt.pkgs.length === 0 || opt.pkgs.includes(path.basename(pkg.rootPath)));
38
37
 
39
38
  this._logger.debug("패키지 이벤트 설정...");
40
39
  let changeCount = 0;
@@ -69,11 +68,15 @@ export class SdCliWorkspace {
69
68
  if (changePkg.config.type === "client") {
70
69
  if (typeof changePkg.config.server === "string") {
71
70
  const serverInfo = this._serverInfoMap.get(changePkg.config.server);
72
- serverInfo?.server?.broadcastReload();
71
+ if (serverInfo) {
72
+ await this._sendServerWorkerBroadcastReloadAsync(serverInfo);
73
+ }
73
74
  }
74
75
  else {
75
76
  const serverInfo = this._serverInfoMap.get("PORT:" + changePkg.config.server.port);
76
- serverInfo?.server?.broadcastReload();
77
+ if (serverInfo) {
78
+ await this._sendServerWorkerBroadcastReloadAsync(serverInfo);
79
+ }
77
80
  }
78
81
  }
79
82
  }
@@ -100,35 +103,15 @@ export class SdCliWorkspace {
100
103
  if (pkg.config.type === "client") {
101
104
  await pkg.watchAsync();
102
105
 
103
- const pkgMiddleware: NextHandleFunction = (req, res, next) => {
104
- if (req.method === "GET") {
105
- const urlObj = url.parse(req.url!, true, false);
106
- const urlPathChain = decodeURI(urlObj.pathname!.slice(1)).split("/");
107
- if (urlPathChain[0] === pkg.name.split("/").last()!) {
108
- let targetFilePath = path.resolve(pkg.rootPath, "dist", ...urlPathChain.slice(1));
109
- targetFilePath = FsUtil.exists(targetFilePath) && FsUtil.stat(targetFilePath).isDirectory() ? path.resolve(targetFilePath, "index.html") : targetFilePath;
110
-
111
- if (FsUtil.exists(targetFilePath) && !path.basename(targetFilePath).startsWith(".")) {
112
- const fileStream = FsUtil.createReadStream(targetFilePath);
113
- const targetFileSize = FsUtil.lstat(targetFilePath).size;
114
-
115
- fileStream.on("open", () => {
116
- res.setHeader("Content-Length", targetFileSize);
117
- res.setHeader("Content-Type", mime.getType(targetFilePath)!);
118
- res.writeHead(200);
119
- });
120
- fileStream.pipe(res);
121
- return;
122
- }
123
- }
124
- }
106
+ if (typeof pkg.config.server === "string") {
107
+ const serverInfo = this._serverInfoMap.getOrCreate(pkg.config.server, { pathProxy: [], clientInfos: [] });
125
108
 
126
- next();
127
- };
109
+ serverInfo.pathProxy.push({
110
+ from: pkg.name.split("/").last()!,
111
+ to: path.resolve(pkg.rootPath, "dist")
112
+ });
113
+ await this._reloadServerWorkerPathProxyAsync(serverInfo);
128
114
 
129
- if (typeof pkg.config.server === "string") {
130
- const serverInfo = this._serverInfoMap.getOrCreate(pkg.config.server, { middlewares: [], clientInfos: [] });
131
- serverInfo.middlewares.push(pkgMiddleware);
132
115
  serverInfo.clientInfos.push({
133
116
  pkgKey: pkg.name.split("/").last()!,
134
117
  platforms: pkg.config.builder ? Object.keys(pkg.config.builder) : ["web"],
@@ -137,18 +120,20 @@ export class SdCliWorkspace {
137
120
  }
138
121
  else { // DEV SERVER
139
122
  const serverInfo = this._serverInfoMap.getOrCreate("PORT:" + pkg.config.server.port, {
140
- middlewares: [],
123
+ pathProxy: [],
141
124
  clientInfos: []
142
125
  });
143
- if (serverInfo.server === undefined) {
144
- const server = new SdServiceServer({
145
- rootPath: process.cwd(),
146
- services: [],
147
- port: pkg.config.server.port
126
+ if (serverInfo.worker === undefined) {
127
+ const serverWorkerInfo = await this._runServerWorkerAsync(pkg.config.server);
128
+ serverInfo.worker = serverWorkerInfo.worker;
129
+ serverInfo.url = serverWorkerInfo.url;
130
+
131
+ serverInfo.pathProxy.push({
132
+ from: pkg.name.split("/").last()!,
133
+ to: path.resolve(pkg.rootPath, "dist")
148
134
  });
149
- await server.listenAsync();
150
- serverInfo.server = server;
151
- serverInfo.server.devMiddlewares = [pkgMiddleware];
135
+ await this._reloadServerWorkerPathProxyAsync(serverInfo);
136
+
152
137
  serverInfo.clientInfos.push({
153
138
  pkgKey: pkg.name.split("/").last()!,
154
139
  platforms: pkg.config.builder ? Object.keys(pkg.config.builder) : ["web"],
@@ -177,97 +162,21 @@ export class SdCliWorkspace {
177
162
  }
178
163
  }
179
164
 
180
- private _isServerRestarting = false;
181
-
182
- private async _restartServerAsync(pkg: SdCliPackage): Promise<void> {
183
- await Wait.until(() => !this._isServerRestarting);
184
-
185
- this._isServerRestarting = true;
186
- const entryFileRelPath = pkg.main;
187
- if (entryFileRelPath === undefined) {
188
- this._logger.error(`서버패키지(${pkg.name})의 'package.json'에서 'main'필드를 찾을 수 없습니다.`);
189
- this._isServerRestarting = false;
190
- return;
191
- }
192
- const entryFilePath = path.resolve(pkg.rootPath, entryFileRelPath);
193
-
194
- try {
195
- const serverInfo = this._serverInfoMap.getOrCreate(path.basename(pkg.rootPath), {
196
- middlewares: [],
197
- clientInfos: []
198
- });
199
- if (serverInfo.server) {
200
- this._logger.log(`[${pkg.name}] 기존 서버 중지...`);
201
- await serverInfo.server.closeAsync();
202
- delete serverInfo.server;
203
- }
204
-
205
- this._logger.log(`[${pkg.name}] 서버 시작중...`);
206
-
207
- const serverMainPath = "file:///" + PathUtil.posix(entryFilePath) + "?update=" + Uuid.new().toString().replace(/-/g, "");
208
- const serverModule = await import(serverMainPath);
209
- serverInfo.server = serverModule.default as SdServiceServer | undefined;
210
- if (!serverInfo.server) {
211
- this._logger.error(`${entryFilePath}(0, 0): 'SdServiceServer'를 'export'해야 합니다.`);
212
- this._isServerRestarting = false;
213
- return;
214
- }
215
- serverInfo.server.devMiddlewares = serverInfo.middlewares;
216
- await Wait.until(() => serverInfo.server!.isOpen);
217
-
218
- this._logger.log(`[${pkg.name}] 서버가 시작되었습니다.`);
219
- }
220
- catch (err) {
221
- this._logger.error(`서버(${pkg.name}) 재시작중 오류 발생`, err);
222
- }
223
-
224
- this._isServerRestarting = false;
225
- }
226
-
227
165
  public async buildAsync(opt: { confFileRelPath: string; optNames: string[]; pkgs: string[] }): Promise<void> {
228
166
  this._logger.debug("프로젝트 설정 가져오기...");
229
167
  const config = await SdCliConfigUtil.loadConfigAsync(path.resolve(this._rootPath, opt.confFileRelPath), false, opt.optNames);
230
168
 
231
169
  this._logger.debug("패키지 목록 구성...");
232
- const pkgs = await this._getPackagesAsync(config, opt.pkgs);
170
+ const allPkgs = await this._getPackagesAsync(config);
171
+ const pkgs = allPkgs.filter((pkg) => opt.pkgs.length === 0 || opt.pkgs.includes(path.basename(pkg.rootPath)));
233
172
 
234
173
  this._logger.debug("프로젝트 및 패키지 버전 설정...");
235
- await this._upgradeVersionAsync(pkgs);
174
+ await this._upgradeVersionAsync(allPkgs);
236
175
 
237
176
  // 빌드
238
177
  await this._buildPkgsAsync(pkgs);
239
178
  }
240
179
 
241
- private async _buildPkgsAsync(pkgs: SdCliPackage[]): Promise<void> {
242
- this._logger.debug("빌드를 시작합니다...");
243
- const pkgNames = pkgs.map((item) => item.name);
244
- const buildCompletedPackageNames: string[] = [];
245
- const totalResultMap = new Map<string, ISdCliPackageBuildResult[]>();
246
- try {
247
- await pkgs.parallelAsync(async (pkg) => {
248
- await Wait.until(() => !pkg.allDependencies.some((dep) => pkgNames.includes(dep) && !buildCompletedPackageNames.includes(dep)));
249
-
250
- this._logger.debug(`[${pkg.name}] 빌드를 시작합니다...`);
251
- totalResultMap.set(pkg.name, await pkg.buildAsync());
252
- this._logger.debug(`[${pkg.name}] 빌드가 완료되었습니다.`);
253
-
254
- buildCompletedPackageNames.push(pkg.name);
255
- });
256
-
257
- this._loggingResults(totalResultMap);
258
- if (Array.from(totalResultMap.values()).mapMany().some((item) => item.severity === "error")) {
259
- throw new Error("빌드중 오류가 발생하였습니다.");
260
- }
261
- else {
262
- this._logger.info("모든 빌드가 완료되었습니다.");
263
- }
264
- }
265
- catch (err) {
266
- this._loggingResults(totalResultMap);
267
- throw err;
268
- }
269
- }
270
-
271
180
  public async publishAsync(opt: { noBuild: boolean; confFileRelPath: string; optNames: string[]; pkgs: string[] }): Promise<void> {
272
181
  this._logger.debug("프로젝트 설정 가져오기...");
273
182
  const config = await SdCliConfigUtil.loadConfigAsync(path.resolve(this._rootPath, opt.confFileRelPath), false, opt.optNames);
@@ -287,10 +196,11 @@ export class SdCliWorkspace {
287
196
  }
288
197
 
289
198
  this._logger.debug("패키지 목록 구성...");
290
- const pkgs = await this._getPackagesAsync(config, opt.pkgs);
199
+ const allPkgs = await this._getPackagesAsync(config);
200
+ const pkgs = allPkgs.filter((pkg) => opt.pkgs.length === 0 || opt.pkgs.includes(path.basename(pkg.rootPath)));
291
201
 
292
202
  this._logger.debug("프로젝트 및 패키지 버전 설정...");
293
- await this._upgradeVersionAsync(pkgs);
203
+ await this._upgradeVersionAsync(allPkgs);
294
204
 
295
205
  // 빌드
296
206
  if (!opt.noBuild) {
@@ -322,6 +232,129 @@ export class SdCliWorkspace {
322
232
  this._logger.info(`모든 배포가 완료되었습니다. (v${this._npmConfig.version})`);
323
233
  }
324
234
 
235
+ private async _runServerWorkerAsync(filePathOrOpt: string | { port: number }): Promise<{ worker: cp.ChildProcess; url: string }> {
236
+ const workerPath = fileURLToPath(await import.meta.resolve!("../worker/server-worker"));
237
+ return await new Promise<{ worker: cp.ChildProcess; url: string }>((resolve, reject) => {
238
+ const worker = cp.fork(workerPath, [
239
+ JsonConvert.stringify(filePathOrOpt)
240
+ ], {
241
+ stdio: ["pipe", "pipe", "pipe", "ipc"],
242
+ env: process.env
243
+ });
244
+
245
+ worker.on("error", (err) => {
246
+ reject(err);
247
+ });
248
+
249
+ worker.stdout!.pipe(process.stdout);
250
+ worker.stderr!.pipe(process.stderr);
251
+
252
+ worker.on("message", (url: string) => {
253
+ resolve({ worker, url });
254
+ });
255
+ });
256
+ }
257
+
258
+ private async _reloadServerWorkerPathProxyAsync(serverInfo: IServerInfo): Promise<void> {
259
+ if (!serverInfo.worker) return;
260
+
261
+ await new Promise<void>((resolve, reject) => {
262
+ serverInfo.worker!.send(JsonConvert.stringify(serverInfo.pathProxy), (err) => {
263
+ if (err) {
264
+ reject(err);
265
+ return;
266
+ }
267
+ resolve();
268
+ });
269
+ });
270
+ }
271
+
272
+ private async _sendServerWorkerBroadcastReloadAsync(serverInfo: IServerInfo): Promise<void> {
273
+ if (!serverInfo.worker) return;
274
+
275
+ await new Promise<void>((resolve, reject) => {
276
+ serverInfo.worker!.send("broadcastReload", (err) => {
277
+ if (err) {
278
+ reject(err);
279
+ return;
280
+ }
281
+ resolve();
282
+ });
283
+ });
284
+ }
285
+
286
+ private _isServerRestarting = false;
287
+
288
+ private async _restartServerAsync(pkg: SdCliPackage): Promise<void> {
289
+ await Wait.until(() => !this._isServerRestarting);
290
+
291
+ this._isServerRestarting = true;
292
+ const entryFileRelPath = pkg.main;
293
+ if (entryFileRelPath === undefined) {
294
+ this._logger.error(`서버패키지(${pkg.name})의 'package.json'에서 'main'필드를 찾을 수 없습니다.`);
295
+ this._isServerRestarting = false;
296
+ return;
297
+ }
298
+ const entryFilePath = path.resolve(pkg.rootPath, entryFileRelPath);
299
+
300
+ try {
301
+ const serverInfo = this._serverInfoMap.getOrCreate(path.basename(pkg.rootPath), {
302
+ pathProxy: [],
303
+ clientInfos: []
304
+ });
305
+ if (serverInfo.worker) {
306
+ this._logger.log(`[${pkg.name}] 기존 서버 중지...`);
307
+ cp.exec(`taskkill /pid ${serverInfo.worker.pid} /F /T`);
308
+ delete serverInfo.worker;
309
+ delete serverInfo.url;
310
+ }
311
+
312
+ this._logger.log(`[${pkg.name}] 서버 시작중...`);
313
+
314
+ const serverWorkerInfo = await this._runServerWorkerAsync(entryFilePath);
315
+ serverInfo.worker = serverWorkerInfo.worker;
316
+ serverInfo.url = serverWorkerInfo.url;
317
+ await this._reloadServerWorkerPathProxyAsync(serverInfo);
318
+
319
+ this._logger.log(`[${pkg.name}] 서버가 시작되었습니다.`);
320
+ }
321
+ catch (err) {
322
+ this._logger.error(`서버(${pkg.name}) 재시작중 오류 발생`, err);
323
+ }
324
+
325
+ this._isServerRestarting = false;
326
+ }
327
+
328
+ private async _buildPkgsAsync(pkgs: SdCliPackage[]): Promise<void> {
329
+ this._logger.debug("빌드를 시작합니다...");
330
+ const pkgNames = pkgs.map((item) => item.name);
331
+ const buildCompletedPackageNames: string[] = [];
332
+ const totalResultMap = new Map<string, ISdCliPackageBuildResult[]>();
333
+ try {
334
+ await pkgs.parallelAsync(async (pkg) => {
335
+ await Wait.until(() => !pkg.allDependencies.some((dep) => pkgNames.includes(dep) && !buildCompletedPackageNames.includes(dep)));
336
+
337
+ this._logger.debug(`[${pkg.name}] 빌드를 시작합니다...`);
338
+ totalResultMap.set(pkg.name, await pkg.buildAsync());
339
+ this._logger.debug(`[${pkg.name}] 빌드가 완료되었습니다.`);
340
+
341
+ buildCompletedPackageNames.push(pkg.name);
342
+ });
343
+
344
+ this._loggingResults(totalResultMap);
345
+ if (Array.from(totalResultMap.values()).mapMany().some((item) => item.severity === "error")) {
346
+ throw new Error("빌드중 오류가 발생하였습니다.");
347
+ }
348
+ else {
349
+ this._logger.info("모든 빌드가 완료되었습니다.");
350
+ }
351
+ }
352
+ catch (err) {
353
+ this._loggingResults(totalResultMap);
354
+ throw err;
355
+ }
356
+ }
357
+
325
358
  private async _upgradeVersionAsync(pkgs: SdCliPackage[]): Promise<void> {
326
359
  // 작업공간 package.json 버전 설정
327
360
  const newVersion = semver.inc(this._npmConfig.version, "patch")!;
@@ -364,14 +397,14 @@ export class SdCliWorkspace {
364
397
  process.stdout.clearLine(0);
365
398
  }
366
399
 
367
- private async _getPackagesAsync(conf: ISdCliConfig, pkgs: string[]): Promise<SdCliPackage[]> {
400
+ private async _getPackagesAsync(conf: ISdCliConfig): Promise<SdCliPackage[]> {
368
401
  const pkgRootPaths = await this._npmConfig.workspaces?.mapManyAsync(async (item) => await FsUtil.globAsync(item));
369
402
  if (!pkgRootPaths) {
370
403
  throw new Error("최상위 'package.json'에서 'workspaces'를 찾을 수 없습니다.");
371
404
  }
372
405
 
373
406
  return pkgRootPaths.map((pkgRootPath) => {
374
- if (pkgs.length > 0 && !pkgs.includes(path.basename(pkgRootPath))) return undefined;
407
+ // if (pkgs.length > 0 && !pkgs.includes(path.basename(pkgRootPath))) return undefined;
375
408
  const pkgConfig = conf.packages[path.basename(pkgRootPath)];
376
409
  if (!pkgConfig) return undefined;
377
410
  return new SdCliPackage(this._rootPath, pkgRootPath, pkgConfig);
@@ -416,26 +449,26 @@ export class SdCliWorkspace {
416
449
 
417
450
  const serverInfos = Array.from(this._serverInfoMap.values());
418
451
  for (const serverInfo of serverInfos) {
419
- if (!serverInfo.server) continue;
452
+ if (!serverInfo.worker) continue;
420
453
 
421
- const protocolStr = serverInfo.server.options.ssl ? "https" : "http";
422
- const portStr = serverInfo.server.options.port.toString();
454
+ /*const protocolStr = serverInfo.server.options.ssl ? "https" : "http";
455
+ const portStr = serverInfo.server.options.port.toString();*/
423
456
 
424
457
  for (const clientInfo of serverInfo.clientInfos) {
425
458
  for (const platform of clientInfo.platforms) {
426
459
  if (platform === "web") {
427
- clientHrefs.push(`${protocolStr}://localhost:${portStr}/${clientInfo.pkgKey}/`);
460
+ clientHrefs.push(`${serverInfo.url}/${clientInfo.pkgKey}/`);
428
461
  }
429
462
  else if (platform === "electron") {
430
- clientHrefs.push(`sd-cli run-electron ${clientInfo.pkgKey} http://localhost:${portStr}`);
463
+ clientHrefs.push(`sd-cli run-electron ${clientInfo.pkgKey} ${serverInfo.url}`);
431
464
  }
432
465
  else if (platform === "cordova") {
433
466
  for (const target of clientInfo.cordovaTargets) {
434
467
  if (target === "browser") {
435
- clientHrefs.push(`${protocolStr}://localhost:${portStr}/${clientInfo.pkgKey}/${platform}/`);
468
+ clientHrefs.push(`${serverInfo.url}/${clientInfo.pkgKey}/${platform}/`);
436
469
  }
437
470
  else {
438
- clientHrefs.push(`sd-cli run-cordova ${target} ${clientInfo.pkgKey} http://[IP]:${portStr}`);
471
+ clientHrefs.push(`sd-cli run-cordova ${target} ${clientInfo.pkgKey} ${serverInfo.url}`);
439
472
  }
440
473
  }
441
474
  }
@@ -449,7 +482,8 @@ export class SdCliWorkspace {
449
482
  }
450
483
 
451
484
  interface IServerInfo {
452
- server?: SdServiceServer;
453
- middlewares: NextHandleFunction[];
485
+ worker?: cp.ChildProcess;
486
+ url?: string;
487
+ pathProxy: { from: string; to: string }[];
454
488
  clientInfos: { pkgKey: string; platforms: string[]; cordovaTargets: string[] }[];
455
489
  }
@@ -0,0 +1,30 @@
1
+ export const fc_package_eslintrc = (opt: { isForAngular: boolean }): string => /* language=cjs */ `
2
+
3
+ module.exports = {
4
+ overrides: [
5
+ {
6
+ files: ["*.ts"],
7
+ parserOptions: {
8
+ tsconfigRootDir: __dirname,
9
+ project: "tsconfig.json"
10
+ },
11
+ settings: {
12
+ "import/resolver": {
13
+ "typescript": {
14
+ project: require("path").resolve(__dirname, "tsconfig.json")
15
+ }
16
+ }
17
+ },${opt.isForAngular ? `
18
+ {
19
+ files: ["*.ts"],
20
+ extends: ["plugin:@simplysm/angular"]
21
+ },
22
+ {
23
+ files: ["*.html"],
24
+ extends: ["plugin:@simplysm/angular-template"]
25
+ }` : ""}
26
+ }
27
+ ]
28
+ };
29
+
30
+ `.trim();
@@ -0,0 +1,43 @@
1
+ import { INpmConfig } from "../../../commons";
2
+ import { FsUtil } from "@simplysm/sd-core-node";
3
+ import path from "path";
4
+
5
+ export const fc_package_npmconfig = (opt: {
6
+ projPath: string;
7
+ name: string;
8
+ description: string;
9
+ isModule: boolean;
10
+ main?: string;
11
+ types?: string;
12
+ dependencies: Record<string, string>;
13
+ }): string => {
14
+ const projNpmConfig = FsUtil.readJson(path.resolve(opt.projPath, "package.json")) as INpmConfig;
15
+
16
+ if (projNpmConfig.repository?.url === undefined) {
17
+ throw new Error("프로젝트 package.json 파일에 reposotry.url 이 설정되어있지 않습니다.");
18
+ }
19
+
20
+ return JSON.stringify({
21
+ name: `@${projNpmConfig.name}/${opt.name}`,
22
+ version: `${projNpmConfig.version}`,
23
+ description: `${projNpmConfig.description} - ${opt.description}`,
24
+ author: projNpmConfig.author,
25
+ repository: {
26
+ type: "git",
27
+ url: projNpmConfig.repository.url,
28
+ directory: `packages/${opt.name}`
29
+ },
30
+ license: "UNLICENSED",
31
+ ...opt.isModule ? {
32
+ type: "module"
33
+ } : {},
34
+ private: true,
35
+ sideEffects: false,
36
+ engines: {
37
+ node: "^16"
38
+ },
39
+ ...opt.types !== undefined ? { types: opt.types } : {},
40
+ ...opt.main !== undefined ? { main: opt.main } : {},
41
+ ...Object.keys(opt.dependencies).length > 0 ? { dependencies: opt.dependencies } : {}
42
+ }, undefined, 2);
43
+ };
@@ -0,0 +1,11 @@
1
+ export const fc_package_tsconfig = (opt: { isModule: boolean; useDom: boolean }): string => JSON.stringify({
2
+ extends: "../../tsconfig.json",
3
+ compilerOptions: {
4
+ ...!opt.isModule ? { module: "CommonJS" } : {},
5
+ lib: [
6
+ "ES2020",
7
+ ...opt.useDom ? ["DOM"] : []
8
+ ],
9
+ outDir: "./dist"
10
+ }
11
+ }, undefined, 2);
@@ -0,0 +1,36 @@
1
+ export const fc_package_AppModule = (): string => /* language=ts */ `
2
+
3
+ import { ApplicationRef, DoBootstrap, NgModule } from "@angular/core";
4
+ import { BrowserModule } from "@angular/platform-browser";
5
+ import {
6
+ SdAngularModule,
7
+ SdCanDeactivateGuardModule,
8
+ SdToastProvider,
9
+ SdToastProviderModule
10
+ } from "@simplysm/sd-angular";
11
+ import { AppPageModule } from "./_modules/AppPageModule";
12
+ import { AppPage } from "./AppPage";
13
+
14
+ @NgModule({
15
+ imports: [
16
+ BrowserModule,
17
+
18
+ SdAngularModule.forRoot(),
19
+ SdToastProviderModule,
20
+ SdCanDeactivateGuardModule,
21
+
22
+ AppPageModule
23
+ ]
24
+ })
25
+ export class AppModule implements DoBootstrap {
26
+ public constructor(private readonly _toast: SdToastProvider) {
27
+ }
28
+
29
+ public async ngDoBootstrap(appRef: ApplicationRef): Promise<void> {
30
+ this._toast.alertThemes = ["danger"];
31
+
32
+ appRef.bootstrap(AppPage);
33
+ }
34
+ }
35
+
36
+ `.trim();
@@ -0,0 +1,33 @@
1
+ export const fc_package_AppPage = (): string => /* language=ts */ `
2
+
3
+ import { ChangeDetectionStrategy, Component, HostListener } from "@angular/core";
4
+ import { DevModal } from "./modals/DevModal";
5
+ import { SdModalProvider } from "@simplysm/sd-angular";
6
+
7
+ @Component({
8
+ selector: "app-root",
9
+ changeDetection: ChangeDetectionStrategy.OnPush,
10
+ template: \`APP_PAGE\`
11
+ })
12
+ export class AppPage {
13
+ public constructor(private readonly _modal: SdModalProvider) {
14
+ }
15
+
16
+ @HostListener("document:keydown", ["$event"])
17
+ public async onKeydown(event: KeyboardEvent): Promise<void> {
18
+ if (
19
+ process.env["NODE_ENV"] !== "production"
20
+ && event.ctrlKey
21
+ && event.altKey
22
+ && event.shiftKey
23
+ && event.key === "F12"
24
+ ) {
25
+ event.preventDefault();
26
+ event.stopPropagation();
27
+
28
+ await this._modal.showAsync(DevModal, "DEV", undefined, { key: "dev-modal" });
29
+ }
30
+ }
31
+ }
32
+
33
+ `.trim();
@@ -0,0 +1,5 @@
1
+ export const fc_package_appicons = (): string => /* language=ts */ `
2
+
3
+ export const appIcons = {};
4
+
5
+ `.trim();
@@ -0,0 +1,18 @@
1
+ export const fc_package_client_main = (): string => /* language=ts */ `
2
+
3
+ import { enableProdMode } from "@angular/core";
4
+ import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
5
+ import { AppModule } from "./AppModule";
6
+
7
+ if (process.env["NODE_ENV"] === "production") {
8
+ enableProdMode();
9
+ }
10
+
11
+ platformBrowserDynamic().bootstrapModule(AppModule)
12
+ .catch(err => {
13
+ // eslint-disable-next-line no-console
14
+ console.error(err);
15
+ });
16
+
17
+
18
+ `.trim();