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