@simplysm/sd-cli 11.0.39 → 11.1.1
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/dist/build-cluster.d.ts +1 -1
- package/dist/build-cluster.js +181 -181
- package/dist/build-tools/SdCliCordova.d.ts +21 -21
- package/dist/build-tools/SdCliCordova.js +217 -217
- package/dist/build-tools/SdCliIndexFileGenerator.d.ts +5 -5
- package/dist/build-tools/SdCliIndexFileGenerator.js +50 -50
- package/dist/build-tools/SdCliNgRoutesFileGenerator.d.ts +4 -4
- package/dist/build-tools/SdCliNgRoutesFileGenerator.js +57 -57
- package/dist/build-tools/SdLinter.d.ts +5 -5
- package/dist/build-tools/SdLinter.js +54 -54
- package/dist/build-tools/SdNgBundler.d.ts +35 -35
- package/dist/build-tools/SdNgBundler.js +545 -533
- package/dist/build-tools/SdNgBundler.js.map +1 -1
- package/dist/build-tools/SdNgBundlerContext.d.ts +16 -16
- package/dist/build-tools/SdNgBundlerContext.js +97 -97
- package/dist/build-tools/SdTsBundler.d.ts +15 -15
- package/dist/build-tools/SdTsBundler.js +87 -87
- package/dist/build-tools/SdTsCompiler.d.ts +29 -29
- package/dist/build-tools/SdTsCompiler.js +227 -227
- package/dist/builders/SdCliClientBuilder.d.ts +18 -18
- package/dist/builders/SdCliClientBuilder.js +129 -129
- package/dist/builders/SdCliJsLibLinter.d.ts +14 -14
- package/dist/builders/SdCliJsLibLinter.js +59 -59
- package/dist/builders/SdCliServerBuilder.d.ts +20 -20
- package/dist/builders/SdCliServerBuilder.js +215 -215
- package/dist/builders/SdCliTsLibBuilder.d.ts +17 -17
- package/dist/builders/SdCliTsLibBuilder.js +79 -79
- package/dist/commons.d.ts +132 -132
- package/dist/commons.js +1 -1
- package/dist/entry/SdCliElectron.d.ts +12 -12
- package/dist/entry/SdCliElectron.js +99 -99
- package/dist/entry/SdCliLocalUpdate.d.ts +12 -12
- package/dist/entry/SdCliLocalUpdate.js +90 -90
- package/dist/entry/SdCliProject.d.ts +26 -26
- package/dist/entry/SdCliProject.js +477 -477
- package/dist/index.d.ts +19 -19
- package/dist/index.js +19 -19
- package/dist/sd-cli.d.ts +2 -2
- package/dist/sd-cli.js +210 -210
- package/dist/server-worker.d.ts +1 -1
- package/dist/server-worker.js +38 -38
- package/dist/utils/SdCliBuildResultUtil.d.ts +6 -6
- package/dist/utils/SdCliBuildResultUtil.js +37 -37
- package/dist/utils/SdMemoryLoadResultCache.d.ts +9 -9
- package/dist/utils/SdMemoryLoadResultCache.js +34 -34
- package/dist/utils/SdSourceFileCache.d.ts +6 -6
- package/dist/utils/SdSourceFileCache.js +14 -14
- package/dist/utils/SdSourceFileCache.js.map +1 -1
- package/package.json +19 -19
- package/src/build-tools/SdNgBundler.ts +21 -12
- package/src/utils/SdSourceFileCache.ts +1 -1
|
@@ -1,478 +1,478 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { FsUtil, Logger, PathUtil, SdProcess } from "@simplysm/sd-core-node";
|
|
3
|
-
import cp from "child_process";
|
|
4
|
-
import { fileURLToPath, pathToFileURL } from "url";
|
|
5
|
-
import { SdCliBuildResultUtil } from "../utils/SdCliBuildResultUtil";
|
|
6
|
-
import semver from "semver";
|
|
7
|
-
import { NeverEntryError, StringUtil, Wait } from "@simplysm/sd-core-common";
|
|
8
|
-
import { SdStorage } from "@simplysm/sd-storage";
|
|
9
|
-
import { SdCliLocalUpdate } from "./SdCliLocalUpdate";
|
|
10
|
-
import xml2js from "xml2js";
|
|
11
|
-
export class SdCliProject {
|
|
12
|
-
static async watchAsync(opt) {
|
|
13
|
-
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "watchAsync"]);
|
|
14
|
-
logger.debug("프로젝트 설정 가져오기...");
|
|
15
|
-
const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(true, opt.optNames);
|
|
16
|
-
if (projConf.localUpdates) {
|
|
17
|
-
logger.debug("로컬 라이브러리 업데이트 변경감지 시작...");
|
|
18
|
-
await SdCliLocalUpdate.watchAsync({
|
|
19
|
-
confFileRelPath: opt.confFileRelPath,
|
|
20
|
-
optNames: opt.optNames
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
logger.debug("프로젝트 package.json 가져오기...");
|
|
24
|
-
const projNpmConf = (await FsUtil.readJsonAsync(path.resolve(process.cwd(), "package.json")));
|
|
25
|
-
logger.debug("패키지 목록 구성...");
|
|
26
|
-
if (!projNpmConf.workspaces) {
|
|
27
|
-
throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
|
|
28
|
-
}
|
|
29
|
-
const allPkgPaths = await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtil.globAsync(item));
|
|
30
|
-
let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
|
|
31
|
-
if (opt.pkgNames.length !== 0) {
|
|
32
|
-
pkgPaths = pkgPaths.filter((pkgPath) => opt.pkgNames.includes(path.basename(pkgPath)));
|
|
33
|
-
}
|
|
34
|
-
logger.debug("패키지 존재 확인...");
|
|
35
|
-
const notExistsPkgs = Object.keys(projConf.packages).filter((pkgConfKey) => allPkgPaths.every((pkgPath) => path.basename(pkgPath) !== pkgConfKey));
|
|
36
|
-
if (notExistsPkgs.length > 0) {
|
|
37
|
-
throw new Error("패키지를 찾을 수 없습니다. (" + notExistsPkgs.join(", ") + ")");
|
|
38
|
-
}
|
|
39
|
-
logger.debug("빌드 프로세스 준비...");
|
|
40
|
-
const cluster = await this._prepareClusterAsync();
|
|
41
|
-
logger.debug("빌드 프로세스 이벤트 준비...");
|
|
42
|
-
const resultCache = new Map();
|
|
43
|
-
let busyCount = 0;
|
|
44
|
-
const serverInfoMap = new Map();
|
|
45
|
-
cluster.on("message", (message) => {
|
|
46
|
-
if (message.type === "change") {
|
|
47
|
-
if (busyCount === 0) {
|
|
48
|
-
logger.log("빌드를 시작합니다...");
|
|
49
|
-
}
|
|
50
|
-
busyCount++;
|
|
51
|
-
}
|
|
52
|
-
else if (message.type === "complete") {
|
|
53
|
-
for (const affectedFilePath of message.result.affectedFilePaths) {
|
|
54
|
-
resultCache.delete(affectedFilePath);
|
|
55
|
-
}
|
|
56
|
-
for (const buildResult of message.result.buildResults) {
|
|
57
|
-
const cacheItem = resultCache.getOrCreate(buildResult.filePath ?? "none", []);
|
|
58
|
-
cacheItem.push(buildResult);
|
|
59
|
-
}
|
|
60
|
-
const pkgConf = message.req.projConf.packages[path.basename(message.req.pkgPath)];
|
|
61
|
-
if (pkgConf.type === "server") {
|
|
62
|
-
const pkgName = path.basename(message.req.pkgPath);
|
|
63
|
-
const serverInfo = serverInfoMap.getOrCreate(pkgName, {
|
|
64
|
-
hasChanges: false,
|
|
65
|
-
hasClientChanges: false,
|
|
66
|
-
pathProxy: {},
|
|
67
|
-
// changeFilePaths: []
|
|
68
|
-
});
|
|
69
|
-
serverInfo.pkgPath = message.req.pkgPath;
|
|
70
|
-
serverInfo.hasChanges = true;
|
|
71
|
-
}
|
|
72
|
-
if (pkgConf.type === "client") {
|
|
73
|
-
const pkgName = path.basename(message.req.pkgPath);
|
|
74
|
-
if (pkgConf.server !== undefined) {
|
|
75
|
-
const serverInfo = serverInfoMap.getOrCreate(pkgConf.server, {
|
|
76
|
-
hasChanges: false,
|
|
77
|
-
hasClientChanges: false,
|
|
78
|
-
pathProxy: {},
|
|
79
|
-
// changeFilePaths: []
|
|
80
|
-
});
|
|
81
|
-
serverInfo.pathProxy[pkgName] = path.resolve(message.req.pkgPath, "dist");
|
|
82
|
-
// serverInfo.changeFilePaths.push(...message.result!.affectedFilePaths);
|
|
83
|
-
serverInfo.hasClientChanges = true;
|
|
84
|
-
// serverInfo.worker?.send({type: "broadcastReload"});
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
const serverInfo = serverInfoMap.getOrCreate(pkgName, {
|
|
88
|
-
hasChanges: false,
|
|
89
|
-
hasClientChanges: false,
|
|
90
|
-
pathProxy: {},
|
|
91
|
-
// changeFilePaths: []
|
|
92
|
-
});
|
|
93
|
-
serverInfo.port = message.result.port;
|
|
94
|
-
// serverInfo.changeFilePaths.push(...message.result!.affectedFilePaths);
|
|
95
|
-
serverInfo.hasClientChanges = true;
|
|
96
|
-
// serverInfo.worker?.send({type: "broadcastReload"});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
setTimeout(async () => {
|
|
100
|
-
busyCount--;
|
|
101
|
-
if (busyCount === 0) {
|
|
102
|
-
for (const serverPkgName of serverInfoMap.keys()) {
|
|
103
|
-
const serverInfo = serverInfoMap.get(serverPkgName);
|
|
104
|
-
if (serverInfo.pkgPath !== undefined && serverInfo.hasChanges) {
|
|
105
|
-
logger.debug("서버 재시작...");
|
|
106
|
-
try {
|
|
107
|
-
const restartServerResult = await this._restartServerAsync(serverInfo.pkgPath, projConf.packages[path.basename(serverInfo.pkgPath)], serverInfo.worker);
|
|
108
|
-
serverInfo.worker = restartServerResult.worker;
|
|
109
|
-
serverInfo.port = restartServerResult.port;
|
|
110
|
-
serverInfo.hasChanges = false;
|
|
111
|
-
}
|
|
112
|
-
catch (err) {
|
|
113
|
-
logger.error(err);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (serverInfo.worker) {
|
|
117
|
-
logger.debug("클라이언트 설정...");
|
|
118
|
-
serverInfo.worker.send({
|
|
119
|
-
type: "setPathProxy",
|
|
120
|
-
pathProxy: serverInfo.pathProxy
|
|
121
|
-
});
|
|
122
|
-
if (serverInfo.hasClientChanges) {
|
|
123
|
-
logger.debug("클라이언트 새로고침...");
|
|
124
|
-
serverInfo.worker.send({ type: "broadcastReload" });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
const clientPaths = [];
|
|
129
|
-
for (const serverInfo of serverInfoMap.values()) {
|
|
130
|
-
if (Object.keys(serverInfo.pathProxy).length > 0) {
|
|
131
|
-
for (const proxyPath of Object.keys(serverInfo.pathProxy)) {
|
|
132
|
-
clientPaths.push(`http://localhost:${serverInfo.port}/${proxyPath}/`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
clientPaths.push(`http://localhost:${serverInfo.port}/`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (clientPaths.length > 0) {
|
|
140
|
-
logger.info("클라이언트 개발 서버 접속 주소\n" + clientPaths.join("\n"));
|
|
141
|
-
}
|
|
142
|
-
const buildResults = Array.from(resultCache.values()).mapMany();
|
|
143
|
-
this._logging(buildResults, logger);
|
|
144
|
-
}
|
|
145
|
-
}, 300);
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
logger.debug("빌드 프로세스 명령 전송...");
|
|
149
|
-
busyCount++;
|
|
150
|
-
logger.log("빌드를 시작합니다...");
|
|
151
|
-
await pkgPaths.parallelAsync(async (pkgPath) => {
|
|
152
|
-
const pkgConf = projConf.packages[path.basename(pkgPath)];
|
|
153
|
-
if (pkgConf.type === "client") {
|
|
154
|
-
const builderKeys = Object.keys(pkgConf.builder ?? { web: {} });
|
|
155
|
-
await builderKeys.parallelAsync(async (builderKey) => {
|
|
156
|
-
await this._runCommandAsync(cluster, "watch", projConf, pkgPath, builderKey);
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
await this._runCommandAsync(cluster, "watch", projConf, pkgPath);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
busyCount--;
|
|
164
|
-
if (busyCount === 0) {
|
|
165
|
-
const buildResults = Array.from(resultCache.values()).mapMany();
|
|
166
|
-
this._logging(buildResults, logger);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
static async buildAsync(opt) {
|
|
170
|
-
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "buildAsync"]);
|
|
171
|
-
logger.debug("프로젝트 설정 가져오기...");
|
|
172
|
-
const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(false, opt.optNames);
|
|
173
|
-
logger.debug("프로젝트 package.json 가져오기...");
|
|
174
|
-
const projNpmConf = (await FsUtil.readJsonAsync(path.resolve(process.cwd(), "package.json")));
|
|
175
|
-
logger.debug("패키지 목록 구성...");
|
|
176
|
-
if (!projNpmConf.workspaces) {
|
|
177
|
-
throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
|
|
178
|
-
}
|
|
179
|
-
const allPkgPaths = await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtil.globAsync(item));
|
|
180
|
-
let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
|
|
181
|
-
if (opt.pkgNames.length !== 0) {
|
|
182
|
-
pkgPaths = pkgPaths.filter((pkgPath) => opt.pkgNames.includes(path.basename(pkgPath)));
|
|
183
|
-
}
|
|
184
|
-
logger.debug("프로젝트 및 패키지 버전 설정...");
|
|
185
|
-
await this._upgradeVersionAsync(projNpmConf, allPkgPaths);
|
|
186
|
-
logger.debug("빌드 프로세스 준비...");
|
|
187
|
-
const cluster = await this._prepareClusterAsync();
|
|
188
|
-
logger.debug("빌드 프로세스 명령 전달...");
|
|
189
|
-
const results = (await pkgPaths.parallelAsync(async (pkgPath) => {
|
|
190
|
-
const pkgConf = projConf.packages[path.basename(pkgPath)];
|
|
191
|
-
if (pkgConf.type === "client") {
|
|
192
|
-
const builderKeys = Object.keys(pkgConf.builder ?? { web: {} });
|
|
193
|
-
return (await builderKeys.parallelAsync(async (builderKey) => {
|
|
194
|
-
return await this._runCommandAsync(cluster, "build", projConf, pkgPath, builderKey);
|
|
195
|
-
})).mapMany();
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
return await this._runCommandAsync(cluster, "build", projConf, pkgPath);
|
|
199
|
-
}
|
|
200
|
-
})).mapMany();
|
|
201
|
-
logger.debug("빌드 프로세스 닫기...");
|
|
202
|
-
this._closeCluster(cluster);
|
|
203
|
-
this._logging(results, logger);
|
|
204
|
-
}
|
|
205
|
-
static async publishAsync(opt) {
|
|
206
|
-
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "publishAsync"]);
|
|
207
|
-
logger.debug("프로젝트 설정 가져오기...");
|
|
208
|
-
const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(false, opt.optNames);
|
|
209
|
-
logger.debug("프로젝트 package.json 가져오기...");
|
|
210
|
-
const projNpmConf = (await FsUtil.readJsonAsync(path.resolve(process.cwd(), "package.json")));
|
|
211
|
-
if (opt.noBuild) {
|
|
212
|
-
logger.warn("빌드하지 않고, 배포하는것은 상당히 위험합니다.");
|
|
213
|
-
await this._waitSecMessageAsync("프로세스를 중지하려면, 'CTRL+C'를 누르세요.", 5);
|
|
214
|
-
}
|
|
215
|
-
// GIT 사용중일 경우, 커밋되지 않은 수정사항이 있는지 확인
|
|
216
|
-
if (FsUtil.exists(path.resolve(process.cwd(), ".git"))) {
|
|
217
|
-
logger.debug("GIT 커밋여부 확인...");
|
|
218
|
-
const gitStatusResult = await SdProcess.spawnAsync("git status");
|
|
219
|
-
if (gitStatusResult.includes("Changes") || gitStatusResult.includes("Untracked")) {
|
|
220
|
-
throw new Error("커밋되지 않은 정보가 있습니다.\n" + gitStatusResult);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
logger.debug("패키지 목록 구성...");
|
|
224
|
-
if (!projNpmConf.workspaces) {
|
|
225
|
-
throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
|
|
226
|
-
}
|
|
227
|
-
const allPkgPaths = await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtil.globAsync(item));
|
|
228
|
-
let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
|
|
229
|
-
if (opt.pkgNames.length !== 0) {
|
|
230
|
-
pkgPaths = pkgPaths.filter((pkgPath) => opt.pkgNames.includes(path.basename(pkgPath)));
|
|
231
|
-
}
|
|
232
|
-
logger.debug("프로젝트 및 패키지 버전 설정...");
|
|
233
|
-
await this._upgradeVersionAsync(projNpmConf, allPkgPaths);
|
|
234
|
-
// 빌드
|
|
235
|
-
if (!opt.noBuild) {
|
|
236
|
-
logger.debug("빌드 프로세스 준비...");
|
|
237
|
-
const cluster = await this._prepareClusterAsync();
|
|
238
|
-
logger.debug("빌드 프로세스 명령 전달...");
|
|
239
|
-
const results = (await pkgPaths.parallelAsync(async (pkgPath) => {
|
|
240
|
-
const pkgConf = projConf.packages[path.basename(pkgPath)];
|
|
241
|
-
if (pkgConf.type === "client") {
|
|
242
|
-
const builderKeys = Object.keys(pkgConf.builder ?? { web: {} });
|
|
243
|
-
return (await builderKeys.parallelAsync(async (builderKey) => {
|
|
244
|
-
return await this._runCommandAsync(cluster, "build", projConf, pkgPath, builderKey);
|
|
245
|
-
})).mapMany();
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
return await this._runCommandAsync(cluster, "build", projConf, pkgPath);
|
|
249
|
-
}
|
|
250
|
-
})).mapMany();
|
|
251
|
-
logger.debug("빌드 프로세스 닫기...");
|
|
252
|
-
this._closeCluster(cluster);
|
|
253
|
-
this._logging(results, logger);
|
|
254
|
-
}
|
|
255
|
-
// GIT 사용중일경우, 새 버전 커밋 및 TAG 생성
|
|
256
|
-
if (FsUtil.exists(path.resolve(process.cwd(), ".git"))) {
|
|
257
|
-
logger.debug("새 버전 커밋 및 TAG 생성...");
|
|
258
|
-
await SdProcess.spawnAsync("git add .");
|
|
259
|
-
await SdProcess.spawnAsync(`git commit -m "v${projNpmConf.version}"`);
|
|
260
|
-
await SdProcess.spawnAsync(`git tag -a "v${projNpmConf.version}" -m "v${projNpmConf.version}"`);
|
|
261
|
-
logger.debug("새 버전 푸쉬...");
|
|
262
|
-
await SdProcess.spawnAsync("git push");
|
|
263
|
-
await SdProcess.spawnAsync("git push --tags");
|
|
264
|
-
}
|
|
265
|
-
logger.debug("배포 시작...");
|
|
266
|
-
await pkgPaths.parallelAsync(async (pkgPath) => {
|
|
267
|
-
const pkgName = path.basename(pkgPath);
|
|
268
|
-
const pkgConf = projConf.packages[pkgName];
|
|
269
|
-
if (pkgConf?.publish == null)
|
|
270
|
-
return;
|
|
271
|
-
logger.debug(`[${pkgName}] 배포 시작...`);
|
|
272
|
-
await this._publishPkgAsync(pkgPath, pkgConf.publish);
|
|
273
|
-
logger.debug(`[${pkgName}] 배포 완료`);
|
|
274
|
-
});
|
|
275
|
-
logger.info(`모든 배포가 완료되었습니다. (v${projNpmConf.version})`);
|
|
276
|
-
}
|
|
277
|
-
static async _publishPkgAsync(pkgPath, pkgPubConf) {
|
|
278
|
-
if (pkgPubConf === "npm") {
|
|
279
|
-
await SdProcess.spawnAsync("yarn npm publish --access public", { cwd: pkgPath });
|
|
280
|
-
}
|
|
281
|
-
else if (pkgPubConf?.type === "local-directory") {
|
|
282
|
-
const pkgNpmConf = (await FsUtil.readJsonAsync(path.resolve(pkgPath, "package.json")));
|
|
283
|
-
const targetRootPath = pkgPubConf.path.replace(/%([^%]*)%/g, (item) => {
|
|
284
|
-
const envName = item.replace(/%/g, "");
|
|
285
|
-
if (!StringUtil.isNullOrEmpty(pkgNpmConf.version) && envName === "SD_VERSION") {
|
|
286
|
-
return pkgNpmConf.version;
|
|
287
|
-
}
|
|
288
|
-
return process.env[envName] ?? item;
|
|
289
|
-
});
|
|
290
|
-
const filePaths = await FsUtil.globAsync(path.resolve(pkgPath, "dist", "**", "*"), {
|
|
291
|
-
dot: true,
|
|
292
|
-
nodir: true
|
|
293
|
-
});
|
|
294
|
-
await filePaths.parallelAsync(async (filePath) => {
|
|
295
|
-
const relativeFilePath = path.relative(path.resolve(pkgPath, "dist"), filePath);
|
|
296
|
-
const targetPath = PathUtil.posix(targetRootPath, relativeFilePath);
|
|
297
|
-
await FsUtil.copyAsync(filePath, targetPath);
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
else if (pkgPubConf?.type === "ftp" || pkgPubConf?.type === "ftps" || pkgPubConf?.type === "sftp") {
|
|
301
|
-
const ftp = await SdStorage.connectAsync(pkgPubConf.type, {
|
|
302
|
-
host: pkgPubConf.host,
|
|
303
|
-
port: pkgPubConf.port,
|
|
304
|
-
user: pkgPubConf.user,
|
|
305
|
-
pass: pkgPubConf.pass
|
|
306
|
-
});
|
|
307
|
-
await ftp.uploadDirAsync(path.resolve(pkgPath, "dist"), pkgPubConf.path ?? "/");
|
|
308
|
-
await ftp.closeAsync();
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
throw new NeverEntryError();
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
static async _waitSecMessageAsync(msg, sec) {
|
|
315
|
-
for (let i = sec; i > 0; i--) {
|
|
316
|
-
if (i !== sec) {
|
|
317
|
-
process.stdout.cursorTo(0);
|
|
318
|
-
}
|
|
319
|
-
process.stdout.write(`${msg} ${i}`);
|
|
320
|
-
await Wait.time(1000);
|
|
321
|
-
}
|
|
322
|
-
process.stdout.cursorTo(0);
|
|
323
|
-
process.stdout.clearLine(0);
|
|
324
|
-
}
|
|
325
|
-
static async _upgradeVersionAsync(projNpmConf, allPkgPaths) {
|
|
326
|
-
// 작업공간 package.json 버전 설정
|
|
327
|
-
const newVersion = semver.inc(projNpmConf.version, "patch");
|
|
328
|
-
projNpmConf.version = newVersion;
|
|
329
|
-
const pkgNames = await allPkgPaths.mapAsync(async (pkgPath) => {
|
|
330
|
-
const pkgNpmConf = await FsUtil.readJsonAsync(path.resolve(pkgPath, "package.json"));
|
|
331
|
-
return pkgNpmConf.name;
|
|
332
|
-
});
|
|
333
|
-
const updateDepVersion = (deps) => {
|
|
334
|
-
if (!deps)
|
|
335
|
-
return;
|
|
336
|
-
for (const depName of Object.keys(deps)) {
|
|
337
|
-
if (pkgNames.includes(depName)) {
|
|
338
|
-
deps[depName] = newVersion;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
updateDepVersion(projNpmConf.dependencies);
|
|
343
|
-
updateDepVersion(projNpmConf.optionalDependencies);
|
|
344
|
-
updateDepVersion(projNpmConf.devDependencies);
|
|
345
|
-
updateDepVersion(projNpmConf.peerDependencies);
|
|
346
|
-
const projNpmConfFilePath = path.resolve(process.cwd(), "package.json");
|
|
347
|
-
await FsUtil.writeJsonAsync(projNpmConfFilePath, projNpmConf, { space: 2 });
|
|
348
|
-
// 각 패키지 package.json 버전 설정
|
|
349
|
-
await allPkgPaths.parallelAsync(async (pkgPath) => {
|
|
350
|
-
const pkgNpmConfFilePath = path.resolve(pkgPath, "package.json");
|
|
351
|
-
const pkgNpmConf = await FsUtil.readJsonAsync(pkgNpmConfFilePath);
|
|
352
|
-
pkgNpmConf.version = newVersion;
|
|
353
|
-
updateDepVersion(pkgNpmConf.dependencies);
|
|
354
|
-
updateDepVersion(pkgNpmConf.optionalDependencies);
|
|
355
|
-
updateDepVersion(pkgNpmConf.devDependencies);
|
|
356
|
-
updateDepVersion(pkgNpmConf.peerDependencies);
|
|
357
|
-
await FsUtil.writeJsonAsync(pkgNpmConfFilePath, pkgNpmConf, { space: 2 });
|
|
358
|
-
if (FsUtil.exists(path.resolve(pkgPath, "plugin.xml"))) {
|
|
359
|
-
const cordovaPluginConfFilePath = path.resolve(pkgPath, "plugin.xml");
|
|
360
|
-
const cordovaPluginConfXml = await xml2js.parseStringPromise(await FsUtil.readFileAsync(cordovaPluginConfFilePath));
|
|
361
|
-
cordovaPluginConfXml.plugin.$.version = newVersion;
|
|
362
|
-
await FsUtil.writeFileAsync(cordovaPluginConfFilePath, new xml2js.Builder().buildObject(cordovaPluginConfXml));
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
static _logging(buildResults, logger) {
|
|
367
|
-
const messageMap = buildResults.toSetMap(item => item.severity, item => SdCliBuildResultUtil.getMessage(item));
|
|
368
|
-
if (messageMap.has("message")) {
|
|
369
|
-
logger.log(`알림\n${[...messageMap.get("message")].join("\n")}`);
|
|
370
|
-
}
|
|
371
|
-
if (messageMap.has("suggestion")) {
|
|
372
|
-
logger.info(`제안\n${[...messageMap.get("suggestion")].join("\n")}`);
|
|
373
|
-
}
|
|
374
|
-
if (messageMap.has("warning")) {
|
|
375
|
-
logger.warn(`경고\n${[...messageMap.get("warning")].join("\n")}`);
|
|
376
|
-
}
|
|
377
|
-
if (messageMap.has("error")) {
|
|
378
|
-
logger.error(`오류\n${[...messageMap.get("error")].join("\n")}`);
|
|
379
|
-
}
|
|
380
|
-
logger.info("모든 빌드가 완료되었습니다.");
|
|
381
|
-
}
|
|
382
|
-
static async _prepareClusterAsync() {
|
|
383
|
-
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "_runBuildClusterAsync"]);
|
|
384
|
-
return await new Promise(async (resolve, reject) => {
|
|
385
|
-
const cluster = cp.fork(fileURLToPath(await import.meta.resolve("../build-cluster")), [], {
|
|
386
|
-
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
387
|
-
env: process.env
|
|
388
|
-
});
|
|
389
|
-
cluster.stdout.pipe(process.stdout);
|
|
390
|
-
cluster.stderr.pipe(process.stderr);
|
|
391
|
-
cluster.on("exit", (code) => {
|
|
392
|
-
if (code != null && code !== 0) {
|
|
393
|
-
const err = new Error(`오류와 함께 닫힘 (${code})`);
|
|
394
|
-
logger.error(err);
|
|
395
|
-
reject(err);
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
|
-
cluster.on("error", (err) => {
|
|
400
|
-
logger.error(err);
|
|
401
|
-
reject(err);
|
|
402
|
-
});
|
|
403
|
-
cluster.on("message", (message) => {
|
|
404
|
-
if (message === "ready") {
|
|
405
|
-
logger.debug("빌드 클러스터 프로세스가 준비되었습니다.");
|
|
406
|
-
resolve(cluster);
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
static async _runCommandAsync(cluster, cmd, projConf, pkgPath, builderKey) {
|
|
412
|
-
return await new Promise((resolve) => {
|
|
413
|
-
const cb = (message) => {
|
|
414
|
-
if (cmd === "watch" && message.type === "ready" && message.req.cmd === cmd && message.req.pkgPath === pkgPath) {
|
|
415
|
-
cluster.off("message", cb);
|
|
416
|
-
resolve();
|
|
417
|
-
}
|
|
418
|
-
else if (cmd === "build" && message.type === "complete" && message.req.cmd === cmd && message.req.pkgPath === pkgPath) {
|
|
419
|
-
cluster.off("message", cb);
|
|
420
|
-
resolve(message.result?.buildResults);
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
cluster.on("message", cb);
|
|
424
|
-
cluster.send({
|
|
425
|
-
cmd,
|
|
426
|
-
projConf,
|
|
427
|
-
pkgPath,
|
|
428
|
-
builderKey
|
|
429
|
-
});
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
static _closeCluster(cluster) {
|
|
433
|
-
cluster.kill("SIGKILL");
|
|
434
|
-
}
|
|
435
|
-
static async _restartServerAsync(pkgPath, pkgConf, prevServerProcess) {
|
|
436
|
-
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "_restartServerAsync"]);
|
|
437
|
-
if (prevServerProcess) {
|
|
438
|
-
prevServerProcess.kill("SIGKILL");
|
|
439
|
-
}
|
|
440
|
-
const npmConf = (await FsUtil.readJsonAsync(path.resolve(pkgPath, "package.json")));
|
|
441
|
-
return await new Promise(async (resolve, reject) => {
|
|
442
|
-
const worker = cp.fork(fileURLToPath(await import.meta.resolve("../server-worker")), [pkgPath], {
|
|
443
|
-
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
444
|
-
env: {
|
|
445
|
-
...process.env,
|
|
446
|
-
NODE_ENV: "development",
|
|
447
|
-
TZ: "Asia/Seoul",
|
|
448
|
-
SD_VERSION: npmConf.version,
|
|
449
|
-
...pkgConf.env
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
worker.stdout.pipe(process.stdout);
|
|
453
|
-
worker.stderr.pipe(process.stderr);
|
|
454
|
-
worker.on("exit", (code) => {
|
|
455
|
-
if (code != null && code !== 0) {
|
|
456
|
-
const err = new Error(`오류와 함께 닫힘 (${code})`);
|
|
457
|
-
logger.error(err);
|
|
458
|
-
reject(err);
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
});
|
|
462
|
-
worker.on("error", (err) => {
|
|
463
|
-
logger.error(err);
|
|
464
|
-
reject(err);
|
|
465
|
-
});
|
|
466
|
-
worker.on("message", (message) => {
|
|
467
|
-
if ("port" in message) {
|
|
468
|
-
logger.debug("서버가 시작되었습니다.");
|
|
469
|
-
resolve({
|
|
470
|
-
worker,
|
|
471
|
-
port: message.port
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
});
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
}
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { FsUtil, Logger, PathUtil, SdProcess } from "@simplysm/sd-core-node";
|
|
3
|
+
import cp from "child_process";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
5
|
+
import { SdCliBuildResultUtil } from "../utils/SdCliBuildResultUtil";
|
|
6
|
+
import semver from "semver";
|
|
7
|
+
import { NeverEntryError, StringUtil, Wait } from "@simplysm/sd-core-common";
|
|
8
|
+
import { SdStorage } from "@simplysm/sd-storage";
|
|
9
|
+
import { SdCliLocalUpdate } from "./SdCliLocalUpdate";
|
|
10
|
+
import xml2js from "xml2js";
|
|
11
|
+
export class SdCliProject {
|
|
12
|
+
static async watchAsync(opt) {
|
|
13
|
+
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "watchAsync"]);
|
|
14
|
+
logger.debug("프로젝트 설정 가져오기...");
|
|
15
|
+
const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(true, opt.optNames);
|
|
16
|
+
if (projConf.localUpdates) {
|
|
17
|
+
logger.debug("로컬 라이브러리 업데이트 변경감지 시작...");
|
|
18
|
+
await SdCliLocalUpdate.watchAsync({
|
|
19
|
+
confFileRelPath: opt.confFileRelPath,
|
|
20
|
+
optNames: opt.optNames
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
logger.debug("프로젝트 package.json 가져오기...");
|
|
24
|
+
const projNpmConf = (await FsUtil.readJsonAsync(path.resolve(process.cwd(), "package.json")));
|
|
25
|
+
logger.debug("패키지 목록 구성...");
|
|
26
|
+
if (!projNpmConf.workspaces) {
|
|
27
|
+
throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
|
|
28
|
+
}
|
|
29
|
+
const allPkgPaths = await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtil.globAsync(item));
|
|
30
|
+
let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
|
|
31
|
+
if (opt.pkgNames.length !== 0) {
|
|
32
|
+
pkgPaths = pkgPaths.filter((pkgPath) => opt.pkgNames.includes(path.basename(pkgPath)));
|
|
33
|
+
}
|
|
34
|
+
logger.debug("패키지 존재 확인...");
|
|
35
|
+
const notExistsPkgs = Object.keys(projConf.packages).filter((pkgConfKey) => allPkgPaths.every((pkgPath) => path.basename(pkgPath) !== pkgConfKey));
|
|
36
|
+
if (notExistsPkgs.length > 0) {
|
|
37
|
+
throw new Error("패키지를 찾을 수 없습니다. (" + notExistsPkgs.join(", ") + ")");
|
|
38
|
+
}
|
|
39
|
+
logger.debug("빌드 프로세스 준비...");
|
|
40
|
+
const cluster = await this._prepareClusterAsync();
|
|
41
|
+
logger.debug("빌드 프로세스 이벤트 준비...");
|
|
42
|
+
const resultCache = new Map();
|
|
43
|
+
let busyCount = 0;
|
|
44
|
+
const serverInfoMap = new Map();
|
|
45
|
+
cluster.on("message", (message) => {
|
|
46
|
+
if (message.type === "change") {
|
|
47
|
+
if (busyCount === 0) {
|
|
48
|
+
logger.log("빌드를 시작합니다...");
|
|
49
|
+
}
|
|
50
|
+
busyCount++;
|
|
51
|
+
}
|
|
52
|
+
else if (message.type === "complete") {
|
|
53
|
+
for (const affectedFilePath of message.result.affectedFilePaths) {
|
|
54
|
+
resultCache.delete(affectedFilePath);
|
|
55
|
+
}
|
|
56
|
+
for (const buildResult of message.result.buildResults) {
|
|
57
|
+
const cacheItem = resultCache.getOrCreate(buildResult.filePath ?? "none", []);
|
|
58
|
+
cacheItem.push(buildResult);
|
|
59
|
+
}
|
|
60
|
+
const pkgConf = message.req.projConf.packages[path.basename(message.req.pkgPath)];
|
|
61
|
+
if (pkgConf.type === "server") {
|
|
62
|
+
const pkgName = path.basename(message.req.pkgPath);
|
|
63
|
+
const serverInfo = serverInfoMap.getOrCreate(pkgName, {
|
|
64
|
+
hasChanges: false,
|
|
65
|
+
hasClientChanges: false,
|
|
66
|
+
pathProxy: {},
|
|
67
|
+
// changeFilePaths: []
|
|
68
|
+
});
|
|
69
|
+
serverInfo.pkgPath = message.req.pkgPath;
|
|
70
|
+
serverInfo.hasChanges = true;
|
|
71
|
+
}
|
|
72
|
+
if (pkgConf.type === "client") {
|
|
73
|
+
const pkgName = path.basename(message.req.pkgPath);
|
|
74
|
+
if (pkgConf.server !== undefined) {
|
|
75
|
+
const serverInfo = serverInfoMap.getOrCreate(pkgConf.server, {
|
|
76
|
+
hasChanges: false,
|
|
77
|
+
hasClientChanges: false,
|
|
78
|
+
pathProxy: {},
|
|
79
|
+
// changeFilePaths: []
|
|
80
|
+
});
|
|
81
|
+
serverInfo.pathProxy[pkgName] = path.resolve(message.req.pkgPath, "dist");
|
|
82
|
+
// serverInfo.changeFilePaths.push(...message.result!.affectedFilePaths);
|
|
83
|
+
serverInfo.hasClientChanges = true;
|
|
84
|
+
// serverInfo.worker?.send({type: "broadcastReload"});
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const serverInfo = serverInfoMap.getOrCreate(pkgName, {
|
|
88
|
+
hasChanges: false,
|
|
89
|
+
hasClientChanges: false,
|
|
90
|
+
pathProxy: {},
|
|
91
|
+
// changeFilePaths: []
|
|
92
|
+
});
|
|
93
|
+
serverInfo.port = message.result.port;
|
|
94
|
+
// serverInfo.changeFilePaths.push(...message.result!.affectedFilePaths);
|
|
95
|
+
serverInfo.hasClientChanges = true;
|
|
96
|
+
// serverInfo.worker?.send({type: "broadcastReload"});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
setTimeout(async () => {
|
|
100
|
+
busyCount--;
|
|
101
|
+
if (busyCount === 0) {
|
|
102
|
+
for (const serverPkgName of serverInfoMap.keys()) {
|
|
103
|
+
const serverInfo = serverInfoMap.get(serverPkgName);
|
|
104
|
+
if (serverInfo.pkgPath !== undefined && serverInfo.hasChanges) {
|
|
105
|
+
logger.debug("서버 재시작...");
|
|
106
|
+
try {
|
|
107
|
+
const restartServerResult = await this._restartServerAsync(serverInfo.pkgPath, projConf.packages[path.basename(serverInfo.pkgPath)], serverInfo.worker);
|
|
108
|
+
serverInfo.worker = restartServerResult.worker;
|
|
109
|
+
serverInfo.port = restartServerResult.port;
|
|
110
|
+
serverInfo.hasChanges = false;
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
logger.error(err);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (serverInfo.worker) {
|
|
117
|
+
logger.debug("클라이언트 설정...");
|
|
118
|
+
serverInfo.worker.send({
|
|
119
|
+
type: "setPathProxy",
|
|
120
|
+
pathProxy: serverInfo.pathProxy
|
|
121
|
+
});
|
|
122
|
+
if (serverInfo.hasClientChanges) {
|
|
123
|
+
logger.debug("클라이언트 새로고침...");
|
|
124
|
+
serverInfo.worker.send({ type: "broadcastReload" });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const clientPaths = [];
|
|
129
|
+
for (const serverInfo of serverInfoMap.values()) {
|
|
130
|
+
if (Object.keys(serverInfo.pathProxy).length > 0) {
|
|
131
|
+
for (const proxyPath of Object.keys(serverInfo.pathProxy)) {
|
|
132
|
+
clientPaths.push(`http://localhost:${serverInfo.port}/${proxyPath}/`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
clientPaths.push(`http://localhost:${serverInfo.port}/`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (clientPaths.length > 0) {
|
|
140
|
+
logger.info("클라이언트 개발 서버 접속 주소\n" + clientPaths.join("\n"));
|
|
141
|
+
}
|
|
142
|
+
const buildResults = Array.from(resultCache.values()).mapMany();
|
|
143
|
+
this._logging(buildResults, logger);
|
|
144
|
+
}
|
|
145
|
+
}, 300);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
logger.debug("빌드 프로세스 명령 전송...");
|
|
149
|
+
busyCount++;
|
|
150
|
+
logger.log("빌드를 시작합니다...");
|
|
151
|
+
await pkgPaths.parallelAsync(async (pkgPath) => {
|
|
152
|
+
const pkgConf = projConf.packages[path.basename(pkgPath)];
|
|
153
|
+
if (pkgConf.type === "client") {
|
|
154
|
+
const builderKeys = Object.keys(pkgConf.builder ?? { web: {} });
|
|
155
|
+
await builderKeys.parallelAsync(async (builderKey) => {
|
|
156
|
+
await this._runCommandAsync(cluster, "watch", projConf, pkgPath, builderKey);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
await this._runCommandAsync(cluster, "watch", projConf, pkgPath);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
busyCount--;
|
|
164
|
+
if (busyCount === 0) {
|
|
165
|
+
const buildResults = Array.from(resultCache.values()).mapMany();
|
|
166
|
+
this._logging(buildResults, logger);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
static async buildAsync(opt) {
|
|
170
|
+
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "buildAsync"]);
|
|
171
|
+
logger.debug("프로젝트 설정 가져오기...");
|
|
172
|
+
const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(false, opt.optNames);
|
|
173
|
+
logger.debug("프로젝트 package.json 가져오기...");
|
|
174
|
+
const projNpmConf = (await FsUtil.readJsonAsync(path.resolve(process.cwd(), "package.json")));
|
|
175
|
+
logger.debug("패키지 목록 구성...");
|
|
176
|
+
if (!projNpmConf.workspaces) {
|
|
177
|
+
throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
|
|
178
|
+
}
|
|
179
|
+
const allPkgPaths = await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtil.globAsync(item));
|
|
180
|
+
let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
|
|
181
|
+
if (opt.pkgNames.length !== 0) {
|
|
182
|
+
pkgPaths = pkgPaths.filter((pkgPath) => opt.pkgNames.includes(path.basename(pkgPath)));
|
|
183
|
+
}
|
|
184
|
+
logger.debug("프로젝트 및 패키지 버전 설정...");
|
|
185
|
+
await this._upgradeVersionAsync(projNpmConf, allPkgPaths);
|
|
186
|
+
logger.debug("빌드 프로세스 준비...");
|
|
187
|
+
const cluster = await this._prepareClusterAsync();
|
|
188
|
+
logger.debug("빌드 프로세스 명령 전달...");
|
|
189
|
+
const results = (await pkgPaths.parallelAsync(async (pkgPath) => {
|
|
190
|
+
const pkgConf = projConf.packages[path.basename(pkgPath)];
|
|
191
|
+
if (pkgConf.type === "client") {
|
|
192
|
+
const builderKeys = Object.keys(pkgConf.builder ?? { web: {} });
|
|
193
|
+
return (await builderKeys.parallelAsync(async (builderKey) => {
|
|
194
|
+
return await this._runCommandAsync(cluster, "build", projConf, pkgPath, builderKey);
|
|
195
|
+
})).mapMany();
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
return await this._runCommandAsync(cluster, "build", projConf, pkgPath);
|
|
199
|
+
}
|
|
200
|
+
})).mapMany();
|
|
201
|
+
logger.debug("빌드 프로세스 닫기...");
|
|
202
|
+
this._closeCluster(cluster);
|
|
203
|
+
this._logging(results, logger);
|
|
204
|
+
}
|
|
205
|
+
static async publishAsync(opt) {
|
|
206
|
+
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "publishAsync"]);
|
|
207
|
+
logger.debug("프로젝트 설정 가져오기...");
|
|
208
|
+
const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(false, opt.optNames);
|
|
209
|
+
logger.debug("프로젝트 package.json 가져오기...");
|
|
210
|
+
const projNpmConf = (await FsUtil.readJsonAsync(path.resolve(process.cwd(), "package.json")));
|
|
211
|
+
if (opt.noBuild) {
|
|
212
|
+
logger.warn("빌드하지 않고, 배포하는것은 상당히 위험합니다.");
|
|
213
|
+
await this._waitSecMessageAsync("프로세스를 중지하려면, 'CTRL+C'를 누르세요.", 5);
|
|
214
|
+
}
|
|
215
|
+
// GIT 사용중일 경우, 커밋되지 않은 수정사항이 있는지 확인
|
|
216
|
+
if (FsUtil.exists(path.resolve(process.cwd(), ".git"))) {
|
|
217
|
+
logger.debug("GIT 커밋여부 확인...");
|
|
218
|
+
const gitStatusResult = await SdProcess.spawnAsync("git status");
|
|
219
|
+
if (gitStatusResult.includes("Changes") || gitStatusResult.includes("Untracked")) {
|
|
220
|
+
throw new Error("커밋되지 않은 정보가 있습니다.\n" + gitStatusResult);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
logger.debug("패키지 목록 구성...");
|
|
224
|
+
if (!projNpmConf.workspaces) {
|
|
225
|
+
throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
|
|
226
|
+
}
|
|
227
|
+
const allPkgPaths = await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtil.globAsync(item));
|
|
228
|
+
let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
|
|
229
|
+
if (opt.pkgNames.length !== 0) {
|
|
230
|
+
pkgPaths = pkgPaths.filter((pkgPath) => opt.pkgNames.includes(path.basename(pkgPath)));
|
|
231
|
+
}
|
|
232
|
+
logger.debug("프로젝트 및 패키지 버전 설정...");
|
|
233
|
+
await this._upgradeVersionAsync(projNpmConf, allPkgPaths);
|
|
234
|
+
// 빌드
|
|
235
|
+
if (!opt.noBuild) {
|
|
236
|
+
logger.debug("빌드 프로세스 준비...");
|
|
237
|
+
const cluster = await this._prepareClusterAsync();
|
|
238
|
+
logger.debug("빌드 프로세스 명령 전달...");
|
|
239
|
+
const results = (await pkgPaths.parallelAsync(async (pkgPath) => {
|
|
240
|
+
const pkgConf = projConf.packages[path.basename(pkgPath)];
|
|
241
|
+
if (pkgConf.type === "client") {
|
|
242
|
+
const builderKeys = Object.keys(pkgConf.builder ?? { web: {} });
|
|
243
|
+
return (await builderKeys.parallelAsync(async (builderKey) => {
|
|
244
|
+
return await this._runCommandAsync(cluster, "build", projConf, pkgPath, builderKey);
|
|
245
|
+
})).mapMany();
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
return await this._runCommandAsync(cluster, "build", projConf, pkgPath);
|
|
249
|
+
}
|
|
250
|
+
})).mapMany();
|
|
251
|
+
logger.debug("빌드 프로세스 닫기...");
|
|
252
|
+
this._closeCluster(cluster);
|
|
253
|
+
this._logging(results, logger);
|
|
254
|
+
}
|
|
255
|
+
// GIT 사용중일경우, 새 버전 커밋 및 TAG 생성
|
|
256
|
+
if (FsUtil.exists(path.resolve(process.cwd(), ".git"))) {
|
|
257
|
+
logger.debug("새 버전 커밋 및 TAG 생성...");
|
|
258
|
+
await SdProcess.spawnAsync("git add .");
|
|
259
|
+
await SdProcess.spawnAsync(`git commit -m "v${projNpmConf.version}"`);
|
|
260
|
+
await SdProcess.spawnAsync(`git tag -a "v${projNpmConf.version}" -m "v${projNpmConf.version}"`);
|
|
261
|
+
logger.debug("새 버전 푸쉬...");
|
|
262
|
+
await SdProcess.spawnAsync("git push");
|
|
263
|
+
await SdProcess.spawnAsync("git push --tags");
|
|
264
|
+
}
|
|
265
|
+
logger.debug("배포 시작...");
|
|
266
|
+
await pkgPaths.parallelAsync(async (pkgPath) => {
|
|
267
|
+
const pkgName = path.basename(pkgPath);
|
|
268
|
+
const pkgConf = projConf.packages[pkgName];
|
|
269
|
+
if (pkgConf?.publish == null)
|
|
270
|
+
return;
|
|
271
|
+
logger.debug(`[${pkgName}] 배포 시작...`);
|
|
272
|
+
await this._publishPkgAsync(pkgPath, pkgConf.publish);
|
|
273
|
+
logger.debug(`[${pkgName}] 배포 완료`);
|
|
274
|
+
});
|
|
275
|
+
logger.info(`모든 배포가 완료되었습니다. (v${projNpmConf.version})`);
|
|
276
|
+
}
|
|
277
|
+
static async _publishPkgAsync(pkgPath, pkgPubConf) {
|
|
278
|
+
if (pkgPubConf === "npm") {
|
|
279
|
+
await SdProcess.spawnAsync("yarn npm publish --access public", { cwd: pkgPath });
|
|
280
|
+
}
|
|
281
|
+
else if (pkgPubConf?.type === "local-directory") {
|
|
282
|
+
const pkgNpmConf = (await FsUtil.readJsonAsync(path.resolve(pkgPath, "package.json")));
|
|
283
|
+
const targetRootPath = pkgPubConf.path.replace(/%([^%]*)%/g, (item) => {
|
|
284
|
+
const envName = item.replace(/%/g, "");
|
|
285
|
+
if (!StringUtil.isNullOrEmpty(pkgNpmConf.version) && envName === "SD_VERSION") {
|
|
286
|
+
return pkgNpmConf.version;
|
|
287
|
+
}
|
|
288
|
+
return process.env[envName] ?? item;
|
|
289
|
+
});
|
|
290
|
+
const filePaths = await FsUtil.globAsync(path.resolve(pkgPath, "dist", "**", "*"), {
|
|
291
|
+
dot: true,
|
|
292
|
+
nodir: true
|
|
293
|
+
});
|
|
294
|
+
await filePaths.parallelAsync(async (filePath) => {
|
|
295
|
+
const relativeFilePath = path.relative(path.resolve(pkgPath, "dist"), filePath);
|
|
296
|
+
const targetPath = PathUtil.posix(targetRootPath, relativeFilePath);
|
|
297
|
+
await FsUtil.copyAsync(filePath, targetPath);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
else if (pkgPubConf?.type === "ftp" || pkgPubConf?.type === "ftps" || pkgPubConf?.type === "sftp") {
|
|
301
|
+
const ftp = await SdStorage.connectAsync(pkgPubConf.type, {
|
|
302
|
+
host: pkgPubConf.host,
|
|
303
|
+
port: pkgPubConf.port,
|
|
304
|
+
user: pkgPubConf.user,
|
|
305
|
+
pass: pkgPubConf.pass
|
|
306
|
+
});
|
|
307
|
+
await ftp.uploadDirAsync(path.resolve(pkgPath, "dist"), pkgPubConf.path ?? "/");
|
|
308
|
+
await ftp.closeAsync();
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
throw new NeverEntryError();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
static async _waitSecMessageAsync(msg, sec) {
|
|
315
|
+
for (let i = sec; i > 0; i--) {
|
|
316
|
+
if (i !== sec) {
|
|
317
|
+
process.stdout.cursorTo(0);
|
|
318
|
+
}
|
|
319
|
+
process.stdout.write(`${msg} ${i}`);
|
|
320
|
+
await Wait.time(1000);
|
|
321
|
+
}
|
|
322
|
+
process.stdout.cursorTo(0);
|
|
323
|
+
process.stdout.clearLine(0);
|
|
324
|
+
}
|
|
325
|
+
static async _upgradeVersionAsync(projNpmConf, allPkgPaths) {
|
|
326
|
+
// 작업공간 package.json 버전 설정
|
|
327
|
+
const newVersion = semver.inc(projNpmConf.version, "patch");
|
|
328
|
+
projNpmConf.version = newVersion;
|
|
329
|
+
const pkgNames = await allPkgPaths.mapAsync(async (pkgPath) => {
|
|
330
|
+
const pkgNpmConf = await FsUtil.readJsonAsync(path.resolve(pkgPath, "package.json"));
|
|
331
|
+
return pkgNpmConf.name;
|
|
332
|
+
});
|
|
333
|
+
const updateDepVersion = (deps) => {
|
|
334
|
+
if (!deps)
|
|
335
|
+
return;
|
|
336
|
+
for (const depName of Object.keys(deps)) {
|
|
337
|
+
if (pkgNames.includes(depName)) {
|
|
338
|
+
deps[depName] = newVersion;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
updateDepVersion(projNpmConf.dependencies);
|
|
343
|
+
updateDepVersion(projNpmConf.optionalDependencies);
|
|
344
|
+
updateDepVersion(projNpmConf.devDependencies);
|
|
345
|
+
updateDepVersion(projNpmConf.peerDependencies);
|
|
346
|
+
const projNpmConfFilePath = path.resolve(process.cwd(), "package.json");
|
|
347
|
+
await FsUtil.writeJsonAsync(projNpmConfFilePath, projNpmConf, { space: 2 });
|
|
348
|
+
// 각 패키지 package.json 버전 설정
|
|
349
|
+
await allPkgPaths.parallelAsync(async (pkgPath) => {
|
|
350
|
+
const pkgNpmConfFilePath = path.resolve(pkgPath, "package.json");
|
|
351
|
+
const pkgNpmConf = await FsUtil.readJsonAsync(pkgNpmConfFilePath);
|
|
352
|
+
pkgNpmConf.version = newVersion;
|
|
353
|
+
updateDepVersion(pkgNpmConf.dependencies);
|
|
354
|
+
updateDepVersion(pkgNpmConf.optionalDependencies);
|
|
355
|
+
updateDepVersion(pkgNpmConf.devDependencies);
|
|
356
|
+
updateDepVersion(pkgNpmConf.peerDependencies);
|
|
357
|
+
await FsUtil.writeJsonAsync(pkgNpmConfFilePath, pkgNpmConf, { space: 2 });
|
|
358
|
+
if (FsUtil.exists(path.resolve(pkgPath, "plugin.xml"))) {
|
|
359
|
+
const cordovaPluginConfFilePath = path.resolve(pkgPath, "plugin.xml");
|
|
360
|
+
const cordovaPluginConfXml = await xml2js.parseStringPromise(await FsUtil.readFileAsync(cordovaPluginConfFilePath));
|
|
361
|
+
cordovaPluginConfXml.plugin.$.version = newVersion;
|
|
362
|
+
await FsUtil.writeFileAsync(cordovaPluginConfFilePath, new xml2js.Builder().buildObject(cordovaPluginConfXml));
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
static _logging(buildResults, logger) {
|
|
367
|
+
const messageMap = buildResults.toSetMap(item => item.severity, item => SdCliBuildResultUtil.getMessage(item));
|
|
368
|
+
if (messageMap.has("message")) {
|
|
369
|
+
logger.log(`알림\n${[...messageMap.get("message")].join("\n")}`);
|
|
370
|
+
}
|
|
371
|
+
if (messageMap.has("suggestion")) {
|
|
372
|
+
logger.info(`제안\n${[...messageMap.get("suggestion")].join("\n")}`);
|
|
373
|
+
}
|
|
374
|
+
if (messageMap.has("warning")) {
|
|
375
|
+
logger.warn(`경고\n${[...messageMap.get("warning")].join("\n")}`);
|
|
376
|
+
}
|
|
377
|
+
if (messageMap.has("error")) {
|
|
378
|
+
logger.error(`오류\n${[...messageMap.get("error")].join("\n")}`);
|
|
379
|
+
}
|
|
380
|
+
logger.info("모든 빌드가 완료되었습니다.");
|
|
381
|
+
}
|
|
382
|
+
static async _prepareClusterAsync() {
|
|
383
|
+
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "_runBuildClusterAsync"]);
|
|
384
|
+
return await new Promise(async (resolve, reject) => {
|
|
385
|
+
const cluster = cp.fork(fileURLToPath(await import.meta.resolve("../build-cluster")), [], {
|
|
386
|
+
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
387
|
+
env: process.env
|
|
388
|
+
});
|
|
389
|
+
cluster.stdout.pipe(process.stdout);
|
|
390
|
+
cluster.stderr.pipe(process.stderr);
|
|
391
|
+
cluster.on("exit", (code) => {
|
|
392
|
+
if (code != null && code !== 0) {
|
|
393
|
+
const err = new Error(`오류와 함께 닫힘 (${code})`);
|
|
394
|
+
logger.error(err);
|
|
395
|
+
reject(err);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
cluster.on("error", (err) => {
|
|
400
|
+
logger.error(err);
|
|
401
|
+
reject(err);
|
|
402
|
+
});
|
|
403
|
+
cluster.on("message", (message) => {
|
|
404
|
+
if (message === "ready") {
|
|
405
|
+
logger.debug("빌드 클러스터 프로세스가 준비되었습니다.");
|
|
406
|
+
resolve(cluster);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
static async _runCommandAsync(cluster, cmd, projConf, pkgPath, builderKey) {
|
|
412
|
+
return await new Promise((resolve) => {
|
|
413
|
+
const cb = (message) => {
|
|
414
|
+
if (cmd === "watch" && message.type === "ready" && message.req.cmd === cmd && message.req.pkgPath === pkgPath) {
|
|
415
|
+
cluster.off("message", cb);
|
|
416
|
+
resolve();
|
|
417
|
+
}
|
|
418
|
+
else if (cmd === "build" && message.type === "complete" && message.req.cmd === cmd && message.req.pkgPath === pkgPath) {
|
|
419
|
+
cluster.off("message", cb);
|
|
420
|
+
resolve(message.result?.buildResults);
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
cluster.on("message", cb);
|
|
424
|
+
cluster.send({
|
|
425
|
+
cmd,
|
|
426
|
+
projConf,
|
|
427
|
+
pkgPath,
|
|
428
|
+
builderKey
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
static _closeCluster(cluster) {
|
|
433
|
+
cluster.kill("SIGKILL");
|
|
434
|
+
}
|
|
435
|
+
static async _restartServerAsync(pkgPath, pkgConf, prevServerProcess) {
|
|
436
|
+
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "_restartServerAsync"]);
|
|
437
|
+
if (prevServerProcess) {
|
|
438
|
+
prevServerProcess.kill("SIGKILL");
|
|
439
|
+
}
|
|
440
|
+
const npmConf = (await FsUtil.readJsonAsync(path.resolve(pkgPath, "package.json")));
|
|
441
|
+
return await new Promise(async (resolve, reject) => {
|
|
442
|
+
const worker = cp.fork(fileURLToPath(await import.meta.resolve("../server-worker")), [pkgPath], {
|
|
443
|
+
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
444
|
+
env: {
|
|
445
|
+
...process.env,
|
|
446
|
+
NODE_ENV: "development",
|
|
447
|
+
TZ: "Asia/Seoul",
|
|
448
|
+
SD_VERSION: npmConf.version,
|
|
449
|
+
...pkgConf.env
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
worker.stdout.pipe(process.stdout);
|
|
453
|
+
worker.stderr.pipe(process.stderr);
|
|
454
|
+
worker.on("exit", (code) => {
|
|
455
|
+
if (code != null && code !== 0) {
|
|
456
|
+
const err = new Error(`오류와 함께 닫힘 (${code})`);
|
|
457
|
+
logger.error(err);
|
|
458
|
+
reject(err);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
worker.on("error", (err) => {
|
|
463
|
+
logger.error(err);
|
|
464
|
+
reject(err);
|
|
465
|
+
});
|
|
466
|
+
worker.on("message", (message) => {
|
|
467
|
+
if ("port" in message) {
|
|
468
|
+
logger.debug("서버가 시작되었습니다.");
|
|
469
|
+
resolve({
|
|
470
|
+
worker,
|
|
471
|
+
port: message.port
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
478
|
//# sourceMappingURL=SdCliProject.js.map
|