@simplysm/sd-cli 7.0.23 → 7.0.37

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.
Files changed (33) hide show
  1. package/dist/bin/sd-cli.d.ts +1 -1
  2. package/dist/bin/sd-cli.mjs +15 -3
  3. package/dist/builder/SdCliClientBuilder.d.ts +20 -0
  4. package/dist/builder/SdCliClientBuilder.mjs +392 -0
  5. package/dist/builder/SdCliServerBuilder.d.ts +24 -0
  6. package/dist/builder/SdCliServerBuilder.mjs +399 -0
  7. package/dist/builder/SdCliTsLibBuilder.d.ts +5 -3
  8. package/dist/builder/SdCliTsLibBuilder.mjs +17 -18
  9. package/dist/commons.d.ts +10 -4
  10. package/dist/entry-points/SdCliNpm.d.ts +6 -0
  11. package/dist/entry-points/SdCliNpm.mjs +22 -0
  12. package/dist/entry-points/SdCliPrepare.d.ts +5 -0
  13. package/dist/entry-points/SdCliPrepare.mjs +54 -0
  14. package/dist/entry-points/SdCliWorkspace.d.ts +1 -0
  15. package/dist/entry-points/SdCliWorkspace.mjs +43 -36
  16. package/dist/packages/SdCliPackage.d.ts +1 -0
  17. package/dist/packages/SdCliPackage.mjs +20 -19
  18. package/dist/utils/SdCliBuildResultUtil.d.ts +4 -1
  19. package/dist/utils/SdCliBuildResultUtil.mjs +24 -2
  20. package/dist/utils/SdCliNpmConfigUtil.d.ts +7 -0
  21. package/dist/utils/SdCliNpmConfigUtil.mjs +15 -0
  22. package/package.json +22 -7
  23. package/src/bin/sd-cli.ts +22 -2
  24. package/src/builder/SdCliClientBuilder.ts +426 -0
  25. package/src/builder/SdCliServerBuilder.ts +457 -0
  26. package/src/builder/SdCliTsLibBuilder.ts +26 -21
  27. package/src/commons.ts +6 -5
  28. package/src/entry-points/SdCliNpm.ts +26 -0
  29. package/src/entry-points/SdCliPrepare.ts +54 -0
  30. package/src/entry-points/SdCliWorkspace.ts +51 -42
  31. package/src/packages/SdCliPackage.ts +21 -23
  32. package/src/utils/SdCliBuildResultUtil.ts +30 -1
  33. package/src/utils/SdCliNpmConfigUtil.ts +16 -0
@@ -0,0 +1,457 @@
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 LintResult = ESLint.LintResult;
17
+
18
+ export class SdCliServerBuilder extends EventEmitter {
19
+ private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
20
+
21
+ private readonly _tsconfigFilePath: string;
22
+ private readonly _parsedTsconfig: ts.ParsedCommandLine;
23
+ private readonly _npmConfigMap = new Map<string, INpmConfig>();
24
+
25
+ public constructor(private readonly _rootPath: string,
26
+ private readonly _config: ISdCliServerPackageConfig,
27
+ private readonly _workspaceRootPath: string) {
28
+ super();
29
+
30
+ // tsconfig
31
+ this._tsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
32
+ const tsconfig = FsUtil.readJson(this._tsconfigFilePath);
33
+ this._parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._rootPath);
34
+ }
35
+
36
+ public override on(event: "change", listener: () => void): this;
37
+ public override on(event: "complete", listener: (results: ISdCliPackageBuildResult[]) => void): this;
38
+ public override on(event: string | symbol, listener: (...args: any[]) => void): this {
39
+ return super.on(event, listener);
40
+ }
41
+
42
+ public async watchAsync(): Promise<void> {
43
+ // DIST 비우기
44
+ await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
45
+
46
+ // 빌드 준비
47
+ const extModuleNames = this._getExternalModuleNames();
48
+ const webpackConfig = this._getWebpackConfig(true, extModuleNames);
49
+ const compiler = webpack(webpackConfig);
50
+ await new Promise<void>((resolve, reject) => {
51
+ compiler.hooks.watchRun.tapAsync(this.constructor.name, (args, callback) => {
52
+ this.emit("change");
53
+ callback();
54
+
55
+ this._logger.debug("Webpack 빌드 수행...");
56
+ });
57
+
58
+ compiler.watch({}, async (err, stats) => {
59
+ if (err != null || stats == null) {
60
+ reject(err);
61
+ return;
62
+ }
63
+
64
+ // .config.json 파일 쓰기
65
+ await this._writeDistConfigFileAsync();
66
+
67
+ // 결과 반환
68
+ const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
69
+ this.emit("complete", results);
70
+
71
+ // 마무리
72
+ this._logger.debug("Webpack 빌드 완료");
73
+ resolve();
74
+ });
75
+ });
76
+ }
77
+
78
+ public async buildAsync(): Promise<ISdCliPackageBuildResult[]> {
79
+ // DIST 비우기
80
+ await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
81
+
82
+ // 빌드
83
+ this._logger.debug("Webpack 빌드 수행...");
84
+ const extModuleNames = this._getExternalModuleNames();
85
+ const webpackConfig = this._getWebpackConfig(false, extModuleNames);
86
+ const compiler = webpack(webpackConfig);
87
+ const buildResults = await new Promise<ISdCliPackageBuildResult[]>((resolve, reject) => {
88
+ compiler.run((err, stats) => {
89
+ if (err != null || stats == null) {
90
+ reject(err);
91
+ return;
92
+ }
93
+
94
+ // 결과 반환
95
+ const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
96
+ resolve(results);
97
+ });
98
+ });
99
+
100
+ // .config.json 파일 쓰기
101
+ await this._writeDistConfigFileAsync();
102
+
103
+ // pm2.json 파일 쓰기
104
+ await this._writeDistPm2ConfigFileAsync();
105
+
106
+ // iis 파일 쓰기
107
+ await this._writeDistIisConfigFileAsync();
108
+
109
+ // 배포용 package.json 파일 생성
110
+ await this._writeDistNpmConfigFileAsync(extModuleNames);
111
+
112
+ // 마무리
113
+ this._logger.debug("Webpack 빌드 완료");
114
+ return buildResults;
115
+ }
116
+
117
+ private async _writeDistConfigFileAsync(): Promise<void> {
118
+ const configDistPath = path.resolve(this._parsedTsconfig.options.outDir!, ".config.json");
119
+ await FsUtil.writeFileAsync(configDistPath, JSON.stringify(this._config.configs ?? {}, undefined, 2));
120
+ }
121
+
122
+ private async _writeDistPm2ConfigFileAsync(): Promise<void> {
123
+ if (this._config.pm2 === undefined || this._config.pm2 === false) return;
124
+
125
+ const npmConfig = this._getNpmConfig(this._rootPath)!;
126
+ const pm2DistPath = path.resolve(this._parsedTsconfig.options.outDir!, "pm2.json");
127
+ await FsUtil.writeFileAsync(
128
+ pm2DistPath,
129
+ JSON.stringify(
130
+ ObjectUtil.merge(
131
+ {
132
+ "name": npmConfig.name.replace(/@/g, "").replace(/\//g, "-"),
133
+ "script": path.basename(path.resolve(this._parsedTsconfig.options.outDir!, "main.mjs")),
134
+ "watch": true,
135
+ "watch_delay": 2000,
136
+ "ignore_watch": [
137
+ "node_modules",
138
+ "www"
139
+ ].distinct(),
140
+ "interpreter": "node@" + process.versions.node,
141
+ "env": {
142
+ NODE_ENV: "production",
143
+ SD_VERSION: npmConfig.version,
144
+ TZ: "Asia/Seoul",
145
+ ...this._config.env ? this._config.env : {}
146
+ }
147
+ },
148
+ (typeof this._config.pm2 !== "boolean") ? this._config.pm2 : {},
149
+ {
150
+ arrayProcess: "concat"
151
+ }),
152
+ undefined,
153
+ 2
154
+ )
155
+ );
156
+ }
157
+
158
+ private async _writeDistIisConfigFileAsync(): Promise<void> {
159
+ if (this._config.iis === undefined || this._config.iis === false) return;
160
+
161
+ const iisDistPath = path.resolve(this._parsedTsconfig.options.outDir!, "web.config");
162
+ const serverExeFilePath = (this._config.iis !== true && "serverExeFilePath" in this._config.iis)
163
+ ? (this._config.iis.serverExeFilePath ?? "C:\\Program Files\\nodejs\\node.exe")
164
+ : "C:\\Program Files\\nodejs\\node.exe";
165
+ await FsUtil.writeFileAsync(iisDistPath, `
166
+ <configuration>
167
+ <system.webServer>
168
+ <webSocket enabled="false" />
169
+ <handlers>
170
+ <add name="iisnode" path="main.js" verb="*" modules="iisnode" />
171
+ </handlers>
172
+ <iisnode nodeProcessCommandLine="${serverExeFilePath}"
173
+ watchedFiles="web.config;*.js"
174
+ loggingEnabled="true"
175
+ devErrorsEnabled="true" />
176
+ <rewrite>
177
+ <rules>
178
+ <rule name="main">
179
+ <action type="Rewrite" url="main.mjs" />
180
+ </rule>
181
+ </rules>
182
+ </rewrite>
183
+ <httpErrors errorMode="Detailed" />
184
+ </system.webServer>
185
+ </configuration>`.trim());
186
+ }
187
+
188
+ private async _writeDistNpmConfigFileAsync(deps: string[]): Promise<void> {
189
+ const distNpmConfig = ObjectUtil.clone(this._getNpmConfig(this._rootPath))!;
190
+ distNpmConfig.dependencies = {};
191
+ for (const dep of deps) {
192
+ distNpmConfig.dependencies[dep] = "*";
193
+ }
194
+ delete distNpmConfig.optionalDependencies;
195
+ delete distNpmConfig.devDependencies;
196
+ delete distNpmConfig.peerDependencies;
197
+
198
+ if (this._config.pm2 !== undefined) {
199
+ distNpmConfig.scripts = { "start": "pm2 start pm2.json" };
200
+ }
201
+
202
+ await FsUtil.writeFileAsync(
203
+ path.resolve(this._parsedTsconfig.options.outDir!, "package.json"),
204
+ JSON.stringify(distNpmConfig, undefined, 2)
205
+ );
206
+ }
207
+
208
+ private _getWebpackConfig(watch: boolean, extModuleNames: string[]): webpack.Configuration {
209
+ const internalModuleCachePaths = watch
210
+ ? FsUtil.findAllParentChildDirPaths("node_modules/!(@simplysm)", this._rootPath, this._workspaceRootPath)
211
+ : undefined;
212
+
213
+ return {
214
+ mode: watch ? "development" : "production",
215
+ devtool: false,
216
+ target: ["node", "es2020"],
217
+ profile: false,
218
+ resolve: {
219
+ roots: [this._rootPath],
220
+ extensions: [".ts", ".js", ".mjs", ".cjs"],
221
+ symlinks: true,
222
+ mainFields: ["es2020", "default", "module", "main"]
223
+ },
224
+ context: this._workspaceRootPath,
225
+ entry: {
226
+ main: [
227
+ path.resolve(this._rootPath, "src/main.ts")
228
+ ]
229
+ },
230
+ output: {
231
+ clean: true,
232
+ path: this._parsedTsconfig.options.outDir,
233
+ filename: "[name].mjs",
234
+ chunkFilename: "[name].mjs",
235
+ assetModuleFilename: "res/[name][ext][query]",
236
+ libraryTarget: "module"
237
+ },
238
+ experiments: {
239
+ outputModule: true
240
+ },
241
+ performance: { hints: false },
242
+ node: false,
243
+ stats: "errors-warnings",
244
+ externals: extModuleNames,
245
+ ...watch ? {
246
+ cache: { type: "memory", maxGenerations: 1 },
247
+ snapshot: {
248
+ immutablePaths: internalModuleCachePaths,
249
+ managedPaths: internalModuleCachePaths
250
+ }
251
+ } : {
252
+ cache: false
253
+ },
254
+ optimization: {
255
+ ...watch ? {} : {
256
+ minimize: true,
257
+ minimizer: [
258
+ new TerserPlugin({
259
+ extractComments: false,
260
+ terserOptions: {
261
+ compress: true,
262
+ ecma: 2020,
263
+ sourceMap: false,
264
+ keep_classnames: true,
265
+ keep_fnames: true,
266
+ ie8: false,
267
+ safari10: false,
268
+ module: true,
269
+ format: {
270
+ comments: false
271
+ }
272
+ }
273
+ })
274
+ ]
275
+ },
276
+ moduleIds: "deterministic",
277
+ chunkIds: watch ? "named" : "deterministic",
278
+ emitOnErrors: watch
279
+ },
280
+ module: {
281
+ strictExportPresence: true,
282
+ rules: [
283
+ {
284
+ test: /\.m?js/,
285
+ resolve: {
286
+ fullySpecified: false
287
+ }
288
+ },
289
+ ...watch ? [
290
+ {
291
+ test: /\.m?js$/,
292
+ enforce: "pre" as const,
293
+ loader: "source-map-loader",
294
+ options: {
295
+ filterSourceMappingUrl: (mapUri: string, resourcePath: string) => {
296
+ return !resourcePath.includes("node_modules") || (/@simplysm[\\/]sd/).test(resourcePath);
297
+ }
298
+ }
299
+ }
300
+ ] : [],
301
+ {
302
+ test: /\.ts$/,
303
+ exclude: /node_modules/,
304
+ loader: "ts-loader",
305
+ options: {
306
+ configFile: this._tsconfigFilePath,
307
+ errorFormatter: (msg: ErrorInfo) => {
308
+ return SdCliBuildResultUtil.getMessage({
309
+ filePath: msg.file,
310
+ line: msg.line,
311
+ char: msg.character,
312
+ code: "TS" + msg.code.toString(),
313
+ severity: msg.severity,
314
+ message: msg.content
315
+ });
316
+ }
317
+ }
318
+ },
319
+ {
320
+ test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf|xlsx?|pptx?|docx?|zip|pfx|pkl)$/,
321
+ type: "asset/resource"
322
+ }
323
+ ]
324
+ },
325
+ plugins: [
326
+ ...watch ? [] : [
327
+ new LicenseWebpackPlugin({
328
+ stats: { warnings: false, errors: false },
329
+ perChunkOutput: false,
330
+ outputFilename: "3rd_party_licenses.txt",
331
+ skipChildCompilers: true
332
+ }) as any
333
+ ],
334
+ new CopyWebpackPlugin({
335
+ patterns: ["assets/"].map((item) => ({
336
+ context: this._rootPath,
337
+ to: item,
338
+ from: `src/${item}`,
339
+ noErrorOnMissing: true,
340
+ force: true,
341
+ globOptions: {
342
+ dot: true,
343
+ followSymbolicLinks: false,
344
+ ignore: [
345
+ ".gitkeep",
346
+ "**/.DS_Store",
347
+ "**/Thumbs.db"
348
+ ].map((i) => PathUtil.posix(this._rootPath, i))
349
+ },
350
+ priority: 0
351
+ }))
352
+ }),
353
+ new webpack.EnvironmentPlugin({
354
+ SD_VERSION: this._getNpmConfig(this._rootPath)!.version,
355
+ ...this._config.env
356
+ }),
357
+ new ESLintWebpackPlugin({
358
+ context: this._rootPath,
359
+ eslintPath: path.resolve(this._workspaceRootPath, "node_modules", "eslint"),
360
+ exclude: ["node_modules"],
361
+ extensions: ["ts", "js", "mjs", "cjs"],
362
+ fix: false,
363
+ threads: false,
364
+ formatter: (results: LintResult[]) => {
365
+ const resultMessages: string[] = [];
366
+ for (const result of results) {
367
+ for (const msg of result.messages) {
368
+ const severity = msg.severity === 1 ? "warning" : msg.severity === 2 ? "error" : undefined;
369
+ if (severity === undefined) continue;
370
+
371
+ resultMessages.push(SdCliBuildResultUtil.getMessage({
372
+ filePath: result.filePath,
373
+ line: msg.line,
374
+ char: msg.column,
375
+ code: msg.ruleId?.toString(),
376
+ severity,
377
+ message: msg.message
378
+ }));
379
+ }
380
+ }
381
+ return resultMessages.join(os.EOL);
382
+ }
383
+ })/*,
384
+ new webpack.ProgressPlugin({
385
+ handler: (per: number, msg: string, ...args: string[]) => {
386
+ const phaseText = msg ? ` - phase: ${msg}` : "";
387
+ const argsText = args.length > 0 ? ` - args: [${args.join(", ")}]` : "";
388
+ this._logger.debug(`Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`);
389
+ }
390
+ })*/
391
+ ]
392
+ };
393
+ }
394
+
395
+ private _getExternalModuleNames(): string[] {
396
+ const loadedModuleNames: string[] = [];
397
+ const resultSet = new Set<string>();
398
+
399
+ const fn = (currPath: string): void => {
400
+ const npmConfig = this._getNpmConfig(currPath);
401
+ if (!npmConfig) return;
402
+
403
+ const moduleNames = [
404
+ ...Object.keys(npmConfig.dependencies ?? {}),
405
+ ...Object.keys(npmConfig.peerDependencies ?? {}).filter((item) => !npmConfig.peerDependenciesMeta?.[item].optional)
406
+ ];
407
+ const optModuleNames = [
408
+ ...Object.keys(npmConfig.optionalDependencies ?? {}),
409
+ ...Object.keys(npmConfig.peerDependencies ?? {}).filter((item) => npmConfig.peerDependenciesMeta?.[item].optional)
410
+ ].distinct();
411
+
412
+ for (const moduleName of moduleNames) {
413
+ if (loadedModuleNames.includes(moduleName)) continue;
414
+ loadedModuleNames.push(moduleName);
415
+
416
+ const modulePath = FsUtil.findAllParentChildDirPaths("node_modules/" + moduleName, currPath, this._workspaceRootPath).first();
417
+ if (StringUtil.isNullOrEmpty(modulePath)) {
418
+ continue;
419
+ }
420
+
421
+ if (FsUtil.exists(path.resolve(modulePath, "binding.gyp"))) {
422
+ resultSet.add(moduleName);
423
+ }
424
+
425
+ fn(modulePath);
426
+ }
427
+
428
+ for (const optModuleName of optModuleNames) {
429
+ if (loadedModuleNames.includes(optModuleName)) continue;
430
+ loadedModuleNames.push(optModuleName);
431
+
432
+ const optModulePath = FsUtil.findAllParentChildDirPaths("node_modules/" + optModuleName, currPath, this._workspaceRootPath).first();
433
+ if (StringUtil.isNullOrEmpty(optModulePath)) {
434
+ resultSet.add(optModuleName);
435
+ continue;
436
+ }
437
+
438
+ if (FsUtil.exists(path.resolve(optModulePath, "binding.gyp"))) {
439
+ resultSet.add(optModuleName);
440
+ }
441
+
442
+ fn(optModulePath);
443
+ }
444
+ };
445
+
446
+ fn(this._rootPath);
447
+
448
+ return Array.from(resultSet.values());
449
+ }
450
+
451
+ private _getNpmConfig(pkgPath: string): INpmConfig | undefined {
452
+ if (!this._npmConfigMap.has(pkgPath)) {
453
+ this._npmConfigMap.set(pkgPath, FsUtil.readJson(path.resolve(pkgPath, "package.json")));
454
+ }
455
+ return this._npmConfigMap.get(pkgPath);
456
+ }
457
+ }
@@ -1,4 +1,4 @@
1
- import { ISdCliPackageBuildResult } from "../commons";
1
+ import { INpmConfig, ISdCliPackageBuildResult } from "../commons";
2
2
  import { EventEmitter } from "events";
3
3
  import ts from "typescript";
4
4
  import { FsUtil, Logger, PathUtil, SdFsWatcher } from "@simplysm/sd-core-node";
@@ -11,6 +11,7 @@ import { SdCliPackageLinter } from "../build-tool/SdCliPackageLinter";
11
11
  import { SdCliCacheCompilerHost } from "../build-tool/SdCliCacheCompilerHost";
12
12
  import { SdCliNgCacheCompilerHost } from "../build-tool/SdCliNgCacheCompilerHost";
13
13
  import { NgCompiler } from "@angular/compiler-cli/src/ngtsc/core";
14
+ import { SdCliNpmConfigUtil } from "../utils/SdCliNpmConfigUtil";
14
15
 
15
16
  export class SdCliTsLibBuilder extends EventEmitter {
16
17
  private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
@@ -26,10 +27,26 @@ export class SdCliTsLibBuilder extends EventEmitter {
26
27
  private _ngProgram?: NgtscProgram;
27
28
  private _builder?: ts.EmitAndSemanticDiagnosticsBuilderProgram;
28
29
 
29
- public constructor(private readonly _rootPath,
30
- private readonly _isAngular: boolean) {
30
+ private readonly _tsconfigFilePath: string;
31
+ private readonly _parsedTsconfig: ts.ParsedCommandLine;
32
+ private readonly _npmConfig: INpmConfig;
33
+
34
+ private readonly _isAngular: boolean;
35
+
36
+ public constructor(private readonly _rootPath) {
31
37
  super();
32
38
  this._linter = new SdCliPackageLinter(this._rootPath);
39
+
40
+ // tsconfig
41
+ this._tsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
42
+ const tsconfig = FsUtil.readJson(this._tsconfigFilePath);
43
+ this._parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._rootPath);
44
+
45
+ // package.json
46
+ this._npmConfig = FsUtil.readJson(path.resolve(this._rootPath, "package.json"));
47
+
48
+ // else
49
+ this._isAngular = SdCliNpmConfigUtil.getDependencies(this._npmConfig).defaults.includes("@angular/core");
33
50
  }
34
51
 
35
52
  public override on(event: "change", listener: () => void): this;
@@ -41,14 +58,11 @@ export class SdCliTsLibBuilder extends EventEmitter {
41
58
  public async watchAsync(): Promise<void> {
42
59
  this.emit("change");
43
60
 
44
- // TSCONFIG 읽기
45
- const parsedTsconfig = await this._getParsedTsconfigAsync();
46
-
47
61
  // DIST 비우기
48
- await FsUtil.removeAsync(parsedTsconfig.options.outDir!);
62
+ await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
49
63
 
50
64
  // 프로그램 리로드
51
- const buildPack = this._createSdBuildPack(parsedTsconfig);
65
+ const buildPack = this._createSdBuildPack(this._parsedTsconfig);
52
66
 
53
67
  const relatedPaths = await this.getAllRelatedPathsAsync();
54
68
  const watcher = SdFsWatcher.watch(relatedPaths);
@@ -65,7 +79,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
65
79
  }
66
80
 
67
81
  // 빌드
68
- const watchBuildPack = this._createSdBuildPack(parsedTsconfig);
82
+ const watchBuildPack = this._createSdBuildPack(this._parsedTsconfig);
69
83
 
70
84
  // 린트
71
85
  const watchBuildResults = await this._runBuilderAsync(watchBuildPack.builder, watchBuildPack.ngCompiler);
@@ -90,14 +104,11 @@ export class SdCliTsLibBuilder extends EventEmitter {
90
104
  }
91
105
 
92
106
  public async buildAsync(): Promise<ISdCliPackageBuildResult[]> {
93
- // TSCONFIG 읽기
94
- const parsedTsconfig = await this._getParsedTsconfigAsync();
95
-
96
107
  // DIST 비우기
97
- await FsUtil.removeAsync(parsedTsconfig.options.outDir!);
108
+ await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
98
109
 
99
110
  // 프로그램 리로드
100
- const buildPack = this._createSdBuildPack(parsedTsconfig);
111
+ const buildPack = this._createSdBuildPack(this._parsedTsconfig);
101
112
 
102
113
  // 빌드
103
114
  const buildResults = await this._runBuilderAsync(buildPack.builder, buildPack.ngCompiler);
@@ -165,7 +176,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
165
176
  results.push(
166
177
  ...diagnostics
167
178
  .filter((item) => [ts.DiagnosticCategory.Error, ts.DiagnosticCategory.Warning].includes(item.category))
168
- .map((item) => SdCliBuildResultUtil.convertDiagsToResult(item))
179
+ .map((item) => SdCliBuildResultUtil.convertFromTsDiag(item))
169
180
  .filterExists()
170
181
  );
171
182
 
@@ -312,12 +323,6 @@ export class SdCliTsLibBuilder extends EventEmitter {
312
323
  return compilerHost;
313
324
  }
314
325
  }
315
-
316
- private async _getParsedTsconfigAsync(): Promise<ts.ParsedCommandLine> {
317
- const tsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
318
- const tsconfig = await FsUtil.readJsonAsync(tsconfigFilePath);
319
- return ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._rootPath);
320
- }
321
326
  }
322
327
 
323
328
  interface IFileCache {
package/src/commons.ts CHANGED
@@ -15,6 +15,7 @@ export interface INpmConfig {
15
15
  optionalDependencies?: Record<string, string>;
16
16
  devDependencies?: Record<string, string>;
17
17
  peerDependencies?: Record<string, string>;
18
+ peerDependenciesMeta?: Record<string, { optional?: boolean }>;
18
19
  }
19
20
 
20
21
  export interface ITsconfig {
@@ -39,17 +40,17 @@ export interface ISdCliConfig {
39
40
  localUpdates?: Record<string, string>;
40
41
  }
41
42
 
42
- export type TSdCliPackageConfig = ISdCliLibPackageConfig | ISdCliNgLibPackageConfig | ISdCliServerPackageConfig;
43
+ export type TSdCliPackageConfig = ISdCliLibPackageConfig | ISdCliServerPackageConfig;
43
44
 
44
45
  export interface ISdCliLibPackageConfig {
45
46
  type: "library";
46
47
  publish?: "npm";
47
48
  }
48
49
 
49
- export interface ISdCliNgLibPackageConfig {
50
- type: "angular";
51
- }
52
-
53
50
  export interface ISdCliServerPackageConfig {
54
51
  type: "server";
52
+ env?: Record<string, string>;
53
+ configs?: Record<string, any>;
54
+ pm2?: Record<string, any> | boolean;
55
+ iis?: { serverExeFilePath?: string } | boolean;
55
56
  }
@@ -0,0 +1,26 @@
1
+ import { Logger, SdProcess } from "@simplysm/sd-core-node";
2
+ import { SdCliPrepare } from "./SdCliPrepare";
3
+
4
+ export class SdCliNpm {
5
+ private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
6
+
7
+ public constructor(private readonly _rootPath: string) {
8
+ }
9
+
10
+ public async updateAsync(): Promise<void> {
11
+ try {
12
+ this._logger.debug("업데이트할 패키지 확인...");
13
+ await SdProcess.spawnAsync("npm outdated", { cwd: this._rootPath });
14
+ }
15
+ catch (err) {
16
+ }
17
+
18
+ this._logger.debug("업데이트 시작...");
19
+ await SdProcess.spawnAsync("npm update", { cwd: this._rootPath });
20
+
21
+ this._logger.debug("sd-cli 준비...");
22
+ await new SdCliPrepare().prepareAsync();
23
+
24
+ this._logger.info("노드 패키지 업데이트 완료");
25
+ }
26
+ }
@@ -0,0 +1,54 @@
1
+ import { FsUtil, Logger } from "@simplysm/sd-core-node";
2
+
3
+ export class SdCliPrepare {
4
+ private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
5
+
6
+ public async prepareAsync(): Promise<void> {
7
+ // 타입체크 속도 지연 메시지 추가
8
+ const r1 = await this._modifyTypescriptCodeForTypeCheckPerformanceWarning();
9
+ this._logger.log(`[모듈수정] 타입체크 속도 지연 메시지 추가 (${r1 ? "O" : "X"})`);
10
+ }
11
+
12
+ private async _modifyTypescriptCodeForTypeCheckPerformanceWarning(): Promise<boolean> {
13
+ const fileUrl = await import.meta.resolve!("typescript");
14
+ const filePath = fileUrl.replace(/^file:\/\/\//, "");
15
+ const fileContent = await FsUtil.readFileAsync(filePath);
16
+ const modifiedFileContent = fileContent.replace(`
17
+ function checkSourceElement(node) {
18
+ if (node) {
19
+ var saveCurrentNode = currentNode;
20
+ currentNode = node;
21
+ instantiationCount = 0;
22
+ checkSourceElementWorker(node);
23
+ currentNode = saveCurrentNode;
24
+ }
25
+ }`, `
26
+ function checkSourceElement(node) {
27
+ if (node) {
28
+ const prevUsage = process.cpuUsage();
29
+
30
+ var saveCurrentNode = currentNode;
31
+ currentNode = node;
32
+ instantiationCount = 0;
33
+ checkSourceElementWorker(node);
34
+ currentNode = saveCurrentNode;
35
+
36
+ const usage = process.cpuUsage(prevUsage);
37
+ if (node.hasChildPerformanceWarning) {
38
+ node.parent.hasChildPerformanceWarning = true;
39
+ }
40
+ else if (usage.user + usage.system > 2000 * 1000 && node.kind !== 253) {
41
+ error(node, {
42
+ code: 9000,
43
+ category: ts.DiagnosticCategory.Warning,
44
+ key: "simplysm_check_source_element_performance_slow",
45
+ message: "소스코드 타입분석에 너무 오랜 시간이 소요됩니다. [" + Math.round((usage.user + usage.system) / 1000) + "ms/cpu, KIND: " + node.kind + "]"
46
+ });
47
+ node.parent.hasChildPerformanceWarning = true;
48
+ }
49
+ }
50
+ }`);
51
+ await FsUtil.writeFileAsync(filePath, modifiedFileContent);
52
+ return fileContent !== modifiedFileContent;
53
+ }
54
+ }