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