@simplysm/sd-cli 7.0.224 → 7.0.231
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/.eslintrc.cjs +21 -21
- package/dist/bin/sd-cli.mjs +2 -2
- package/dist/builder/SdCliClientBuilder.mjs +4 -3
- package/dist/builder/SdCliServerBuilder.mjs +2 -2
- package/dist/entry-points/SdCliNpm.mjs +2 -8
- package/dist/packages/SdCliPackage.mjs +2 -2
- package/package.json +37 -38
- package/src/bin/sd-cli.ts +274 -274
- package/src/build-tool/SdCliElectron.ts +81 -81
- package/src/builder/SdCliClientBuilder.ts +737 -736
- package/src/builder/SdCliServerBuilder.ts +483 -483
- package/src/commons.ts +138 -135
- package/src/entry-points/SdCliNpm.ts +19 -26
- package/src/packages/SdCliPackage.ts +242 -242
|
@@ -1,483 +1,483 @@
|
|
|
1
|
-
import { INpmConfig, ISdCliPackageBuildResult, ISdCliServerPackageConfig } from "../commons";
|
|
2
|
-
import { EventEmitter } from "events";
|
|
3
|
-
import { FsUtil, Logger, PathUtil } from "@simplysm/sd-core-node";
|
|
4
|
-
import webpack from "webpack";
|
|
5
|
-
import path from "path";
|
|
6
|
-
import ts from "typescript";
|
|
7
|
-
import { SdCliBuildResultUtil } from "../utils/SdCliBuildResultUtil";
|
|
8
|
-
import { ErrorInfo } from "ts-loader/dist/interfaces";
|
|
9
|
-
import os from "os";
|
|
10
|
-
import { ESLint } from "eslint";
|
|
11
|
-
import TerserPlugin from "terser-webpack-plugin";
|
|
12
|
-
import { ObjectUtil, StringUtil } from "@simplysm/sd-core-common";
|
|
13
|
-
import ESLintWebpackPlugin from "eslint-webpack-plugin";
|
|
14
|
-
import CopyWebpackPlugin from "copy-webpack-plugin";
|
|
15
|
-
import { LicenseWebpackPlugin } from "license-webpack-plugin";
|
|
16
|
-
import { SdCliNpmConfigUtil } from "../utils/SdCliNpmConfigUtil";
|
|
17
|
-
import { createHash } from "crypto";
|
|
18
|
-
import LintResult = ESLint.LintResult;
|
|
19
|
-
|
|
20
|
-
export class SdCliServerBuilder extends EventEmitter {
|
|
21
|
-
private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
|
|
22
|
-
|
|
23
|
-
private readonly _tsconfigFilePath: string;
|
|
24
|
-
private readonly _parsedTsconfig: ts.ParsedCommandLine;
|
|
25
|
-
private readonly _npmConfigMap = new Map<string, INpmConfig>();
|
|
26
|
-
|
|
27
|
-
public constructor(private readonly _rootPath: string,
|
|
28
|
-
private readonly _config: ISdCliServerPackageConfig,
|
|
29
|
-
private readonly _workspaceRootPath: string) {
|
|
30
|
-
super();
|
|
31
|
-
|
|
32
|
-
// tsconfig
|
|
33
|
-
this._tsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
|
|
34
|
-
const tsconfig = FsUtil.readJson(this._tsconfigFilePath);
|
|
35
|
-
this._parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._rootPath);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public override on(event: "change", listener: () => void): this;
|
|
39
|
-
public override on(event: "complete", listener: (results: ISdCliPackageBuildResult[]) => void): this;
|
|
40
|
-
public override on(event: string | symbol, listener: (...args: any[]) => void): this {
|
|
41
|
-
return super.on(event, listener);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
public async watchAsync(): Promise<void> {
|
|
45
|
-
// DIST 비우기
|
|
46
|
-
await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
|
|
47
|
-
|
|
48
|
-
// 빌드 준비
|
|
49
|
-
const extModules = this._getExternalModules();
|
|
50
|
-
const webpackConfig = this._getWebpackConfig(true, extModules);
|
|
51
|
-
const compiler = webpack(webpackConfig);
|
|
52
|
-
await new Promise<void>((resolve, reject) => {
|
|
53
|
-
compiler.hooks.watchRun.tapAsync(this.constructor.name, (args, callback) => {
|
|
54
|
-
this.emit("change");
|
|
55
|
-
this._logger.debug("Webpack 빌드 수행...");
|
|
56
|
-
callback();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
compiler.watch({}, async (err, stats) => {
|
|
60
|
-
if (err != null || stats == null) {
|
|
61
|
-
this.emit("complete", [{
|
|
62
|
-
filePath: undefined,
|
|
63
|
-
line: undefined,
|
|
64
|
-
char: undefined,
|
|
65
|
-
code: undefined,
|
|
66
|
-
severity: "error",
|
|
67
|
-
message: err?.stack ?? "알 수 없는 오류 (stats=null)"
|
|
68
|
-
}]);
|
|
69
|
-
reject(err);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// .config.json 파일 쓰기
|
|
74
|
-
await this._writeDistConfigFileAsync();
|
|
75
|
-
|
|
76
|
-
// 결과 반환
|
|
77
|
-
this._logger.debug("Webpack 빌드 완료");
|
|
78
|
-
const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
|
|
79
|
-
this.emit("complete", results);
|
|
80
|
-
resolve();
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
public async buildAsync(): Promise<ISdCliPackageBuildResult[]> {
|
|
86
|
-
// DIST 비우기
|
|
87
|
-
await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
|
|
88
|
-
|
|
89
|
-
// 빌드
|
|
90
|
-
this._logger.debug("Webpack 빌드 수행...");
|
|
91
|
-
const extModules = this._getExternalModules();
|
|
92
|
-
const webpackConfig = this._getWebpackConfig(false, extModules);
|
|
93
|
-
const compiler = webpack(webpackConfig);
|
|
94
|
-
const buildResults = await new Promise<ISdCliPackageBuildResult[]>((resolve, reject) => {
|
|
95
|
-
compiler.run((err, stats) => {
|
|
96
|
-
if (err != null || stats == null) {
|
|
97
|
-
reject(err);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// 결과 반환
|
|
102
|
-
const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
|
|
103
|
-
resolve(results);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// .config.json 파일 쓰기
|
|
108
|
-
await this._writeDistConfigFileAsync();
|
|
109
|
-
|
|
110
|
-
// pm2.json 파일 쓰기
|
|
111
|
-
await this._writeDistPm2ConfigFileAsync();
|
|
112
|
-
|
|
113
|
-
// 배포용 package.json 파일 생성
|
|
114
|
-
await this._writeDistNpmConfigFileAsync(extModules.filter((item) => item.exists).map((item) => item.name));
|
|
115
|
-
|
|
116
|
-
// 마무리
|
|
117
|
-
this._logger.debug("Webpack 빌드 완료");
|
|
118
|
-
return buildResults;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
private async _writeDistConfigFileAsync(): Promise<void> {
|
|
122
|
-
const configDistPath = path.resolve(this._parsedTsconfig.options.outDir!, ".config.json");
|
|
123
|
-
await FsUtil.writeFileAsync(configDistPath, JSON.stringify(this._config.configs ?? {}, undefined, 2));
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private async _writeDistPm2ConfigFileAsync(): Promise<void> {
|
|
127
|
-
if (this._config.pm2 === undefined || this._config.pm2 === false) return;
|
|
128
|
-
|
|
129
|
-
const npmConfig = this._getNpmConfig(this._rootPath)!;
|
|
130
|
-
const pm2DistPath = path.resolve(this._parsedTsconfig.options.outDir!, "pm2.json");
|
|
131
|
-
await FsUtil.writeFileAsync(
|
|
132
|
-
pm2DistPath,
|
|
133
|
-
JSON.stringify(
|
|
134
|
-
ObjectUtil.merge(
|
|
135
|
-
{
|
|
136
|
-
"name": npmConfig.name.replace(/@/g, "").replace(/\//g, "-"),
|
|
137
|
-
"script": path.basename(path.resolve(this._parsedTsconfig.options.outDir!, "main.mjs")),
|
|
138
|
-
"node_args": "--experimental-specifier-resolution=node --experimental-import-meta-resolve",
|
|
139
|
-
"watch": true,
|
|
140
|
-
"watch_delay": 2000,
|
|
141
|
-
"ignore_watch": [
|
|
142
|
-
"node_modules",
|
|
143
|
-
"www"
|
|
144
|
-
].distinct(),
|
|
145
|
-
"interpreter": "node@" + process.versions.node,
|
|
146
|
-
"env": {
|
|
147
|
-
NODE_ENV: "production",
|
|
148
|
-
SD_VERSION: npmConfig.version,
|
|
149
|
-
TZ: "Asia/Seoul",
|
|
150
|
-
...this._config.env ? this._config.env : {}
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
(typeof this._config.pm2 !== "boolean") ? this._config.pm2 : {},
|
|
154
|
-
{
|
|
155
|
-
arrayProcess: "concat"
|
|
156
|
-
}),
|
|
157
|
-
undefined,
|
|
158
|
-
2
|
|
159
|
-
)
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
private async _writeDistNpmConfigFileAsync(deps: string[]): Promise<void> {
|
|
164
|
-
const distNpmConfig = ObjectUtil.clone(this._getNpmConfig(this._rootPath))!;
|
|
165
|
-
distNpmConfig.dependencies = {};
|
|
166
|
-
for (const dep of deps) {
|
|
167
|
-
distNpmConfig.dependencies[dep] = "*";
|
|
168
|
-
}
|
|
169
|
-
delete distNpmConfig.optionalDependencies;
|
|
170
|
-
delete distNpmConfig.devDependencies;
|
|
171
|
-
delete distNpmConfig.peerDependencies;
|
|
172
|
-
|
|
173
|
-
if (this._config.pm2 !== undefined) {
|
|
174
|
-
distNpmConfig.scripts = { "start": "pm2 start pm2.json" };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
await FsUtil.writeFileAsync(
|
|
178
|
-
path.resolve(this._parsedTsconfig.options.outDir!, "package.json"),
|
|
179
|
-
JSON.stringify(distNpmConfig, undefined, 2)
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
private _getInternalModuleCachePaths(workspaceName: string): string[] {
|
|
184
|
-
return [
|
|
185
|
-
...FsUtil.findAllParentChildDirPaths("node_modules/*/package.json", this._rootPath, this._workspaceRootPath),
|
|
186
|
-
...FsUtil.findAllParentChildDirPaths(`node_modules/!(@simplysm|@${workspaceName})/*/package.json`, this._rootPath, this._workspaceRootPath),
|
|
187
|
-
].map((p) => path.dirname(p));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private _getWebpackConfig(watch: boolean, extModules: { name: string; exists: boolean }[]): webpack.Configuration {
|
|
191
|
-
const workspaceNpmConfig = this._getNpmConfig(this._workspaceRootPath)!;
|
|
192
|
-
const workspaceName = workspaceNpmConfig.name;
|
|
193
|
-
|
|
194
|
-
const internalModuleCachePaths = watch ? this._getInternalModuleCachePaths(workspaceName) : undefined;
|
|
195
|
-
|
|
196
|
-
const npmConfig = this._getNpmConfig(this._rootPath)!;
|
|
197
|
-
const pkgKey = npmConfig.name.split("/").last()!;
|
|
198
|
-
// const pkgVersion = npmConfig.version;
|
|
199
|
-
|
|
200
|
-
const workspacePkgLockContent = FsUtil.readFile(path.resolve(this._workspaceRootPath, "
|
|
201
|
-
|
|
202
|
-
const cacheBasePath = path.resolve(this._rootPath, ".cache");
|
|
203
|
-
// const cachePath = path.resolve(cacheBasePath, pkgVersion);
|
|
204
|
-
|
|
205
|
-
let prevProgressMessage = "";
|
|
206
|
-
return {
|
|
207
|
-
mode: watch ? "development" : "production",
|
|
208
|
-
devtool: false,
|
|
209
|
-
target: ["node", "es2020"],
|
|
210
|
-
profile: false,
|
|
211
|
-
resolve: {
|
|
212
|
-
roots: [this._rootPath],
|
|
213
|
-
extensions: [".ts", ".js", ".mjs", ".cjs"],
|
|
214
|
-
symlinks: true,
|
|
215
|
-
modules: [this._workspaceRootPath, "node_modules"],
|
|
216
|
-
mainFields: ["es2020", "default", "module", "main"],
|
|
217
|
-
conditionNames: ["es2020", "..."]
|
|
218
|
-
},
|
|
219
|
-
resolveLoader: {
|
|
220
|
-
symlinks: true
|
|
221
|
-
},
|
|
222
|
-
context: this._workspaceRootPath,
|
|
223
|
-
entry: {
|
|
224
|
-
main: [
|
|
225
|
-
path.resolve(this._rootPath, "src/main.ts")
|
|
226
|
-
]
|
|
227
|
-
},
|
|
228
|
-
output: {
|
|
229
|
-
uniqueName: pkgKey,
|
|
230
|
-
hashFunction: "xxhash64",
|
|
231
|
-
clean: true,
|
|
232
|
-
path: this._parsedTsconfig.options.outDir,
|
|
233
|
-
filename: "[name].mjs",
|
|
234
|
-
chunkFilename: "[name].mjs",
|
|
235
|
-
assetModuleFilename: "res/[name][ext][query]",
|
|
236
|
-
library: {
|
|
237
|
-
type: "module"
|
|
238
|
-
},
|
|
239
|
-
module: true
|
|
240
|
-
},
|
|
241
|
-
experiments: {
|
|
242
|
-
outputModule: true
|
|
243
|
-
},
|
|
244
|
-
watch: false,
|
|
245
|
-
watchOptions: { poll: undefined, ignored: undefined },
|
|
246
|
-
performance: { hints: false },
|
|
247
|
-
infrastructureLogging: { level: "error" },
|
|
248
|
-
stats: "errors-warnings",
|
|
249
|
-
externals: extModules.toObject((item) => item.name, (item) => "node-commonjs " + item.name),
|
|
250
|
-
cache: {
|
|
251
|
-
type: "filesystem",
|
|
252
|
-
profile: watch ? undefined : false,
|
|
253
|
-
cacheDirectory: path.resolve(cacheBasePath, "server-webpack"),
|
|
254
|
-
maxMemoryGenerations: 1,
|
|
255
|
-
name: createHash("sha1")
|
|
256
|
-
.update(workspacePkgLockContent)
|
|
257
|
-
.update(JSON.stringify(this._parsedTsconfig.options))
|
|
258
|
-
.update(JSON.stringify(this._config))
|
|
259
|
-
.update(watch.toString())
|
|
260
|
-
.digest("hex")
|
|
261
|
-
},
|
|
262
|
-
// cache: { type: "memory", maxGenerations: 1 },
|
|
263
|
-
...watch ? {
|
|
264
|
-
snapshot: {
|
|
265
|
-
immutablePaths: internalModuleCachePaths,
|
|
266
|
-
managedPaths: internalModuleCachePaths
|
|
267
|
-
}
|
|
268
|
-
} : {},
|
|
269
|
-
node: false,
|
|
270
|
-
optimization: {
|
|
271
|
-
minimizer: watch ? [] : [
|
|
272
|
-
new TerserPlugin({
|
|
273
|
-
extractComments: false,
|
|
274
|
-
terserOptions: {
|
|
275
|
-
compress: true,
|
|
276
|
-
ecma: 2020,
|
|
277
|
-
sourceMap: false,
|
|
278
|
-
keep_classnames: true,
|
|
279
|
-
keep_fnames: true,
|
|
280
|
-
ie8: false,
|
|
281
|
-
safari10: false,
|
|
282
|
-
module: true,
|
|
283
|
-
format: {
|
|
284
|
-
comments: false
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
})
|
|
288
|
-
],
|
|
289
|
-
moduleIds: "deterministic",
|
|
290
|
-
chunkIds: watch ? "named" : "deterministic",
|
|
291
|
-
emitOnErrors: watch
|
|
292
|
-
},
|
|
293
|
-
module: {
|
|
294
|
-
strictExportPresence: true,
|
|
295
|
-
parser: {
|
|
296
|
-
javascript: {
|
|
297
|
-
importMeta: false
|
|
298
|
-
}
|
|
299
|
-
},
|
|
300
|
-
rules: [
|
|
301
|
-
{
|
|
302
|
-
test: /\.[cm]?[tj]sx?$/,
|
|
303
|
-
resolve: {
|
|
304
|
-
fullySpecified: false
|
|
305
|
-
}
|
|
306
|
-
},
|
|
307
|
-
...watch ? [
|
|
308
|
-
{
|
|
309
|
-
test: /\.[cm]?jsx?$/,
|
|
310
|
-
enforce: "pre" as const,
|
|
311
|
-
loader: "source-map-loader",
|
|
312
|
-
options: {
|
|
313
|
-
filterSourceMappingUrl: (mapUri: string, resourcePath: string) => {
|
|
314
|
-
const workspaceRegex = new RegExp(`node_modules[\\\\/]@${workspaceName}[\\\\/]`);
|
|
315
|
-
return !resourcePath.includes("node_modules")
|
|
316
|
-
|| (/node_modules[\\/]@simplysm[\\/]/).test(resourcePath)
|
|
317
|
-
|| workspaceRegex.test(resourcePath);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
] : [],
|
|
322
|
-
{
|
|
323
|
-
test: /\.[cm]?tsx?$/,
|
|
324
|
-
exclude: /node_modules/,
|
|
325
|
-
loader: "ts-loader",
|
|
326
|
-
options: {
|
|
327
|
-
configFile: this._tsconfigFilePath,
|
|
328
|
-
errorFormatter: (msg: ErrorInfo) => {
|
|
329
|
-
return SdCliBuildResultUtil.getMessage({
|
|
330
|
-
filePath: msg.file,
|
|
331
|
-
line: msg.line,
|
|
332
|
-
char: msg.character,
|
|
333
|
-
code: "TS" + msg.code.toString(),
|
|
334
|
-
severity: msg.severity,
|
|
335
|
-
message: msg.content
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
},
|
|
340
|
-
{
|
|
341
|
-
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf|xlsx?|pptx?|docx?|zip|pfx|pkl)$/,
|
|
342
|
-
type: "asset/resource"
|
|
343
|
-
}
|
|
344
|
-
]
|
|
345
|
-
},
|
|
346
|
-
plugins: [
|
|
347
|
-
...watch ? [] : [
|
|
348
|
-
new LicenseWebpackPlugin({
|
|
349
|
-
stats: { warnings: false, errors: false },
|
|
350
|
-
perChunkOutput: false,
|
|
351
|
-
outputFilename: "3rd_party_licenses.txt",
|
|
352
|
-
skipChildCompilers: true
|
|
353
|
-
}) as any
|
|
354
|
-
],
|
|
355
|
-
new CopyWebpackPlugin({
|
|
356
|
-
patterns: ["assets/"].map((item) => ({
|
|
357
|
-
context: this._rootPath,
|
|
358
|
-
to: item,
|
|
359
|
-
from: `src/${item}`,
|
|
360
|
-
noErrorOnMissing: true,
|
|
361
|
-
force: true,
|
|
362
|
-
globOptions: {
|
|
363
|
-
dot: true,
|
|
364
|
-
followSymbolicLinks: false,
|
|
365
|
-
ignore: [
|
|
366
|
-
".gitkeep",
|
|
367
|
-
"**/.DS_Store",
|
|
368
|
-
"**/Thumbs.db"
|
|
369
|
-
].map((i) => PathUtil.posix(this._rootPath, i))
|
|
370
|
-
},
|
|
371
|
-
priority: 0
|
|
372
|
-
}))
|
|
373
|
-
}),
|
|
374
|
-
new webpack.EnvironmentPlugin({
|
|
375
|
-
SD_VERSION: this._getNpmConfig(this._rootPath)!.version,
|
|
376
|
-
...this._config.env
|
|
377
|
-
}),
|
|
378
|
-
new ESLintWebpackPlugin({
|
|
379
|
-
context: this._rootPath,
|
|
380
|
-
eslintPath: path.resolve(this._workspaceRootPath, "node_modules", "eslint"),
|
|
381
|
-
exclude: ["node_modules"],
|
|
382
|
-
extensions: ["ts", "js", "mjs", "cjs"],
|
|
383
|
-
fix: false,
|
|
384
|
-
threads: false,
|
|
385
|
-
formatter: (results: LintResult[]) => {
|
|
386
|
-
const resultMessages: string[] = [];
|
|
387
|
-
for (const result of results) {
|
|
388
|
-
for (const msg of result.messages) {
|
|
389
|
-
const severity = msg.severity === 1 ? "warning" : msg.severity === 2 ? "error" : undefined;
|
|
390
|
-
if (severity === undefined) continue;
|
|
391
|
-
|
|
392
|
-
resultMessages.push(SdCliBuildResultUtil.getMessage({
|
|
393
|
-
filePath: result.filePath,
|
|
394
|
-
line: msg.line,
|
|
395
|
-
char: msg.column,
|
|
396
|
-
code: msg.ruleId?.toString(),
|
|
397
|
-
severity,
|
|
398
|
-
message: msg.message
|
|
399
|
-
}));
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return resultMessages.join(os.EOL);
|
|
403
|
-
}
|
|
404
|
-
}),
|
|
405
|
-
new webpack.ProgressPlugin({
|
|
406
|
-
handler: (per: number, msg: string, ...args: string[]) => {
|
|
407
|
-
const phaseText = msg ? ` - phase: ${msg}` : "";
|
|
408
|
-
const argsText = args.length > 0 ? ` - args: [${args.join(", ")}]` : "";
|
|
409
|
-
const progressMessage = `Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`;
|
|
410
|
-
if (progressMessage !== prevProgressMessage) {
|
|
411
|
-
prevProgressMessage = progressMessage;
|
|
412
|
-
this._logger.debug(progressMessage);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
})
|
|
416
|
-
]
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
private _getExternalModules(): { name: string; exists: boolean }[] {
|
|
421
|
-
const loadedModuleNames: string[] = [];
|
|
422
|
-
const results: { name: string; exists: boolean }[] = [];
|
|
423
|
-
|
|
424
|
-
const fn = (currPath: string): void => {
|
|
425
|
-
const npmConfig = this._getNpmConfig(currPath);
|
|
426
|
-
if (!npmConfig) return;
|
|
427
|
-
|
|
428
|
-
const deps = SdCliNpmConfigUtil.getDependencies(npmConfig);
|
|
429
|
-
|
|
430
|
-
for (const moduleName of deps.defaults) {
|
|
431
|
-
if (loadedModuleNames.includes(moduleName)) continue;
|
|
432
|
-
loadedModuleNames.push(moduleName);
|
|
433
|
-
|
|
434
|
-
const modulePath = FsUtil.findAllParentChildDirPaths("node_modules/" + moduleName, currPath, this._workspaceRootPath).first();
|
|
435
|
-
if (StringUtil.isNullOrEmpty(modulePath)) {
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if (FsUtil.exists(path.resolve(modulePath, "binding.gyp"))) {
|
|
440
|
-
results.push({ name: moduleName, exists: true });
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (this._config.externalNodeModules?.includes(moduleName)) {
|
|
444
|
-
results.push({ name: moduleName, exists: true });
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
fn(modulePath);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
for (const optModuleName of deps.optionals) {
|
|
451
|
-
if (loadedModuleNames.includes(optModuleName)) continue;
|
|
452
|
-
loadedModuleNames.push(optModuleName);
|
|
453
|
-
|
|
454
|
-
const optModulePath = FsUtil.findAllParentChildDirPaths("node_modules/" + optModuleName, currPath, this._workspaceRootPath).first();
|
|
455
|
-
if (StringUtil.isNullOrEmpty(optModulePath)) {
|
|
456
|
-
results.push({ name: optModuleName, exists: false });
|
|
457
|
-
continue;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
if (FsUtil.exists(path.resolve(optModulePath, "binding.gyp"))) {
|
|
461
|
-
results.push({ name: optModuleName, exists: true });
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (this._config.externalNodeModules?.includes(optModuleName)) {
|
|
465
|
-
results.push({ name: optModuleName, exists: true });
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
fn(optModulePath);
|
|
469
|
-
}
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
fn(this._rootPath);
|
|
473
|
-
|
|
474
|
-
return results;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
private _getNpmConfig(pkgPath: string): INpmConfig | undefined {
|
|
478
|
-
if (!this._npmConfigMap.has(pkgPath)) {
|
|
479
|
-
this._npmConfigMap.set(pkgPath, FsUtil.readJson(path.resolve(pkgPath, "package.json")));
|
|
480
|
-
}
|
|
481
|
-
return this._npmConfigMap.get(pkgPath);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
1
|
+
import { INpmConfig, ISdCliPackageBuildResult, ISdCliServerPackageConfig } from "../commons";
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
|
+
import { FsUtil, Logger, PathUtil } from "@simplysm/sd-core-node";
|
|
4
|
+
import webpack from "webpack";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import ts from "typescript";
|
|
7
|
+
import { SdCliBuildResultUtil } from "../utils/SdCliBuildResultUtil";
|
|
8
|
+
import { ErrorInfo } from "ts-loader/dist/interfaces";
|
|
9
|
+
import os from "os";
|
|
10
|
+
import { ESLint } from "eslint";
|
|
11
|
+
import TerserPlugin from "terser-webpack-plugin";
|
|
12
|
+
import { ObjectUtil, StringUtil } from "@simplysm/sd-core-common";
|
|
13
|
+
import ESLintWebpackPlugin from "eslint-webpack-plugin";
|
|
14
|
+
import CopyWebpackPlugin from "copy-webpack-plugin";
|
|
15
|
+
import { LicenseWebpackPlugin } from "license-webpack-plugin";
|
|
16
|
+
import { SdCliNpmConfigUtil } from "../utils/SdCliNpmConfigUtil";
|
|
17
|
+
import { createHash } from "crypto";
|
|
18
|
+
import LintResult = ESLint.LintResult;
|
|
19
|
+
|
|
20
|
+
export class SdCliServerBuilder extends EventEmitter {
|
|
21
|
+
private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
|
|
22
|
+
|
|
23
|
+
private readonly _tsconfigFilePath: string;
|
|
24
|
+
private readonly _parsedTsconfig: ts.ParsedCommandLine;
|
|
25
|
+
private readonly _npmConfigMap = new Map<string, INpmConfig>();
|
|
26
|
+
|
|
27
|
+
public constructor(private readonly _rootPath: string,
|
|
28
|
+
private readonly _config: ISdCliServerPackageConfig,
|
|
29
|
+
private readonly _workspaceRootPath: string) {
|
|
30
|
+
super();
|
|
31
|
+
|
|
32
|
+
// tsconfig
|
|
33
|
+
this._tsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
|
|
34
|
+
const tsconfig = FsUtil.readJson(this._tsconfigFilePath);
|
|
35
|
+
this._parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._rootPath);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public override on(event: "change", listener: () => void): this;
|
|
39
|
+
public override on(event: "complete", listener: (results: ISdCliPackageBuildResult[]) => void): this;
|
|
40
|
+
public override on(event: string | symbol, listener: (...args: any[]) => void): this {
|
|
41
|
+
return super.on(event, listener);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public async watchAsync(): Promise<void> {
|
|
45
|
+
// DIST 비우기
|
|
46
|
+
await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
|
|
47
|
+
|
|
48
|
+
// 빌드 준비
|
|
49
|
+
const extModules = this._getExternalModules();
|
|
50
|
+
const webpackConfig = this._getWebpackConfig(true, extModules);
|
|
51
|
+
const compiler = webpack(webpackConfig);
|
|
52
|
+
await new Promise<void>((resolve, reject) => {
|
|
53
|
+
compiler.hooks.watchRun.tapAsync(this.constructor.name, (args, callback) => {
|
|
54
|
+
this.emit("change");
|
|
55
|
+
this._logger.debug("Webpack 빌드 수행...");
|
|
56
|
+
callback();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
compiler.watch({}, async (err, stats) => {
|
|
60
|
+
if (err != null || stats == null) {
|
|
61
|
+
this.emit("complete", [{
|
|
62
|
+
filePath: undefined,
|
|
63
|
+
line: undefined,
|
|
64
|
+
char: undefined,
|
|
65
|
+
code: undefined,
|
|
66
|
+
severity: "error",
|
|
67
|
+
message: err?.stack ?? "알 수 없는 오류 (stats=null)"
|
|
68
|
+
}]);
|
|
69
|
+
reject(err);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// .config.json 파일 쓰기
|
|
74
|
+
await this._writeDistConfigFileAsync();
|
|
75
|
+
|
|
76
|
+
// 결과 반환
|
|
77
|
+
this._logger.debug("Webpack 빌드 완료");
|
|
78
|
+
const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
|
|
79
|
+
this.emit("complete", results);
|
|
80
|
+
resolve();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public async buildAsync(): Promise<ISdCliPackageBuildResult[]> {
|
|
86
|
+
// DIST 비우기
|
|
87
|
+
await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
|
|
88
|
+
|
|
89
|
+
// 빌드
|
|
90
|
+
this._logger.debug("Webpack 빌드 수행...");
|
|
91
|
+
const extModules = this._getExternalModules();
|
|
92
|
+
const webpackConfig = this._getWebpackConfig(false, extModules);
|
|
93
|
+
const compiler = webpack(webpackConfig);
|
|
94
|
+
const buildResults = await new Promise<ISdCliPackageBuildResult[]>((resolve, reject) => {
|
|
95
|
+
compiler.run((err, stats) => {
|
|
96
|
+
if (err != null || stats == null) {
|
|
97
|
+
reject(err);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 결과 반환
|
|
102
|
+
const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
|
|
103
|
+
resolve(results);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// .config.json 파일 쓰기
|
|
108
|
+
await this._writeDistConfigFileAsync();
|
|
109
|
+
|
|
110
|
+
// pm2.json 파일 쓰기
|
|
111
|
+
await this._writeDistPm2ConfigFileAsync();
|
|
112
|
+
|
|
113
|
+
// 배포용 package.json 파일 생성
|
|
114
|
+
await this._writeDistNpmConfigFileAsync(extModules.filter((item) => item.exists).map((item) => item.name));
|
|
115
|
+
|
|
116
|
+
// 마무리
|
|
117
|
+
this._logger.debug("Webpack 빌드 완료");
|
|
118
|
+
return buildResults;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private async _writeDistConfigFileAsync(): Promise<void> {
|
|
122
|
+
const configDistPath = path.resolve(this._parsedTsconfig.options.outDir!, ".config.json");
|
|
123
|
+
await FsUtil.writeFileAsync(configDistPath, JSON.stringify(this._config.configs ?? {}, undefined, 2));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private async _writeDistPm2ConfigFileAsync(): Promise<void> {
|
|
127
|
+
if (this._config.pm2 === undefined || this._config.pm2 === false) return;
|
|
128
|
+
|
|
129
|
+
const npmConfig = this._getNpmConfig(this._rootPath)!;
|
|
130
|
+
const pm2DistPath = path.resolve(this._parsedTsconfig.options.outDir!, "pm2.json");
|
|
131
|
+
await FsUtil.writeFileAsync(
|
|
132
|
+
pm2DistPath,
|
|
133
|
+
JSON.stringify(
|
|
134
|
+
ObjectUtil.merge(
|
|
135
|
+
{
|
|
136
|
+
"name": npmConfig.name.replace(/@/g, "").replace(/\//g, "-"),
|
|
137
|
+
"script": path.basename(path.resolve(this._parsedTsconfig.options.outDir!, "main.mjs")),
|
|
138
|
+
"node_args": "--experimental-specifier-resolution=node --experimental-import-meta-resolve",
|
|
139
|
+
"watch": true,
|
|
140
|
+
"watch_delay": 2000,
|
|
141
|
+
"ignore_watch": [
|
|
142
|
+
"node_modules",
|
|
143
|
+
"www"
|
|
144
|
+
].distinct(),
|
|
145
|
+
"interpreter": "node@" + process.versions.node,
|
|
146
|
+
"env": {
|
|
147
|
+
NODE_ENV: "production",
|
|
148
|
+
SD_VERSION: npmConfig.version,
|
|
149
|
+
TZ: "Asia/Seoul",
|
|
150
|
+
...this._config.env ? this._config.env : {}
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
(typeof this._config.pm2 !== "boolean") ? this._config.pm2 : {},
|
|
154
|
+
{
|
|
155
|
+
arrayProcess: "concat"
|
|
156
|
+
}),
|
|
157
|
+
undefined,
|
|
158
|
+
2
|
|
159
|
+
)
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private async _writeDistNpmConfigFileAsync(deps: string[]): Promise<void> {
|
|
164
|
+
const distNpmConfig = ObjectUtil.clone(this._getNpmConfig(this._rootPath))!;
|
|
165
|
+
distNpmConfig.dependencies = {};
|
|
166
|
+
for (const dep of deps) {
|
|
167
|
+
distNpmConfig.dependencies[dep] = "*";
|
|
168
|
+
}
|
|
169
|
+
delete distNpmConfig.optionalDependencies;
|
|
170
|
+
delete distNpmConfig.devDependencies;
|
|
171
|
+
delete distNpmConfig.peerDependencies;
|
|
172
|
+
|
|
173
|
+
if (this._config.pm2 !== undefined) {
|
|
174
|
+
distNpmConfig.scripts = { "start": "pm2 start pm2.json" };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
await FsUtil.writeFileAsync(
|
|
178
|
+
path.resolve(this._parsedTsconfig.options.outDir!, "package.json"),
|
|
179
|
+
JSON.stringify(distNpmConfig, undefined, 2)
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private _getInternalModuleCachePaths(workspaceName: string): string[] {
|
|
184
|
+
return [
|
|
185
|
+
...FsUtil.findAllParentChildDirPaths("node_modules/*/package.json", this._rootPath, this._workspaceRootPath),
|
|
186
|
+
...FsUtil.findAllParentChildDirPaths(`node_modules/!(@simplysm|@${workspaceName})/*/package.json`, this._rootPath, this._workspaceRootPath),
|
|
187
|
+
].map((p) => path.dirname(p));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private _getWebpackConfig(watch: boolean, extModules: { name: string; exists: boolean }[]): webpack.Configuration {
|
|
191
|
+
const workspaceNpmConfig = this._getNpmConfig(this._workspaceRootPath)!;
|
|
192
|
+
const workspaceName = workspaceNpmConfig.name;
|
|
193
|
+
|
|
194
|
+
const internalModuleCachePaths = watch ? this._getInternalModuleCachePaths(workspaceName) : undefined;
|
|
195
|
+
|
|
196
|
+
const npmConfig = this._getNpmConfig(this._rootPath)!;
|
|
197
|
+
const pkgKey = npmConfig.name.split("/").last()!;
|
|
198
|
+
// const pkgVersion = npmConfig.version;
|
|
199
|
+
|
|
200
|
+
const workspacePkgLockContent = FsUtil.readFile(path.resolve(this._workspaceRootPath, "yarn.lock"));
|
|
201
|
+
|
|
202
|
+
const cacheBasePath = path.resolve(this._rootPath, ".cache");
|
|
203
|
+
// const cachePath = path.resolve(cacheBasePath, pkgVersion);
|
|
204
|
+
|
|
205
|
+
let prevProgressMessage = "";
|
|
206
|
+
return {
|
|
207
|
+
mode: watch ? "development" : "production",
|
|
208
|
+
devtool: false,
|
|
209
|
+
target: ["node", "es2020"],
|
|
210
|
+
profile: false,
|
|
211
|
+
resolve: {
|
|
212
|
+
roots: [this._rootPath],
|
|
213
|
+
extensions: [".ts", ".js", ".mjs", ".cjs"],
|
|
214
|
+
symlinks: true,
|
|
215
|
+
modules: [this._workspaceRootPath, "node_modules"],
|
|
216
|
+
mainFields: ["es2020", "default", "module", "main"],
|
|
217
|
+
conditionNames: ["es2020", "..."]
|
|
218
|
+
},
|
|
219
|
+
resolveLoader: {
|
|
220
|
+
symlinks: true
|
|
221
|
+
},
|
|
222
|
+
context: this._workspaceRootPath,
|
|
223
|
+
entry: {
|
|
224
|
+
main: [
|
|
225
|
+
path.resolve(this._rootPath, "src/main.ts")
|
|
226
|
+
]
|
|
227
|
+
},
|
|
228
|
+
output: {
|
|
229
|
+
uniqueName: pkgKey,
|
|
230
|
+
hashFunction: "xxhash64",
|
|
231
|
+
clean: true,
|
|
232
|
+
path: this._parsedTsconfig.options.outDir,
|
|
233
|
+
filename: "[name].mjs",
|
|
234
|
+
chunkFilename: "[name].mjs",
|
|
235
|
+
assetModuleFilename: "res/[name][ext][query]",
|
|
236
|
+
library: {
|
|
237
|
+
type: "module"
|
|
238
|
+
},
|
|
239
|
+
module: true
|
|
240
|
+
},
|
|
241
|
+
experiments: {
|
|
242
|
+
outputModule: true
|
|
243
|
+
},
|
|
244
|
+
watch: false,
|
|
245
|
+
watchOptions: { poll: undefined, ignored: undefined },
|
|
246
|
+
performance: { hints: false },
|
|
247
|
+
infrastructureLogging: { level: "error" },
|
|
248
|
+
stats: "errors-warnings",
|
|
249
|
+
externals: extModules.toObject((item) => item.name, (item) => "node-commonjs " + item.name),
|
|
250
|
+
cache: {
|
|
251
|
+
type: "filesystem",
|
|
252
|
+
profile: watch ? undefined : false,
|
|
253
|
+
cacheDirectory: path.resolve(cacheBasePath, "server-webpack"),
|
|
254
|
+
maxMemoryGenerations: 1,
|
|
255
|
+
name: createHash("sha1")
|
|
256
|
+
.update(workspacePkgLockContent)
|
|
257
|
+
.update(JSON.stringify(this._parsedTsconfig.options))
|
|
258
|
+
.update(JSON.stringify(this._config))
|
|
259
|
+
.update(watch.toString())
|
|
260
|
+
.digest("hex")
|
|
261
|
+
},
|
|
262
|
+
// cache: { type: "memory", maxGenerations: 1 },
|
|
263
|
+
...watch ? {
|
|
264
|
+
snapshot: {
|
|
265
|
+
immutablePaths: internalModuleCachePaths,
|
|
266
|
+
managedPaths: internalModuleCachePaths
|
|
267
|
+
}
|
|
268
|
+
} : {},
|
|
269
|
+
node: false,
|
|
270
|
+
optimization: {
|
|
271
|
+
minimizer: watch ? [] : [
|
|
272
|
+
new TerserPlugin({
|
|
273
|
+
extractComments: false,
|
|
274
|
+
terserOptions: {
|
|
275
|
+
compress: true,
|
|
276
|
+
ecma: 2020,
|
|
277
|
+
sourceMap: false,
|
|
278
|
+
keep_classnames: true,
|
|
279
|
+
keep_fnames: true,
|
|
280
|
+
ie8: false,
|
|
281
|
+
safari10: false,
|
|
282
|
+
module: true,
|
|
283
|
+
format: {
|
|
284
|
+
comments: false
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
],
|
|
289
|
+
moduleIds: "deterministic",
|
|
290
|
+
chunkIds: watch ? "named" : "deterministic",
|
|
291
|
+
emitOnErrors: watch
|
|
292
|
+
},
|
|
293
|
+
module: {
|
|
294
|
+
strictExportPresence: true,
|
|
295
|
+
parser: {
|
|
296
|
+
javascript: {
|
|
297
|
+
importMeta: false
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
rules: [
|
|
301
|
+
{
|
|
302
|
+
test: /\.[cm]?[tj]sx?$/,
|
|
303
|
+
resolve: {
|
|
304
|
+
fullySpecified: false
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
...watch ? [
|
|
308
|
+
{
|
|
309
|
+
test: /\.[cm]?jsx?$/,
|
|
310
|
+
enforce: "pre" as const,
|
|
311
|
+
loader: "source-map-loader",
|
|
312
|
+
options: {
|
|
313
|
+
filterSourceMappingUrl: (mapUri: string, resourcePath: string) => {
|
|
314
|
+
const workspaceRegex = new RegExp(`node_modules[\\\\/]@${workspaceName}[\\\\/]`);
|
|
315
|
+
return !resourcePath.includes("node_modules")
|
|
316
|
+
|| (/node_modules[\\/]@simplysm[\\/]/).test(resourcePath)
|
|
317
|
+
|| workspaceRegex.test(resourcePath);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
] : [],
|
|
322
|
+
{
|
|
323
|
+
test: /\.[cm]?tsx?$/,
|
|
324
|
+
exclude: /node_modules/,
|
|
325
|
+
loader: "ts-loader",
|
|
326
|
+
options: {
|
|
327
|
+
configFile: this._tsconfigFilePath,
|
|
328
|
+
errorFormatter: (msg: ErrorInfo) => {
|
|
329
|
+
return SdCliBuildResultUtil.getMessage({
|
|
330
|
+
filePath: msg.file,
|
|
331
|
+
line: msg.line,
|
|
332
|
+
char: msg.character,
|
|
333
|
+
code: "TS" + msg.code.toString(),
|
|
334
|
+
severity: msg.severity,
|
|
335
|
+
message: msg.content
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf|xlsx?|pptx?|docx?|zip|pfx|pkl)$/,
|
|
342
|
+
type: "asset/resource"
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
},
|
|
346
|
+
plugins: [
|
|
347
|
+
...watch ? [] : [
|
|
348
|
+
new LicenseWebpackPlugin({
|
|
349
|
+
stats: { warnings: false, errors: false },
|
|
350
|
+
perChunkOutput: false,
|
|
351
|
+
outputFilename: "3rd_party_licenses.txt",
|
|
352
|
+
skipChildCompilers: true
|
|
353
|
+
}) as any
|
|
354
|
+
],
|
|
355
|
+
new CopyWebpackPlugin({
|
|
356
|
+
patterns: ["assets/"].map((item) => ({
|
|
357
|
+
context: this._rootPath,
|
|
358
|
+
to: item,
|
|
359
|
+
from: `src/${item}`,
|
|
360
|
+
noErrorOnMissing: true,
|
|
361
|
+
force: true,
|
|
362
|
+
globOptions: {
|
|
363
|
+
dot: true,
|
|
364
|
+
followSymbolicLinks: false,
|
|
365
|
+
ignore: [
|
|
366
|
+
".gitkeep",
|
|
367
|
+
"**/.DS_Store",
|
|
368
|
+
"**/Thumbs.db"
|
|
369
|
+
].map((i) => PathUtil.posix(this._rootPath, i))
|
|
370
|
+
},
|
|
371
|
+
priority: 0
|
|
372
|
+
}))
|
|
373
|
+
}),
|
|
374
|
+
new webpack.EnvironmentPlugin({
|
|
375
|
+
SD_VERSION: this._getNpmConfig(this._rootPath)!.version,
|
|
376
|
+
...this._config.env
|
|
377
|
+
}),
|
|
378
|
+
new ESLintWebpackPlugin({
|
|
379
|
+
context: this._rootPath,
|
|
380
|
+
eslintPath: path.resolve(this._workspaceRootPath, "node_modules", "eslint"),
|
|
381
|
+
exclude: ["node_modules"],
|
|
382
|
+
extensions: ["ts", "js", "mjs", "cjs"],
|
|
383
|
+
fix: false,
|
|
384
|
+
threads: false,
|
|
385
|
+
formatter: (results: LintResult[]) => {
|
|
386
|
+
const resultMessages: string[] = [];
|
|
387
|
+
for (const result of results) {
|
|
388
|
+
for (const msg of result.messages) {
|
|
389
|
+
const severity = msg.severity === 1 ? "warning" : msg.severity === 2 ? "error" : undefined;
|
|
390
|
+
if (severity === undefined) continue;
|
|
391
|
+
|
|
392
|
+
resultMessages.push(SdCliBuildResultUtil.getMessage({
|
|
393
|
+
filePath: result.filePath,
|
|
394
|
+
line: msg.line,
|
|
395
|
+
char: msg.column,
|
|
396
|
+
code: msg.ruleId?.toString(),
|
|
397
|
+
severity,
|
|
398
|
+
message: msg.message
|
|
399
|
+
}));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return resultMessages.join(os.EOL);
|
|
403
|
+
}
|
|
404
|
+
}),
|
|
405
|
+
new webpack.ProgressPlugin({
|
|
406
|
+
handler: (per: number, msg: string, ...args: string[]) => {
|
|
407
|
+
const phaseText = msg ? ` - phase: ${msg}` : "";
|
|
408
|
+
const argsText = args.length > 0 ? ` - args: [${args.join(", ")}]` : "";
|
|
409
|
+
const progressMessage = `Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`;
|
|
410
|
+
if (progressMessage !== prevProgressMessage) {
|
|
411
|
+
prevProgressMessage = progressMessage;
|
|
412
|
+
this._logger.debug(progressMessage);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
})
|
|
416
|
+
]
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private _getExternalModules(): { name: string; exists: boolean }[] {
|
|
421
|
+
const loadedModuleNames: string[] = [];
|
|
422
|
+
const results: { name: string; exists: boolean }[] = [];
|
|
423
|
+
|
|
424
|
+
const fn = (currPath: string): void => {
|
|
425
|
+
const npmConfig = this._getNpmConfig(currPath);
|
|
426
|
+
if (!npmConfig) return;
|
|
427
|
+
|
|
428
|
+
const deps = SdCliNpmConfigUtil.getDependencies(npmConfig);
|
|
429
|
+
|
|
430
|
+
for (const moduleName of deps.defaults) {
|
|
431
|
+
if (loadedModuleNames.includes(moduleName)) continue;
|
|
432
|
+
loadedModuleNames.push(moduleName);
|
|
433
|
+
|
|
434
|
+
const modulePath = FsUtil.findAllParentChildDirPaths("node_modules/" + moduleName, currPath, this._workspaceRootPath).first();
|
|
435
|
+
if (StringUtil.isNullOrEmpty(modulePath)) {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (FsUtil.exists(path.resolve(modulePath, "binding.gyp"))) {
|
|
440
|
+
results.push({ name: moduleName, exists: true });
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (this._config.externalNodeModules?.includes(moduleName)) {
|
|
444
|
+
results.push({ name: moduleName, exists: true });
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
fn(modulePath);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
for (const optModuleName of deps.optionals) {
|
|
451
|
+
if (loadedModuleNames.includes(optModuleName)) continue;
|
|
452
|
+
loadedModuleNames.push(optModuleName);
|
|
453
|
+
|
|
454
|
+
const optModulePath = FsUtil.findAllParentChildDirPaths("node_modules/" + optModuleName, currPath, this._workspaceRootPath).first();
|
|
455
|
+
if (StringUtil.isNullOrEmpty(optModulePath)) {
|
|
456
|
+
results.push({ name: optModuleName, exists: false });
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (FsUtil.exists(path.resolve(optModulePath, "binding.gyp"))) {
|
|
461
|
+
results.push({ name: optModuleName, exists: true });
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (this._config.externalNodeModules?.includes(optModuleName)) {
|
|
465
|
+
results.push({ name: optModuleName, exists: true });
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
fn(optModulePath);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
fn(this._rootPath);
|
|
473
|
+
|
|
474
|
+
return results;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
private _getNpmConfig(pkgPath: string): INpmConfig | undefined {
|
|
478
|
+
if (!this._npmConfigMap.has(pkgPath)) {
|
|
479
|
+
this._npmConfigMap.set(pkgPath, FsUtil.readJson(path.resolve(pkgPath, "package.json")));
|
|
480
|
+
}
|
|
481
|
+
return this._npmConfigMap.get(pkgPath);
|
|
482
|
+
}
|
|
483
|
+
}
|