@simplysm/sd-cli 7.0.24 → 7.0.38

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 (35) hide show
  1. package/dist/bin/sd-cli.mjs +2 -1
  2. package/dist/build-tool/SdCliNgCacheCompilerHost.mjs +2 -2
  3. package/dist/builder/SdCliClientBuilder.d.ts +20 -0
  4. package/dist/builder/SdCliClientBuilder.mjs +417 -0
  5. package/dist/builder/SdCliJsLibBuilder.mjs +2 -2
  6. package/dist/builder/SdCliServerBuilder.d.ts +7 -4
  7. package/dist/builder/SdCliServerBuilder.mjs +232 -92
  8. package/dist/builder/SdCliTsLibBuilder.d.ts +5 -3
  9. package/dist/builder/SdCliTsLibBuilder.mjs +17 -18
  10. package/dist/commons.d.ts +13 -1
  11. package/dist/entry-points/SdCliLocalUpdate.mjs +15 -9
  12. package/dist/entry-points/SdCliNpm.mjs +8 -4
  13. package/dist/entry-points/SdCliPrepare.mjs +3 -2
  14. package/dist/entry-points/SdCliWorkspace.d.ts +1 -0
  15. package/dist/entry-points/SdCliWorkspace.mjs +55 -23
  16. package/dist/packages/SdCliPackage.d.ts +5 -4
  17. package/dist/packages/SdCliPackage.mjs +27 -28
  18. package/dist/utils/SdCliBuildResultUtil.mjs +3 -4
  19. package/dist/utils/SdCliNpmConfigUtil.d.ts +4 -1
  20. package/dist/utils/SdCliNpmConfigUtil.mjs +12 -8
  21. package/package.json +19 -8
  22. package/src/bin/sd-cli.ts +1 -0
  23. package/src/build-tool/SdCliNgCacheCompilerHost.ts +1 -1
  24. package/src/builder/SdCliClientBuilder.ts +451 -0
  25. package/src/builder/SdCliJsLibBuilder.ts +1 -1
  26. package/src/builder/SdCliServerBuilder.ts +260 -91
  27. package/src/builder/SdCliTsLibBuilder.ts +26 -21
  28. package/src/commons.ts +10 -1
  29. package/src/entry-points/SdCliLocalUpdate.ts +14 -8
  30. package/src/entry-points/SdCliNpm.ts +7 -3
  31. package/src/entry-points/SdCliPrepare.ts +2 -1
  32. package/src/entry-points/SdCliWorkspace.ts +63 -24
  33. package/src/packages/SdCliPackage.ts +29 -34
  34. package/src/utils/SdCliBuildResultUtil.ts +2 -3
  35. package/src/utils/SdCliNpmConfigUtil.ts +11 -7
@@ -8,17 +8,17 @@ import { SdCliBuildResultUtil } from "../utils/SdCliBuildResultUtil";
8
8
  import { ErrorInfo } from "ts-loader/dist/interfaces";
9
9
  import os from "os";
10
10
  import { ESLint } from "eslint";
11
- import webpackMerge from "webpack-merge";
12
11
  import TerserPlugin from "terser-webpack-plugin";
13
- import { StringUtil } from "@simplysm/sd-core-common";
14
- import { SdCliNpmConfigUtil } from "../utils/SdCliNpmConfigUtil";
12
+ import { ObjectUtil, StringUtil } from "@simplysm/sd-core-common";
15
13
  import ESLintWebpackPlugin from "eslint-webpack-plugin";
16
14
  import CopyWebpackPlugin from "copy-webpack-plugin";
15
+ import { LicenseWebpackPlugin } from "license-webpack-plugin";
17
16
  import LintResult = ESLint.LintResult;
18
17
 
19
18
  export class SdCliServerBuilder extends EventEmitter {
20
19
  private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
21
20
 
21
+ private readonly _tsconfigFilePath: string;
22
22
  private readonly _parsedTsconfig: ts.ParsedCommandLine;
23
23
  private readonly _npmConfigMap = new Map<string, INpmConfig>();
24
24
 
@@ -28,8 +28,8 @@ export class SdCliServerBuilder extends EventEmitter {
28
28
  super();
29
29
 
30
30
  // tsconfig
31
- const tsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
32
- const tsconfig = FsUtil.readJson(tsconfigFilePath);
31
+ this._tsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
32
+ const tsconfig = FsUtil.readJson(this._tsconfigFilePath);
33
33
  this._parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._rootPath);
34
34
  }
35
35
 
@@ -40,18 +40,46 @@ export class SdCliServerBuilder extends EventEmitter {
40
40
  }
41
41
 
42
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
+ this._logger.debug("Webpack 빌드 수행...");
54
+ callback();
55
+ });
56
+
57
+ compiler.watch({}, async (err, stats) => {
58
+ if (err != null || stats == null) {
59
+ reject(err);
60
+ return;
61
+ }
62
+
63
+ // .config.json 파일 쓰기
64
+ await this._writeDistConfigFileAsync();
65
+
66
+ // 결과 반환
67
+ this._logger.debug("Webpack 빌드 완료");
68
+ const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
69
+ this.emit("complete", results);
70
+ resolve();
71
+ });
72
+ });
43
73
  }
44
74
 
45
75
  public async buildAsync(): Promise<ISdCliPackageBuildResult[]> {
46
76
  // DIST 비우기
47
77
  await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
48
78
 
49
- // 빌드 준비
50
- const extModuleNames = this._findExternalModules(false).map((item) => item.name);
51
-
52
79
  // 빌드
53
80
  this._logger.debug("Webpack 빌드 수행...");
54
- const webpackConfig = this._getWebpackBuildConfig(extModuleNames);
81
+ const extModuleNames = this._getExternalModuleNames();
82
+ const webpackConfig = this._getWebpackConfig(false, extModuleNames);
55
83
  const compiler = webpack(webpackConfig);
56
84
  const buildResults = await new Promise<ISdCliPackageBuildResult[]>((resolve, reject) => {
57
85
  compiler.run((err, stats) => {
@@ -60,18 +88,127 @@ export class SdCliServerBuilder extends EventEmitter {
60
88
  return;
61
89
  }
62
90
 
63
- // 결과 리턴
91
+ // 결과 반환
64
92
  const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
65
93
  resolve(results);
66
94
  });
67
95
  });
68
- this._logger.debug("Webpack 빌드 완료");
69
96
 
97
+ // .config.json 파일 쓰기
98
+ await this._writeDistConfigFileAsync();
99
+
100
+ // pm2.json 파일 쓰기
101
+ await this._writeDistPm2ConfigFileAsync();
102
+
103
+ // iis 파일 쓰기
104
+ await this._writeDistIisConfigFileAsync();
105
+
106
+ // 배포용 package.json 파일 생성
107
+ await this._writeDistNpmConfigFileAsync(extModuleNames);
108
+
109
+ // 마무리
110
+ this._logger.debug("Webpack 빌드 완료");
70
111
  return buildResults;
71
112
  }
72
113
 
73
- private get _webpackCommonConfig(): webpack.Configuration {
114
+ private async _writeDistConfigFileAsync(): Promise<void> {
115
+ const configDistPath = path.resolve(this._parsedTsconfig.options.outDir!, ".config.json");
116
+ await FsUtil.writeFileAsync(configDistPath, JSON.stringify(this._config.configs ?? {}, undefined, 2));
117
+ }
118
+
119
+ private async _writeDistPm2ConfigFileAsync(): Promise<void> {
120
+ if (this._config.pm2 === undefined || this._config.pm2 === false) return;
121
+
122
+ const npmConfig = this._getNpmConfig(this._rootPath)!;
123
+ const pm2DistPath = path.resolve(this._parsedTsconfig.options.outDir!, "pm2.json");
124
+ await FsUtil.writeFileAsync(
125
+ pm2DistPath,
126
+ JSON.stringify(
127
+ ObjectUtil.merge(
128
+ {
129
+ "name": npmConfig.name.replace(/@/g, "").replace(/\//g, "-"),
130
+ "script": path.basename(path.resolve(this._parsedTsconfig.options.outDir!, "main.mjs")),
131
+ "watch": true,
132
+ "watch_delay": 2000,
133
+ "ignore_watch": [
134
+ "node_modules",
135
+ "www"
136
+ ].distinct(),
137
+ "interpreter": "node@" + process.versions.node,
138
+ "env": {
139
+ NODE_ENV: "production",
140
+ SD_VERSION: npmConfig.version,
141
+ TZ: "Asia/Seoul",
142
+ ...this._config.env ? this._config.env : {}
143
+ }
144
+ },
145
+ (typeof this._config.pm2 !== "boolean") ? this._config.pm2 : {},
146
+ {
147
+ arrayProcess: "concat"
148
+ }),
149
+ undefined,
150
+ 2
151
+ )
152
+ );
153
+ }
154
+
155
+ private async _writeDistIisConfigFileAsync(): Promise<void> {
156
+ if (this._config.iis === undefined || this._config.iis === false) return;
157
+
158
+ const iisDistPath = path.resolve(this._parsedTsconfig.options.outDir!, "web.config");
159
+ const serverExeFilePath = (this._config.iis !== true && "serverExeFilePath" in this._config.iis)
160
+ ? (this._config.iis.serverExeFilePath ?? "C:\\Program Files\\nodejs\\node.exe")
161
+ : "C:\\Program Files\\nodejs\\node.exe";
162
+ await FsUtil.writeFileAsync(iisDistPath, `
163
+ <configuration>
164
+ <system.webServer>
165
+ <webSocket enabled="false" />
166
+ <handlers>
167
+ <add name="iisnode" path="main.js" verb="*" modules="iisnode" />
168
+ </handlers>
169
+ <iisnode nodeProcessCommandLine="${serverExeFilePath}"
170
+ watchedFiles="web.config;*.js"
171
+ loggingEnabled="true"
172
+ devErrorsEnabled="true" />
173
+ <rewrite>
174
+ <rules>
175
+ <rule name="main">
176
+ <action type="Rewrite" url="main.mjs" />
177
+ </rule>
178
+ </rules>
179
+ </rewrite>
180
+ <httpErrors errorMode="Detailed" />
181
+ </system.webServer>
182
+ </configuration>`.trim());
183
+ }
184
+
185
+ private async _writeDistNpmConfigFileAsync(deps: string[]): Promise<void> {
186
+ const distNpmConfig = ObjectUtil.clone(this._getNpmConfig(this._rootPath))!;
187
+ distNpmConfig.dependencies = {};
188
+ for (const dep of deps) {
189
+ distNpmConfig.dependencies[dep] = "*";
190
+ }
191
+ delete distNpmConfig.optionalDependencies;
192
+ delete distNpmConfig.devDependencies;
193
+ delete distNpmConfig.peerDependencies;
194
+
195
+ if (this._config.pm2 !== undefined) {
196
+ distNpmConfig.scripts = { "start": "pm2 start pm2.json" };
197
+ }
198
+
199
+ await FsUtil.writeFileAsync(
200
+ path.resolve(this._parsedTsconfig.options.outDir!, "package.json"),
201
+ JSON.stringify(distNpmConfig, undefined, 2)
202
+ );
203
+ }
204
+
205
+ private _getWebpackConfig(watch: boolean, extModuleNames: string[]): webpack.Configuration {
206
+ const internalModuleCachePaths = watch
207
+ ? FsUtil.findAllParentChildDirPaths("node_modules/!(@simplysm)", this._rootPath, this._workspaceRootPath)
208
+ : undefined;
209
+
74
210
  return {
211
+ mode: watch ? "development" : "production",
75
212
  devtool: false,
76
213
  target: ["node", "es2020"],
77
214
  profile: false,
@@ -90,22 +227,82 @@ export class SdCliServerBuilder extends EventEmitter {
90
227
  output: {
91
228
  clean: true,
92
229
  path: this._parsedTsconfig.options.outDir,
93
- filename: "[name].js",
94
- chunkFilename: "[name].js",
95
- assetModuleFilename: "resources/[name][ext][query]"
230
+ filename: "[name].mjs",
231
+ chunkFilename: "[name].mjs",
232
+ assetModuleFilename: "res/[name][ext][query]",
233
+ libraryTarget: "module"
234
+ },
235
+ experiments: {
236
+ outputModule: true
96
237
  },
97
238
  performance: { hints: false },
98
- node: false,
239
+ node: {
240
+ __dirname: true
241
+ },
99
242
  stats: "errors-warnings",
243
+ externals: extModuleNames,
244
+ ...watch ? {
245
+ cache: { type: "memory", maxGenerations: 1 },
246
+ snapshot: {
247
+ immutablePaths: internalModuleCachePaths,
248
+ managedPaths: internalModuleCachePaths
249
+ }
250
+ } : {
251
+ cache: false
252
+ },
253
+ optimization: {
254
+ ...watch ? {} : {
255
+ minimize: true,
256
+ minimizer: [
257
+ new TerserPlugin({
258
+ extractComments: false,
259
+ terserOptions: {
260
+ compress: true,
261
+ ecma: 2020,
262
+ sourceMap: false,
263
+ keep_classnames: true,
264
+ keep_fnames: true,
265
+ ie8: false,
266
+ safari10: false,
267
+ module: true,
268
+ format: {
269
+ comments: false
270
+ }
271
+ }
272
+ })
273
+ ]
274
+ },
275
+ moduleIds: "deterministic",
276
+ chunkIds: watch ? "named" : "deterministic",
277
+ emitOnErrors: watch
278
+ },
100
279
  module: {
101
280
  strictExportPresence: true,
102
281
  rules: [
282
+ {
283
+ test: /\.m?js/,
284
+ resolve: {
285
+ fullySpecified: false
286
+ }
287
+ },
288
+ ...watch ? [
289
+ {
290
+ test: /\.m?js$/,
291
+ enforce: "pre" as const,
292
+ loader: "source-map-loader",
293
+ options: {
294
+ filterSourceMappingUrl: (mapUri: string, resourcePath: string) => {
295
+ return !resourcePath.includes("node_modules") || (/@simplysm[\\/]sd/).test(resourcePath);
296
+ }
297
+ }
298
+ }
299
+ ] : [],
103
300
  {
104
301
  test: /\.ts$/,
105
302
  exclude: /node_modules/,
106
303
  loader: "ts-loader",
107
304
  options: {
108
- compilerOptions: this._parsedTsconfig.options,
305
+ configFile: this._tsconfigFilePath,
109
306
  errorFormatter: (msg: ErrorInfo) => {
110
307
  return SdCliBuildResultUtil.getMessage({
111
308
  filePath: msg.file,
@@ -125,6 +322,14 @@ export class SdCliServerBuilder extends EventEmitter {
125
322
  ]
126
323
  },
127
324
  plugins: [
325
+ ...watch ? [] : [
326
+ new LicenseWebpackPlugin({
327
+ stats: { warnings: false, errors: false },
328
+ perChunkOutput: false,
329
+ outputFilename: "3rd_party_licenses.txt",
330
+ skipChildCompilers: true
331
+ }) as any
332
+ ],
128
333
  new CopyWebpackPlugin({
129
334
  patterns: ["assets/"].map((item) => ({
130
335
  context: this._rootPath,
@@ -150,6 +355,8 @@ export class SdCliServerBuilder extends EventEmitter {
150
355
  }),
151
356
  new ESLintWebpackPlugin({
152
357
  context: this._rootPath,
358
+ eslintPath: path.resolve(this._workspaceRootPath, "node_modules", "eslint"),
359
+ exclude: ["node_modules"],
153
360
  extensions: ["ts", "js", "mjs", "cjs"],
154
361
  fix: false,
155
362
  threads: false,
@@ -172,110 +379,72 @@ export class SdCliServerBuilder extends EventEmitter {
172
379
  }
173
380
  return resultMessages.join(os.EOL);
174
381
  }
175
- }),
382
+ })/*,
176
383
  new webpack.ProgressPlugin({
177
384
  handler: (per: number, msg: string, ...args: string[]) => {
178
385
  const phaseText = msg ? ` - phase: ${msg}` : "";
179
386
  const argsText = args.length > 0 ? ` - args: [${args.join(", ")}]` : "";
180
387
  this._logger.debug(`Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`);
181
388
  }
182
- })
389
+ })*/
183
390
  ]
184
391
  };
185
392
  }
186
393
 
187
- private _getWebpackWatchConfig(extModuleNames: string[]): webpack.Configuration {
188
- const internalModuleCachePaths = FsUtil.findAllParentChildDirPaths("node_modules/!(@simplysm)", this._rootPath, this._workspaceRootPath);
189
-
190
- return webpackMerge(this._webpackCommonConfig, {
191
- mode: "development",
192
- output: {
193
- libraryTarget: "umd"
194
- },
195
- module: {
196
- rules: [
197
- {
198
- test: /\.m?js$/,
199
- enforce: "pre",
200
- loader: "source-map-loader",
201
- options: {
202
- filterSourceMappingUrl: (mapUri: string, resourcePath: string) => {
203
- return !resourcePath.includes("node_modules") || (/@simplysm[\\/]sd/).test(resourcePath);
204
- }
205
- }
206
- }
207
- ]
208
- },
209
- cache: { type: "memory", maxGenerations: 1 },
210
- snapshot: {
211
- immutablePaths: internalModuleCachePaths,
212
- managedPaths: internalModuleCachePaths
213
- },
214
- optimization: {
215
- moduleIds: "deterministic",
216
- chunkIds: "named",
217
- emitOnErrors: true
218
- },
219
- externals: extModuleNames
220
- });
221
- }
222
-
223
- private _getWebpackBuildConfig(extModuleNames: string[]): webpack.Configuration {
224
- return webpackMerge(this._webpackCommonConfig, {
225
- mode: "production",
226
- output: {
227
- libraryTarget: "module"
228
- },
229
- cache: false,
230
- optimization: {
231
- minimizer: [
232
- new TerserPlugin({
233
- terserOptions: {
234
- ecma: 2020,
235
- sourceMap: false,
236
- keep_classnames: true,
237
- keep_fnames: true,
238
- ie8: false,
239
- safari10: false,
240
- module: true
241
- }
242
- })
243
- ],
244
- moduleIds: "deterministic",
245
- chunkIds: "deterministic",
246
- emitOnErrors: false
247
- },
248
- externals: extModuleNames
249
- });
250
- }
251
-
252
- private _findExternalModules(all: boolean): { name: string; path: string }[] {
394
+ private _getExternalModuleNames(): string[] {
253
395
  const loadedModuleNames: string[] = [];
254
- const externalModules: { name: string; path: string }[] = [];
396
+ const resultSet = new Set<string>();
255
397
 
256
398
  const fn = (currPath: string): void => {
257
399
  const npmConfig = this._getNpmConfig(currPath);
258
400
  if (!npmConfig) return;
259
401
 
402
+ const moduleNames = [
403
+ ...Object.keys(npmConfig.dependencies ?? {}),
404
+ ...Object.keys(npmConfig.peerDependencies ?? {}).filter((item) => !npmConfig.peerDependenciesMeta?.[item].optional)
405
+ ];
406
+ const optModuleNames = [
407
+ ...Object.keys(npmConfig.optionalDependencies ?? {}),
408
+ ...Object.keys(npmConfig.peerDependencies ?? {}).filter((item) => npmConfig.peerDependenciesMeta?.[item].optional)
409
+ ].distinct();
260
410
 
261
- for (const moduleName of SdCliNpmConfigUtil.getAllDependencies(npmConfig)) {
411
+ for (const moduleName of moduleNames) {
262
412
  if (loadedModuleNames.includes(moduleName)) continue;
263
413
  loadedModuleNames.push(moduleName);
264
414
 
265
415
  const modulePath = FsUtil.findAllParentChildDirPaths("node_modules/" + moduleName, currPath, this._workspaceRootPath).first();
266
- if (StringUtil.isNullOrEmpty(modulePath)) continue;
416
+ if (StringUtil.isNullOrEmpty(modulePath)) {
417
+ continue;
418
+ }
267
419
 
268
- if (all || FsUtil.exists(path.resolve(modulePath, "binding.gyp"))) {
269
- externalModules.push({ path: modulePath, name: moduleName });
420
+ if (FsUtil.exists(path.resolve(modulePath, "binding.gyp"))) {
421
+ resultSet.add(moduleName);
270
422
  }
271
423
 
272
424
  fn(modulePath);
273
425
  }
426
+
427
+ for (const optModuleName of optModuleNames) {
428
+ if (loadedModuleNames.includes(optModuleName)) continue;
429
+ loadedModuleNames.push(optModuleName);
430
+
431
+ const optModulePath = FsUtil.findAllParentChildDirPaths("node_modules/" + optModuleName, currPath, this._workspaceRootPath).first();
432
+ if (StringUtil.isNullOrEmpty(optModulePath)) {
433
+ resultSet.add(optModuleName);
434
+ continue;
435
+ }
436
+
437
+ if (FsUtil.exists(path.resolve(optModulePath, "binding.gyp"))) {
438
+ resultSet.add(optModuleName);
439
+ }
440
+
441
+ fn(optModulePath);
442
+ }
274
443
  };
275
444
 
276
445
  fn(this._rootPath);
277
446
 
278
- return externalModules.distinct();
447
+ return Array.from(resultSet.values());
279
448
  }
280
449
 
281
450
  private _getNpmConfig(pkgPath: string): INpmConfig | undefined {
@@ -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,18 +58,15 @@ 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);
55
- watcher.onChange(async (changeInfos) => {
69
+ watcher.onChange({}, async (changeInfos) => {
56
70
  const changeFilePaths = changeInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
57
71
  if (changeFilePaths.length === 0) return;
58
72
 
@@ -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);
@@ -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,7 +40,7 @@ export interface ISdCliConfig {
39
40
  localUpdates?: Record<string, string>;
40
41
  }
41
42
 
42
- export type TSdCliPackageConfig = ISdCliLibPackageConfig | ISdCliServerPackageConfig;
43
+ export type TSdCliPackageConfig = ISdCliLibPackageConfig | ISdCliServerPackageConfig | ISdCliClientPackageConfig;
43
44
 
44
45
  export interface ISdCliLibPackageConfig {
45
46
  type: "library";
@@ -49,4 +50,12 @@ export interface ISdCliLibPackageConfig {
49
50
  export interface ISdCliServerPackageConfig {
50
51
  type: "server";
51
52
  env?: Record<string, string>;
53
+ configs?: Record<string, any>;
54
+ pm2?: Record<string, any> | boolean;
55
+ iis?: { serverExeFilePath?: string } | boolean;
56
+ }
57
+
58
+ export interface ISdCliClientPackageConfig {
59
+ type: "client";
60
+ server: string;
52
61
  }
@@ -40,19 +40,25 @@ export class SdCliLocalUpdate {
40
40
  const watchPaths = (await updatePathInfos.mapManyAsync(async (item) => await this._getWatchPathsAsync(item.source))).distinct();
41
41
 
42
42
  const watcher = SdFsWatcher.watch(watchPaths);
43
- watcher.onChange(async (changeInfos) => {
43
+ watcher.onChange({ delay: 1000 }, async (changeInfos) => {
44
44
  const changeFilePaths = changeInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
45
45
  if (changeFilePaths.length === 0) return;
46
46
 
47
47
  this._logger.log("로컬 라이브러리 변경감지...");
48
48
  for (const changedFilePath of changeFilePaths) {
49
- const currentUpdatePathInfos = updatePathInfos.filter((item) => PathUtil.isChildPath(changedFilePath, item.source));
50
- const targetFilePaths = currentUpdatePathInfos.map((item) => path.resolve(item.target, path.relative(item.source, changedFilePath)));
51
- if (FsUtil.exists(changedFilePath)) {
52
- this._logger.debug(`변경파일감지(복사): ${changedFilePath}`);
53
- for (const targetFilePath of targetFilePaths) {
54
- await FsUtil.copyAsync(changedFilePath, targetFilePath);
55
- }
49
+ if (!FsUtil.exists(changedFilePath)) continue;
50
+
51
+ for (const updatePathInfo of updatePathInfos) {
52
+ if (!PathUtil.isChildPath(changedFilePath, updatePathInfo.source)) continue;
53
+
54
+ const sourceRelPath = path.relative(updatePathInfo.source, changedFilePath);
55
+ if (sourceRelPath.includes("node_modules")) continue;
56
+ if (sourceRelPath.includes("package.json")) continue;
57
+
58
+ const targetFilePath = path.resolve(updatePathInfo.target, sourceRelPath);
59
+
60
+ this._logger.debug(`변경파일감지(복사): ${changedFilePath} => ${targetFilePath}`);
61
+ await FsUtil.copyAsync(changedFilePath, targetFilePath);
56
62
  }
57
63
  }
58
64
 
@@ -8,11 +8,15 @@ export class SdCliNpm {
8
8
  }
9
9
 
10
10
  public async updateAsync(): Promise<void> {
11
- this._logger.debug("업데이트할 패키지 확인...");
12
- await SdProcess.execAsync("npm outdated", { cwd: this._rootPath });
11
+ try {
12
+ this._logger.debug("업데이트할 패키지 확인...");
13
+ await SdProcess.spawnAsync("npm outdated", { cwd: this._rootPath });
14
+ }
15
+ catch (err) {
16
+ }
13
17
 
14
18
  this._logger.debug("업데이트 시작...");
15
- await SdProcess.execAsync("npm update", { cwd: this._rootPath });
19
+ await SdProcess.spawnAsync("npm update", { cwd: this._rootPath });
16
20
 
17
21
  this._logger.debug("sd-cli 준비...");
18
22
  await new SdCliPrepare().prepareAsync();
@@ -1,4 +1,5 @@
1
1
  import { FsUtil, Logger } from "@simplysm/sd-core-node";
2
+ import { fileURLToPath } from "url";
2
3
 
3
4
  export class SdCliPrepare {
4
5
  private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
@@ -11,7 +12,7 @@ export class SdCliPrepare {
11
12
 
12
13
  private async _modifyTypescriptCodeForTypeCheckPerformanceWarning(): Promise<boolean> {
13
14
  const fileUrl = await import.meta.resolve!("typescript");
14
- const filePath = fileUrl.replace(/^file:\/\/\//, "");
15
+ const filePath = fileURLToPath(fileUrl);
15
16
  const fileContent = await FsUtil.readFileAsync(filePath);
16
17
  const modifiedFileContent = fileContent.replace(`
17
18
  function checkSourceElement(node) {