@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.
- package/README.md +48 -0
- package/dist/bin/sd-cli.mjs +171 -13
- package/dist/build-tool/SdCliElectron.mjs +3 -2
- package/dist/builder/SdCliClientBuilder.mjs +33 -35
- package/dist/builder/SdCliServerBuilder.d.ts +1 -0
- package/dist/builder/SdCliServerBuilder.mjs +58 -22
- package/dist/builder/SdCliTsLibBuilder.mjs +18 -12
- package/dist/commons.d.ts +9 -1
- package/dist/entry-points/SdCliProjectGenerator.d.ts +37 -0
- package/dist/entry-points/SdCliProjectGenerator.mjs +288 -0
- package/dist/entry-points/SdCliWorkspace.d.ts +6 -3
- package/dist/entry-points/SdCliWorkspace.mjs +147 -120
- package/dist/entry-points/file/base/fc_package_eslintrc.d.ts +3 -0
- package/dist/entry-points/file/base/fc_package_eslintrc.mjs +31 -0
- package/dist/entry-points/file/base/fc_package_npmconfig.d.ts +9 -0
- package/dist/entry-points/file/base/fc_package_npmconfig.mjs +32 -0
- package/dist/entry-points/file/base/fc_package_tsconfig.d.ts +4 -0
- package/dist/entry-points/file/base/fc_package_tsconfig.mjs +12 -0
- package/dist/entry-points/file/client/fc_package_AppModule.d.ts +1 -0
- package/dist/entry-points/file/client/fc_package_AppModule.mjs +37 -0
- package/dist/entry-points/file/client/fc_package_AppPage.d.ts +1 -0
- package/dist/entry-points/file/client/fc_package_AppPage.mjs +34 -0
- package/dist/entry-points/file/client/fc_package_appicons.d.ts +1 -0
- package/dist/entry-points/file/client/fc_package_appicons.mjs +6 -0
- package/dist/entry-points/file/client/fc_package_client_main.d.ts +1 -0
- package/dist/entry-points/file/client/fc_package_client_main.mjs +19 -0
- package/dist/entry-points/file/client/fc_package_index.d.ts +3 -0
- package/dist/entry-points/file/client/fc_package_index.mjs +88 -0
- package/dist/entry-points/file/client/fc_package_manifest.d.ts +5 -0
- package/dist/entry-points/file/client/fc_package_manifest.mjs +54 -0
- package/dist/entry-points/file/client/fc_package_polyfills.d.ts +1 -0
- package/dist/entry-points/file/client/fc_package_polyfills.mjs +11 -0
- package/dist/entry-points/file/client/fc_package_styles.d.ts +1 -0
- package/dist/entry-points/file/client/fc_package_styles.mjs +7 -0
- package/dist/entry-points/file/db/fc_package_DbContext.d.ts +3 -0
- package/dist/entry-points/file/db/fc_package_DbContext.mjs +14 -0
- package/dist/entry-points/file/db/fc_package_DbModel.d.ts +4 -0
- package/dist/entry-points/file/db/fc_package_DbModel.mjs +12 -0
- package/dist/entry-points/file/project/fc_project_editor_config.d.ts +1 -0
- package/dist/entry-points/file/project/fc_project_editor_config.mjs +22 -0
- package/dist/entry-points/file/project/fc_project_eslintrc.d.ts +1 -0
- package/dist/entry-points/file/project/fc_project_eslintrc.mjs +30 -0
- package/dist/entry-points/file/project/fc_project_gitattributes.d.ts +1 -0
- package/dist/entry-points/file/project/fc_project_gitattributes.mjs +6 -0
- package/dist/entry-points/file/project/fc_project_gitignore.d.ts +1 -0
- package/dist/entry-points/file/project/fc_project_gitignore.mjs +17 -0
- package/dist/entry-points/file/project/fc_project_npmconfig.d.ts +7 -0
- package/dist/entry-points/file/project/fc_project_npmconfig.mjs +35 -0
- package/dist/entry-points/file/project/fc_project_readme.d.ts +3 -0
- package/dist/entry-points/file/project/fc_project_readme.mjs +12 -0
- package/dist/entry-points/file/project/fc_project_simplysm.d.ts +1 -0
- package/dist/entry-points/file/project/fc_project_simplysm.mjs +2 -0
- package/dist/entry-points/file/project/fc_project_tsconfig.d.ts +1 -0
- package/dist/entry-points/file/project/fc_project_tsconfig.mjs +26 -0
- package/dist/entry-points/file/server/fc_package_server_main.d.ts +4 -0
- package/dist/entry-points/file/server/fc_package_server_main.mjs +60 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.mjs +24 -1
- package/dist/packages/SdCliPackage.d.ts +1 -0
- package/dist/packages/SdCliPackage.mjs +68 -62
- package/dist/utils/SdCliBuildResultUtil.mjs +2 -2
- package/dist/worker/server-worker.d.ts +1 -0
- package/dist/worker/server-worker.mjs +67 -0
- package/package.json +10 -8
- package/src/bin/sd-cli.ts +198 -12
- package/src/build-tool/SdCliElectron.ts +2 -1
- package/src/builder/SdCliClientBuilder.ts +36 -36
- package/src/builder/SdCliJsLibBuilder.ts +70 -70
- package/src/builder/SdCliServerBuilder.ts +58 -20
- package/src/builder/SdCliTsLibBuilder.ts +450 -445
- package/src/commons.ts +7 -1
- package/src/entry-points/SdCliProjectGenerator.ts +353 -0
- package/src/entry-points/SdCliWorkspace.ts +171 -137
- package/src/entry-points/file/assets/assets/icons/icon-128x128.png +0 -0
- package/src/entry-points/file/assets/assets/icons/icon-144x144.png +0 -0
- package/src/entry-points/file/assets/assets/icons/icon-152x152.png +0 -0
- package/src/entry-points/file/assets/assets/icons/icon-192x192.png +0 -0
- package/src/entry-points/file/assets/assets/icons/icon-384x384.png +0 -0
- package/src/entry-points/file/assets/assets/icons/icon-512x512.png +0 -0
- package/src/entry-points/file/assets/assets/icons/icon-72x72.png +0 -0
- package/src/entry-points/file/assets/assets/icons/icon-96x96.png +0 -0
- package/src/entry-points/file/assets/favicon.ico +0 -0
- package/src/entry-points/file/base/fc_package_eslintrc.ts +30 -0
- package/src/entry-points/file/base/fc_package_npmconfig.ts +43 -0
- package/src/entry-points/file/base/fc_package_tsconfig.ts +11 -0
- package/src/entry-points/file/client/fc_package_AppModule.ts +36 -0
- package/src/entry-points/file/client/fc_package_AppPage.ts +33 -0
- package/src/entry-points/file/client/fc_package_appicons.ts +5 -0
- package/src/entry-points/file/client/fc_package_client_main.ts +18 -0
- package/src/entry-points/file/client/fc_package_index.ts +87 -0
- package/src/entry-points/file/client/fc_package_manifest.ts +53 -0
- package/src/entry-points/file/client/fc_package_polyfills.ts +10 -0
- package/src/entry-points/file/client/fc_package_styles.ts +6 -0
- package/src/entry-points/file/db/fc_package_DbContext.ts +14 -0
- package/src/entry-points/file/db/fc_package_DbModel.ts +11 -0
- package/src/entry-points/file/project/fc_project_editor_config.ts +21 -0
- package/src/entry-points/file/project/fc_project_eslintrc.ts +29 -0
- package/src/entry-points/file/project/fc_project_gitattributes.ts +5 -0
- package/src/entry-points/file/project/fc_project_gitignore.ts +16 -0
- package/src/entry-points/file/project/fc_project_npmconfig.ts +34 -0
- package/src/entry-points/file/project/fc_project_readme.ts +11 -0
- package/src/entry-points/file/project/fc_project_simplysm.ts +1 -0
- package/src/entry-points/file/project/fc_project_tsconfig.ts +25 -0
- package/src/entry-points/file/server/fc_package_server_main.ts +62 -0
- package/src/index.ts +23 -0
- package/src/packages/SdCliPackage.ts +75 -58
- package/src/utils/SdCliBuildResultUtil.ts +76 -76
- package/src/worker/server-worker.ts +72 -0
- package/tsconfig-build.json +2 -1
- package/tsconfig.json +2 -1
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { FsUtil, Logger,
|
|
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 {
|
|
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
|
|
13
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
123
|
+
pathProxy: [],
|
|
141
124
|
clientInfos: []
|
|
142
125
|
});
|
|
143
|
-
if (serverInfo.
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|
150
|
-
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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.
|
|
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(`${
|
|
460
|
+
clientHrefs.push(`${serverInfo.url}/${clientInfo.pkgKey}/`);
|
|
428
461
|
}
|
|
429
462
|
else if (platform === "electron") {
|
|
430
|
-
clientHrefs.push(`sd-cli run-electron ${clientInfo.pkgKey}
|
|
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(`${
|
|
468
|
+
clientHrefs.push(`${serverInfo.url}/${clientInfo.pkgKey}/${platform}/`);
|
|
436
469
|
}
|
|
437
470
|
else {
|
|
438
|
-
clientHrefs.push(`sd-cli run-cordova ${target} ${clientInfo.pkgKey}
|
|
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
|
-
|
|
453
|
-
|
|
485
|
+
worker?: cp.ChildProcess;
|
|
486
|
+
url?: string;
|
|
487
|
+
pathProxy: { from: string; to: string }[];
|
|
454
488
|
clientInfos: { pkgKey: string; platforms: string[]; cordovaTargets: string[] }[];
|
|
455
489
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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,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();
|