@simplysm/sd-cli 10.0.39 → 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 -14
- 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/SdCliViteNodeGlobalPlugin.js +2 -2
- package/dist/utils/SdCliViteNodeGlobalPlugin.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 +15 -14
- 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 +526 -451
- 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/SdCliViteNodeGlobalPlugin.ts +5 -5
- 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,524 +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";
|
|
15
|
+
import {NotImplementError, StringUtil, Wait} from "@simplysm/sd-core-common";
|
|
16
|
+
import {SdCliIndexFileGenerator} from "../build-tools/SdCliIndexFileGenerator";
|
|
17
|
+
import {SdStorage} from "@simplysm/sd-storage";
|
|
16
18
|
|
|
17
19
|
export class SdCliProject {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
+
}
|
|
43
|
+
|
|
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
|
+
}
|
|
49
|
+
|
|
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("빌드를 시작합니다...");
|
|
35
79
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
80
|
+
busyCount++;
|
|
81
|
+
}
|
|
82
|
+
else if (message.type === "complete") {
|
|
83
|
+
for (const affectedFilePath of message.result!.affectedFilePaths) {
|
|
84
|
+
resultCache.delete(affectedFilePath);
|
|
40
85
|
}
|
|
41
86
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
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);
|
|
46
90
|
}
|
|
47
91
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
port?: number;
|
|
59
|
-
hasChanges: boolean;
|
|
60
|
-
|
|
61
|
-
//client
|
|
62
|
-
pathProxy: Record<string, number | undefined>; // persist
|
|
63
|
-
changeFilePaths: string[];
|
|
64
|
-
}>();
|
|
65
|
-
cluster.on("message", (message: ISdCliBuildClusterResMessage) => {
|
|
66
|
-
if (message.type === "change") {
|
|
67
|
-
if (busyCount === 0) {
|
|
68
|
-
logger.log("빌드를 시작합니다...");
|
|
69
|
-
}
|
|
70
|
-
busyCount++;
|
|
71
|
-
} else if (message.type === "complete") {
|
|
72
|
-
for (const affectedFilePath of message.result!.affectedFilePaths) {
|
|
73
|
-
resultCache.delete(affectedFilePath);
|
|
74
|
-
}
|
|
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
|
+
}
|
|
75
102
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
cacheItem.push(buildResult);
|
|
79
|
-
}
|
|
103
|
+
if (message.req.pkgConf.type === "client") {
|
|
104
|
+
const pkgName = path.basename(message.req.pkgPath);
|
|
80
105
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
}
|
|
91
125
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const serverInfo = serverInfoMap.getOrCreate(pkgName, {
|
|
105
|
-
hasChanges: false,
|
|
106
|
-
pathProxy: {},
|
|
107
|
-
changeFilePaths: []
|
|
108
|
-
});
|
|
109
|
-
serverInfo.port = message.result!.port;
|
|
110
|
-
serverInfo.changeFilePaths.push(...message.result!.affectedFilePaths);
|
|
111
|
-
}
|
|
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;
|
|
112
138
|
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
logger.error(err);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
113
143
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
const restartServerResult = await this._restartServerAsync(serverInfo.pkgPath, serverInfo.worker);
|
|
123
|
-
serverInfo.worker = restartServerResult.worker;
|
|
124
|
-
serverInfo.port = restartServerResult.port;
|
|
125
|
-
serverInfo.hasChanges = false;
|
|
126
|
-
} catch (err) {
|
|
127
|
-
logger.error(err);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (serverInfo.worker) {
|
|
132
|
-
logger.debug("클라이언트 설정...");
|
|
133
|
-
serverInfo.worker.send({
|
|
134
|
-
type: "setPathProxy",
|
|
135
|
-
pathProxy: serverInfo.pathProxy
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const clientPaths: string[] = [];
|
|
141
|
-
for (const serverInfo of serverInfoMap.values()) {
|
|
142
|
-
if (Object.keys(serverInfo.pathProxy).length > 0) {
|
|
143
|
-
for (const proxyPath of Object.keys(serverInfo.pathProxy)) {
|
|
144
|
-
clientPaths.push(`http://localhost:${serverInfo.port}/${proxyPath}/`);
|
|
145
|
-
}
|
|
146
|
-
} else {
|
|
147
|
-
clientPaths.push(`http://localhost:${serverInfo.port}/`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (clientPaths.length > 0) {
|
|
151
|
-
logger.info("클라이언트 개발 서버 접속 주소\n" + clientPaths.join("\n"));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const buildResults = Array.from(resultCache.values()).mapMany();
|
|
155
|
-
this._logging(buildResults, logger);
|
|
156
|
-
}
|
|
157
|
-
}, 300);
|
|
144
|
+
if (serverInfo.worker) {
|
|
145
|
+
logger.debug("클라이언트 설정...");
|
|
146
|
+
serverInfo.worker.send({
|
|
147
|
+
type: "setPathProxy",
|
|
148
|
+
pathProxy: serverInfo.pathProxy
|
|
149
|
+
});
|
|
150
|
+
}
|
|
158
151
|
}
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
logger.debug("빌드 프로세스 명령 전송...");
|
|
162
|
-
busyCount++;
|
|
163
|
-
logger.log("빌드를 시작합니다...");
|
|
164
152
|
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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"));
|
|
174
166
|
}
|
|
175
|
-
});
|
|
176
167
|
|
|
177
|
-
busyCount--;
|
|
178
|
-
if (busyCount === 0) {
|
|
179
168
|
const buildResults = Array.from(resultCache.values()).mapMany();
|
|
180
169
|
this._logging(buildResults, logger);
|
|
181
|
-
|
|
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);
|
|
182
196
|
}
|
|
197
|
+
}
|
|
183
198
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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"]);
|
|
191
206
|
|
|
192
|
-
|
|
193
|
-
|
|
207
|
+
logger.debug("프로젝트 설정 가져오기...");
|
|
208
|
+
const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(false, opt.optNames) as TSdCliConfig;
|
|
194
209
|
|
|
195
|
-
|
|
196
|
-
|
|
210
|
+
logger.debug("프로젝트 package.json 가져오기...");
|
|
211
|
+
const projNpmConf = (await FsUtil.readJsonAsync(path.resolve(process.cwd(), "package.json"))) as INpmConfig;
|
|
197
212
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
+
}
|
|
222
|
+
|
|
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();
|
|
201
246
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (opt.pkgNames.length !== 0) {
|
|
205
|
-
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);
|
|
206
249
|
}
|
|
250
|
+
})
|
|
251
|
+
).mapMany();
|
|
207
252
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
logger.debug("빌드 프로세스 준비...");
|
|
212
|
-
const cluster = await this._prepareClusterAsync();
|
|
213
|
-
|
|
214
|
-
logger.debug("빌드 프로세스 명령 전달...");
|
|
215
|
-
const results = (
|
|
216
|
-
await pkgPaths.parallelAsync(async (pkgPath) => {
|
|
217
|
-
const pkgConf = projConf.packages[path.basename(pkgPath)]!;
|
|
218
|
-
if (pkgConf.type === "client") {
|
|
219
|
-
const builderKeys = Object.keys(pkgConf.builder ?? {web: {}});
|
|
220
|
-
return (await builderKeys.parallelAsync(async (builderKey) => {
|
|
221
|
-
return await this._runCommandAsync(cluster, "build", pkgPath, projConf.packages[path.basename(pkgPath)]!, opt.withLint, builderKey);
|
|
222
|
-
})).mapMany();
|
|
223
|
-
} else {
|
|
224
|
-
return await this._runCommandAsync(cluster, "build", pkgPath, projConf.packages[path.basename(pkgPath)]!, opt.withLint);
|
|
225
|
-
}
|
|
226
|
-
})
|
|
227
|
-
).mapMany();
|
|
253
|
+
logger.debug("빌드 프로세스 닫기...");
|
|
254
|
+
this._closeCluster(cluster);
|
|
228
255
|
|
|
229
|
-
|
|
230
|
-
|
|
256
|
+
this._logging(results, logger);
|
|
257
|
+
}
|
|
231
258
|
|
|
232
|
-
|
|
233
|
-
|
|
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"]);
|
|
234
267
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
confFileRelPath: string;
|
|
238
|
-
optNames: string[];
|
|
239
|
-
pkgNames: string[];
|
|
240
|
-
withLint: boolean;
|
|
241
|
-
}): Promise<void> {
|
|
242
|
-
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "publishAsync"]);
|
|
268
|
+
logger.debug("프로젝트 설정 가져오기...");
|
|
269
|
+
const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(false, opt.optNames) as TSdCliConfig;
|
|
243
270
|
|
|
244
|
-
|
|
245
|
-
|
|
271
|
+
logger.debug("프로젝트 package.json 가져오기...");
|
|
272
|
+
const projNpmConf = (await FsUtil.readJsonAsync(path.resolve(process.cwd(), "package.json"))) as INpmConfig;
|
|
246
273
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
logger.warn("빌드하지 않고, 배포하는것은 상당히 위험합니다.");
|
|
252
|
-
await this._waitSecMessageAsync("프로세스를 중지하려면, 'CTRL+C'를 누르세요.", 5);
|
|
253
|
-
}
|
|
274
|
+
if (opt.noBuild) {
|
|
275
|
+
logger.warn("빌드하지 않고, 배포하는것은 상당히 위험합니다.");
|
|
276
|
+
await this._waitSecMessageAsync("프로세스를 중지하려면, 'CTRL+C'를 누르세요.", 5);
|
|
277
|
+
}
|
|
254
278
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
+
}
|
|
263
287
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
+
}
|
|
273
297
|
|
|
274
|
-
|
|
275
|
-
|
|
298
|
+
logger.debug("프로젝트 및 패키지 버전 설정...");
|
|
299
|
+
await this._upgradeVersionAsync(projNpmConf, allPkgPaths);
|
|
276
300
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
+
});
|
|
281
310
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
await pkgPaths.parallelAsync(async (pkgPath) => {
|
|
285
|
-
return await this._runCommandAsync(cluster, "build", pkgPath, projConf.packages[path.basename(pkgPath)]!, opt.withLint);
|
|
286
|
-
})
|
|
287
|
-
).mapMany();
|
|
311
|
+
logger.debug("빌드 프로세스 준비...");
|
|
312
|
+
const cluster = await this._prepareClusterAsync();
|
|
288
313
|
|
|
289
|
-
|
|
290
|
-
|
|
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
|
+
}
|
|
291
335
|
|
|
292
|
-
|
|
293
|
-
|
|
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}"`);
|
|
294
342
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
await SdProcess.spawnAsync(`git commit -m "v${projNpmConf.version}"`);
|
|
300
|
-
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
|
+
}
|
|
301
347
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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;
|
|
306
353
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if (pkgConf?.publish == null) return;
|
|
354
|
+
logger.debug(`[${pkgName}] 배포 시작...`);
|
|
355
|
+
await this._publishPkgAsync(pkgPath, pkgConf.publish);
|
|
356
|
+
logger.debug(`[${pkgName}] 배포 완료`);
|
|
357
|
+
});
|
|
312
358
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
logger.debug(`[${pkgName}] 배포 완료`);
|
|
316
|
-
});
|
|
359
|
+
logger.info(`모든 배포가 완료되었습니다. (v${projNpmConf.version})`);
|
|
360
|
+
}
|
|
317
361
|
|
|
318
|
-
|
|
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});
|
|
319
365
|
}
|
|
366
|
+
else if (pkgPubConf?.type === "local-directory") {
|
|
367
|
+
const pkgNpmConf = (await FsUtil.readJsonAsync(path.resolve(pkgPath, "package.json"))) as INpmConfig;
|
|
320
368
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
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;
|
|
326
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
|
+
});
|
|
327
387
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
process.stdout.cursorTo(0);
|
|
339
|
-
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();
|
|
340
397
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
// 작업공간 package.json 버전 설정
|
|
344
|
-
const newVersion = semver.inc(projNpmConf.version, "patch")!;
|
|
345
|
-
projNpmConf.version = newVersion;
|
|
346
|
-
|
|
347
|
-
const pkgNames = await allPkgPaths.mapAsync(async (pkgPath) => {
|
|
348
|
-
const pkgNpmConf = await FsUtil.readJsonAsync(path.resolve(pkgPath, "package.json"));
|
|
349
|
-
return pkgNpmConf.name;
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
const updateDepVersion = (deps: Record<string, string> | undefined): void => {
|
|
353
|
-
if (!deps) return;
|
|
354
|
-
for (const depName of Object.keys(deps)) {
|
|
355
|
-
if (pkgNames.includes(depName)) {
|
|
356
|
-
deps[depName] = newVersion;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
updateDepVersion(projNpmConf.dependencies);
|
|
361
|
-
updateDepVersion(projNpmConf.optionalDependencies);
|
|
362
|
-
updateDepVersion(projNpmConf.devDependencies);
|
|
363
|
-
updateDepVersion(projNpmConf.peerDependencies);
|
|
364
|
-
|
|
365
|
-
const projNpmConfFilePath = path.resolve(process.cwd(), "package.json");
|
|
366
|
-
await FsUtil.writeJsonAsync(projNpmConfFilePath, projNpmConf, {space: 2});
|
|
367
|
-
|
|
368
|
-
// 각 패키지 package.json 버전 설정
|
|
369
|
-
await allPkgPaths.parallelAsync(async (pkgPath) => {
|
|
370
|
-
const pkgNpmConfFilePath = path.resolve(pkgPath, "package.json");
|
|
371
|
-
const pkgNpmConf = await FsUtil.readJsonAsync(pkgNpmConfFilePath);
|
|
372
|
-
pkgNpmConf.version = newVersion;
|
|
373
|
-
|
|
374
|
-
updateDepVersion(pkgNpmConf.dependencies);
|
|
375
|
-
updateDepVersion(pkgNpmConf.optionalDependencies);
|
|
376
|
-
updateDepVersion(pkgNpmConf.devDependencies);
|
|
377
|
-
updateDepVersion(pkgNpmConf.peerDependencies);
|
|
378
|
-
|
|
379
|
-
await FsUtil.writeJsonAsync(pkgNpmConfFilePath, pkgNpmConf, {space: 2});
|
|
380
|
-
});
|
|
398
|
+
else {
|
|
399
|
+
throw new NotImplementError();
|
|
381
400
|
}
|
|
401
|
+
}
|
|
382
402
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
+
}
|
|
388
411
|
|
|
389
|
-
|
|
390
|
-
|
|
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;
|
|
391
431
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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"));
|
|
403
474
|
}
|
|
404
475
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
return await new Promise<cp.ChildProcess>(async (resolve, reject) => {
|
|
409
|
-
const cluster = cp.fork(
|
|
410
|
-
fileURLToPath(await import.meta.resolve!("../build-cluster")),
|
|
411
|
-
[],
|
|
412
|
-
{
|
|
413
|
-
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
414
|
-
env: process.env
|
|
415
|
-
}
|
|
416
|
-
);
|
|
476
|
+
logger.info("모든 빌드가 완료되었습니다.");
|
|
477
|
+
}
|
|
417
478
|
|
|
418
|
-
|
|
419
|
-
|
|
479
|
+
private static async _prepareClusterAsync(): Promise<cp.ChildProcess> {
|
|
480
|
+
const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "_runBuildClusterAsync"]);
|
|
420
481
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
+
);
|
|
429
491
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
reject(err);
|
|
433
|
-
});
|
|
492
|
+
cluster.stdout!.pipe(process.stdout);
|
|
493
|
+
cluster.stderr!.pipe(process.stderr);
|
|
434
494
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
|
|
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
|
+
});
|
|
443
503
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const cb = (message: ISdCliBuildClusterResMessage): void => {
|
|
449
|
-
if (cmd === "watch" && message.type === "ready" && message.req.cmd === cmd && message.req.pkgPath === pkgPath) {
|
|
450
|
-
cluster.off("message", cb);
|
|
451
|
-
resolve();
|
|
452
|
-
} else if (cmd === "build" && message.type === "complete" && message.req.cmd === cmd && message.req.pkgPath === pkgPath) {
|
|
453
|
-
cluster.off("message", cb);
|
|
454
|
-
resolve(message.result?.buildResults);
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
cluster.on("message", cb);
|
|
458
|
-
|
|
459
|
-
cluster.send({
|
|
460
|
-
cmd,
|
|
461
|
-
pkgPath,
|
|
462
|
-
pkgConf,
|
|
463
|
-
builderKey,
|
|
464
|
-
withLint
|
|
465
|
-
} as ISdCliBuildClusterReqMessage);
|
|
466
|
-
});
|
|
467
|
-
}
|
|
504
|
+
cluster.on("error", (err) => {
|
|
505
|
+
logger.error(err);
|
|
506
|
+
reject(err);
|
|
507
|
+
});
|
|
468
508
|
|
|
469
|
-
|
|
470
|
-
|
|
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");
|
|
471
556
|
}
|
|
472
557
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}> {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
|
481
568
|
}
|
|
569
|
+
);
|
|
482
570
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
port: number
|
|
486
|
-
}>(async (resolve, reject) => {
|
|
487
|
-
const worker = cp.fork(
|
|
488
|
-
fileURLToPath(await import.meta.resolve!("../server-worker")),
|
|
489
|
-
[pkgPath],
|
|
490
|
-
{
|
|
491
|
-
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
492
|
-
env: process.env
|
|
493
|
-
}
|
|
494
|
-
);
|
|
571
|
+
worker.stdout!.pipe(process.stdout);
|
|
572
|
+
worker.stderr!.pipe(process.stderr);
|
|
495
573
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
});
|
|
522
|
-
});
|
|
523
|
-
}
|
|
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
|
+
}
|
|
524
599
|
}
|