@simplysm/sd-cli 7.0.66 → 7.0.77

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 (53) hide show
  1. package/.eslintrc.cjs +3 -0
  2. package/dist/bin/sd-cli.mjs +62 -6
  3. package/dist/build-tool/SdCliCordova.d.ts +1 -1
  4. package/dist/build-tool/SdCliCordova.mjs +40 -27
  5. package/dist/build-tool/SdCliNgCacheCompilerHost.d.ts +1 -0
  6. package/dist/build-tool/SdCliNgCacheCompilerHost.mjs +11 -4
  7. package/dist/build-tool/SdCliPackageLinter.mjs +9 -5
  8. package/dist/builder/SdCliClientBuilder.d.ts +1 -0
  9. package/dist/builder/SdCliClientBuilder.mjs +45 -37
  10. package/dist/builder/SdCliJsLibBuilder.mjs +3 -3
  11. package/dist/builder/SdCliServerBuilder.d.ts +0 -1
  12. package/dist/builder/SdCliServerBuilder.mjs +39 -54
  13. package/dist/builder/SdCliTsLibBuilder.d.ts +1 -0
  14. package/dist/builder/SdCliTsLibBuilder.mjs +19 -7
  15. package/dist/commons.d.ts +18 -4
  16. package/dist/entry-points/SdCliFileCrypto.d.ts +7 -0
  17. package/dist/entry-points/SdCliFileCrypto.mjs +72 -0
  18. package/dist/entry-points/SdCliLocalUpdate.mjs +3 -3
  19. package/dist/entry-points/SdCliPrepare.mjs +3 -3
  20. package/dist/entry-points/SdCliWorkspace.d.ts +3 -0
  21. package/dist/entry-points/SdCliWorkspace.mjs +57 -37
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.mjs +2 -1
  24. package/dist/ng-tools/SdCliNgModuleGenerator.d.ts +2 -0
  25. package/dist/ng-tools/SdCliNgModuleGenerator.mjs +97 -18
  26. package/dist/ng-tools/babel/SdCliBbFileMetadata.d.ts +1 -1
  27. package/dist/ng-tools/babel/SdCliBbFileMetadata.mjs +4 -4
  28. package/dist/ng-tools/babel/TSdCliBbNgMetadata.mjs +6 -6
  29. package/dist/ng-tools/typescript/SdCliTsFileMetadata.mjs +2 -3
  30. package/dist/packages/SdCliPackage.mjs +52 -5
  31. package/dist/utils/SdCliBuildResultUtil.mjs +4 -1
  32. package/package.json +7 -9
  33. package/src/bin/sd-cli.ts +77 -6
  34. package/src/build-tool/SdCliCordova.ts +43 -26
  35. package/src/build-tool/SdCliNgCacheCompilerHost.ts +19 -11
  36. package/src/build-tool/SdCliPackageLinter.ts +8 -4
  37. package/src/builder/SdCliClientBuilder.ts +42 -32
  38. package/src/builder/SdCliJsLibBuilder.ts +2 -2
  39. package/src/builder/SdCliServerBuilder.ts +37 -51
  40. package/src/builder/SdCliTsLibBuilder.ts +22 -6
  41. package/src/commons.ts +21 -2
  42. package/src/entry-points/SdCliFileCrypto.ts +87 -0
  43. package/src/entry-points/SdCliLocalUpdate.ts +2 -2
  44. package/src/entry-points/SdCliPrepare.ts +2 -2
  45. package/src/entry-points/SdCliWorkspace.ts +59 -41
  46. package/src/index.ts +1 -0
  47. package/src/ng-tools/SdCliNgModuleGenerator.ts +119 -17
  48. package/src/ng-tools/babel/SdCliBbFileMetadata.ts +5 -5
  49. package/src/ng-tools/babel/TSdCliBbNgMetadata.ts +5 -5
  50. package/src/ng-tools/typescript/SdCliTsFileMetadata.ts +1 -2
  51. package/src/packages/SdCliPackage.ts +55 -4
  52. package/src/utils/SdCliBuildResultUtil.ts +4 -0
  53. package/tsconfig.json +3 -0
@@ -7,7 +7,6 @@ import ts from "typescript";
7
7
  import { SdCliBuildResultUtil } from "../utils/SdCliBuildResultUtil";
8
8
  import { NamedChunksPlugin } from "@angular-devkit/build-angular/src/webpack/plugins/named-chunks-plugin";
9
9
  import {
10
- AnyComponentStyleBudgetChecker,
11
10
  CommonJsUsageWarnPlugin,
12
11
  DedupeModuleResolvePlugin,
13
12
  JavaScriptOptimizerPlugin,
@@ -23,7 +22,6 @@ import wdm from "webpack-dev-middleware";
23
22
  import whm from "webpack-hot-middleware";
24
23
  import { NextHandleFunction } from "connect";
25
24
  import { LicenseWebpackPlugin } from "license-webpack-plugin";
26
- import { Type } from "@angular-devkit/build-angular";
27
25
  import NodePolyfillPlugin from "node-polyfill-webpack-plugin";
28
26
  import { createHash } from "crypto";
29
27
  import ESLintWebpackPlugin from "eslint-webpack-plugin";
@@ -36,6 +34,7 @@ import { augmentAppWithServiceWorker } from "@angular-devkit/build-angular/src/u
36
34
  import { StringUtil } from "@simplysm/sd-core-common";
37
35
  import { SdCliNgModuleGenerator } from "../ng-tools/SdCliNgModuleGenerator";
38
36
  import { SdCliCordova } from "../build-tool/SdCliCordova";
37
+ import { SdCliNpmConfigUtil } from "../utils/SdCliNpmConfigUtil";
39
38
  import LintResult = ESLint.LintResult;
40
39
 
41
40
  export class SdCliClientBuilder extends EventEmitter {
@@ -48,6 +47,8 @@ export class SdCliClientBuilder extends EventEmitter {
48
47
 
49
48
  private readonly _cordova?: SdCliCordova;
50
49
 
50
+ private readonly _hasAngularRoute: boolean;
51
+
51
52
  public constructor(private readonly _rootPath: string,
52
53
  private readonly _config: ISdCliClientPackageConfig,
53
54
  private readonly _workspaceRootPath: string) {
@@ -61,6 +62,9 @@ export class SdCliClientBuilder extends EventEmitter {
61
62
  const tsconfig = FsUtil.readJson(this._tsconfigFilePath) as ITsconfig;
62
63
  this._parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._rootPath, tsconfig.angularCompilerOptions);
63
64
 
65
+ // isAngular
66
+ this._hasAngularRoute = SdCliNpmConfigUtil.getDependencies(npmConfig).defaults.includes("@angular/router");
67
+
64
68
  // NgModule 생성기 초기화
65
69
  this._ngModuleGenerator = new SdCliNgModuleGenerator(this._rootPath, [
66
70
  "controls",
@@ -73,11 +77,11 @@ export class SdCliClientBuilder extends EventEmitter {
73
77
  "print-templates",
74
78
  "toasts",
75
79
  "AppPage"
76
- ], {
80
+ ], this._hasAngularRoute ? {
77
81
  glob: "**/*Page.ts",
78
82
  fileEndsWith: "Page",
79
83
  rootClassName: "AppPage"
80
- });
84
+ } : undefined);
81
85
 
82
86
  // CORDOVA
83
87
  if (this._config.cordova) {
@@ -244,7 +248,7 @@ export class SdCliClientBuilder extends EventEmitter {
244
248
  const ngVersion = this._getNpmConfig(FsUtil.findAllParentChildDirPaths("node_modules/@angular/core", this._rootPath, this._workspaceRootPath)[0])!.version;
245
249
 
246
250
  const pkgKey = npmConfig.name.split("/").last()!;
247
- const publicPath = `/${pkgKey}/`;
251
+ const publicPath = (this._cordova && !watch) ? `/` : `/${pkgKey}/`;
248
252
 
249
253
  const cacheBasePath = path.resolve(this._rootPath, ".cache");
250
254
  const cachePath = path.resolve(cacheBasePath, pkgVersion);
@@ -257,6 +261,7 @@ export class SdCliClientBuilder extends EventEmitter {
257
261
  const polyfillsFilePath = path.resolve(this._rootPath, "src/polyfills.ts");
258
262
  const stylesFilePath = path.resolve(this._rootPath, "src/styles.scss");
259
263
 
264
+ let prevProgressMessage = "";
260
265
  return {
261
266
  mode: watch ? "development" : "production",
262
267
  devtool: false,
@@ -323,6 +328,7 @@ export class SdCliClientBuilder extends EventEmitter {
323
328
  .update(watch.toString())
324
329
  .digest("hex")
325
330
  },
331
+ // cache: { type: "memory", maxGenerations: 1 },
326
332
  ...watch ? {
327
333
  snapshot: {
328
334
  immutablePaths: internalModuleCachePaths,
@@ -395,6 +401,27 @@ export class SdCliClientBuilder extends EventEmitter {
395
401
  test: /[/\\]rxjs[/\\]add[/\\].+\.js$/,
396
402
  sideEffects: true
397
403
  },
404
+ ...watch ? [
405
+ {
406
+ loader: HmrLoader,
407
+ include: [mainFilePath]
408
+ }
409
+ ] : [],
410
+ ...watch ? [
411
+ {
412
+ test: /\.[cm]?jsx?$/,
413
+ enforce: "pre" as const,
414
+ loader: "source-map-loader",
415
+ options: {
416
+ filterSourceMappingUrl: (mapUri: string, resourcePath: string) => {
417
+ const workspaceRegex = new RegExp(`node_modules[\\\\/]@${workspaceName}[\\\\/]`);
418
+ return !resourcePath.includes("node_modules")
419
+ || (/node_modules[\\/]@simplysm[\\/]/).test(resourcePath)
420
+ || workspaceRegex.test(resourcePath);
421
+ }
422
+ }
423
+ }
424
+ ] : [],
398
425
  {
399
426
  test: /\.[cm]?[tj]sx?$/,
400
427
  resolve: { fullySpecified: false },
@@ -412,21 +439,6 @@ export class SdCliClientBuilder extends EventEmitter {
412
439
  }
413
440
  ]
414
441
  },
415
- ...watch ? [
416
- {
417
- test: /\.[cm]?jsx?$/,
418
- enforce: "pre" as const,
419
- loader: "source-map-loader",
420
- options: {
421
- filterSourceMappingUrl: (mapUri: string, resourcePath: string) => {
422
- const workspaceRegex = new RegExp(`node_modules[\\\\/]@${workspaceName}[\\\\/]`);
423
- return !resourcePath.includes("node_modules")
424
- || (/node_modules[\\/]@simplysm[\\/]/).test(resourcePath)
425
- || workspaceRegex.test(resourcePath);
426
- }
427
- }
428
- }
429
- ] : [],
430
442
  {
431
443
  test: /\.[cm]?tsx?$/,
432
444
  loader: "@ngtools/webpack",
@@ -485,12 +497,6 @@ export class SdCliClientBuilder extends EventEmitter {
485
497
  }
486
498
  ]
487
499
  },
488
- ...watch ? [
489
- {
490
- loader: HmrLoader,
491
- include: [mainFilePath]
492
- }
493
- ] : [],
494
500
  {
495
501
  test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf|xlsx?|pptx?|docx?|zip|pfx|pkl)$/,
496
502
  type: "asset/resource"
@@ -501,13 +507,17 @@ export class SdCliClientBuilder extends EventEmitter {
501
507
  new NodePolyfillPlugin(),
502
508
  new NamedChunksPlugin(),
503
509
  new DedupeModuleResolvePlugin(),
504
- /*new webpack.ProgressPlugin({
510
+ new webpack.ProgressPlugin({
505
511
  handler: (per: number, msg: string, ...args: string[]) => {
506
512
  const phaseText = msg ? ` - phase: ${msg}` : "";
507
513
  const argsText = args.length > 0 ? ` - args: [${args.join(", ")}]` : "";
508
- this._logger.debug(`Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`);
514
+ const progressMessage = `Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`;
515
+ if (progressMessage !== prevProgressMessage) {
516
+ prevProgressMessage = progressMessage;
517
+ this._logger.debug(progressMessage);
518
+ }
509
519
  }
510
- }),*/
520
+ }),
511
521
  new CommonJsUsageWarnPlugin(),
512
522
  ...watch ? [] : [
513
523
  new LicenseWebpackPlugin({
@@ -564,9 +574,9 @@ export class SdCliClientBuilder extends EventEmitter {
564
574
  emitNgModuleScope: watch,
565
575
  inlineStyleFileExtension: "scss"
566
576
  }),
567
- new AnyComponentStyleBudgetChecker(watch ? [] : [
568
- { type: Type.AnyComponentStyle, maximumWarning: "2kb", maximumError: "4kb" }
569
- ]),
577
+ // new AnyComponentStyleBudgetChecker(watch ? [] : [
578
+ // { type: Type.AnyComponentStyle, maximumWarning: "2kb", maximumError: "4kb" }
579
+ // ]),
570
580
  {
571
581
  apply: (compiler: webpack.Compiler) => {
572
582
  compiler.hooks.shutdown.tap("sass-worker", () => {
@@ -29,7 +29,7 @@ export class SdCliJsLibBuilder extends EventEmitter {
29
29
  const changeFilePaths = changeInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
30
30
  if (changeFilePaths.length === 0) return;
31
31
 
32
- this._logger.debug("파일 변경 감지", changeInfos);
32
+ this._logger.debug("파일 변경 감지");
33
33
  this.emit("change");
34
34
  const watchBuildResults: ISdCliPackageBuildResult[] = [];
35
35
 
@@ -56,7 +56,7 @@ export class SdCliJsLibBuilder extends EventEmitter {
56
56
  }
57
57
 
58
58
  private async getRelatedPathsAsync(): Promise<string[]> {
59
- const mySourceGlobPath = path.resolve(this._rootPath, "**", "+(*.js|*.cjs|*.mjs|*.ts)");
59
+ const mySourceGlobPath = path.resolve(this._rootPath, "**", "+(*.js|*.cjs|*.mjs)");
60
60
  const mySourceFilePaths = await FsUtil.globAsync(mySourceGlobPath, {
61
61
  ignore: [
62
62
  "**/node_modules/**",
@@ -13,8 +13,8 @@ import { ObjectUtil, StringUtil } from "@simplysm/sd-core-common";
13
13
  import ESLintWebpackPlugin from "eslint-webpack-plugin";
14
14
  import CopyWebpackPlugin from "copy-webpack-plugin";
15
15
  import { LicenseWebpackPlugin } from "license-webpack-plugin";
16
- import { createHash } from "crypto";
17
16
  import { SdCliNpmConfigUtil } from "../utils/SdCliNpmConfigUtil";
17
+ import { createHash } from "crypto";
18
18
  import LintResult = ESLint.LintResult;
19
19
 
20
20
  export class SdCliServerBuilder extends EventEmitter {
@@ -47,7 +47,7 @@ export class SdCliServerBuilder extends EventEmitter {
47
47
 
48
48
  // 빌드 준비
49
49
  const extModules = this._getExternalModules();
50
- const webpackConfig = this._getWebpackConfig(true, extModules.map((item) => item.name));
50
+ const webpackConfig = this._getWebpackConfig(true, extModules);
51
51
  const compiler = webpack(webpackConfig);
52
52
  await new Promise<void>((resolve, reject) => {
53
53
  compiler.hooks.watchRun.tapAsync(this.constructor.name, (args, callback) => {
@@ -81,7 +81,7 @@ export class SdCliServerBuilder extends EventEmitter {
81
81
  // 빌드
82
82
  this._logger.debug("Webpack 빌드 수행...");
83
83
  const extModules = this._getExternalModules();
84
- const webpackConfig = this._getWebpackConfig(false, extModules.map((item) => item.name));
84
+ const webpackConfig = this._getWebpackConfig(false, extModules);
85
85
  const compiler = webpack(webpackConfig);
86
86
  const buildResults = await new Promise<ISdCliPackageBuildResult[]>((resolve, reject) => {
87
87
  compiler.run((err, stats) => {
@@ -102,9 +102,6 @@ export class SdCliServerBuilder extends EventEmitter {
102
102
  // pm2.json 파일 쓰기
103
103
  await this._writeDistPm2ConfigFileAsync();
104
104
 
105
- // iis 파일 쓰기
106
- await this._writeDistIisConfigFileAsync();
107
-
108
105
  // 배포용 package.json 파일 생성
109
106
  await this._writeDistNpmConfigFileAsync(extModules.filter((item) => item.exists).map((item) => item.name));
110
107
 
@@ -130,6 +127,7 @@ export class SdCliServerBuilder extends EventEmitter {
130
127
  {
131
128
  "name": npmConfig.name.replace(/@/g, "").replace(/\//g, "-"),
132
129
  "script": path.basename(path.resolve(this._parsedTsconfig.options.outDir!, "main.mjs")),
130
+ "node_args": "--experimental-specifier-resolution=node --experimental-import-meta-resolve",
133
131
  "watch": true,
134
132
  "watch_delay": 2000,
135
133
  "ignore_watch": [
@@ -154,36 +152,6 @@ export class SdCliServerBuilder extends EventEmitter {
154
152
  );
155
153
  }
156
154
 
157
- private async _writeDistIisConfigFileAsync(): Promise<void> {
158
- if (this._config.iis === undefined || this._config.iis === false) return;
159
-
160
- const iisDistPath = path.resolve(this._parsedTsconfig.options.outDir!, "web.config");
161
- const serverExeFilePath = (this._config.iis !== true && "serverExeFilePath" in this._config.iis)
162
- ? (this._config.iis.serverExeFilePath ?? "C:\\Program Files\\nodejs\\node.exe")
163
- : "C:\\Program Files\\nodejs\\node.exe";
164
- await FsUtil.writeFileAsync(iisDistPath, `
165
- <configuration>
166
- <system.webServer>
167
- <webSocket enabled="false" />
168
- <handlers>
169
- <add name="iisnode" path="main.js" verb="*" modules="iisnode" />
170
- </handlers>
171
- <iisnode nodeProcessCommandLine="${serverExeFilePath}"
172
- watchedFiles="web.config;*.js"
173
- loggingEnabled="true"
174
- devErrorsEnabled="true" />
175
- <rewrite>
176
- <rules>
177
- <rule name="main">
178
- <action type="Rewrite" url="main.mjs" />
179
- </rule>
180
- </rules>
181
- </rewrite>
182
- <httpErrors errorMode="Detailed" />
183
- </system.webServer>
184
- </configuration>`.trim());
185
- }
186
-
187
155
  private async _writeDistNpmConfigFileAsync(deps: string[]): Promise<void> {
188
156
  const distNpmConfig = ObjectUtil.clone(this._getNpmConfig(this._rootPath))!;
189
157
  distNpmConfig.dependencies = {};
@@ -211,7 +179,7 @@ export class SdCliServerBuilder extends EventEmitter {
211
179
  ].map((p) => path.dirname(p));
212
180
  }
213
181
 
214
- private _getWebpackConfig(watch: boolean, extModuleNames: string[]): webpack.Configuration {
182
+ private _getWebpackConfig(watch: boolean, extModules: { name: string; exists: boolean }[]): webpack.Configuration {
215
183
  const workspaceNpmConfig = this._getNpmConfig(this._workspaceRootPath)!;
216
184
  const workspaceName = workspaceNpmConfig.name;
217
185
 
@@ -224,18 +192,20 @@ export class SdCliServerBuilder extends EventEmitter {
224
192
  const cacheBasePath = path.resolve(this._rootPath, ".cache");
225
193
  const cachePath = path.resolve(cacheBasePath, pkgVersion);
226
194
 
195
+
196
+ let prevProgressMessage = "";
227
197
  return {
228
198
  mode: watch ? "development" : "production",
229
199
  devtool: false,
230
- target: ["node", "es2015"],
200
+ target: ["node", "es2020"],
231
201
  profile: false,
232
202
  resolve: {
233
203
  roots: [this._rootPath],
234
204
  extensions: [".ts", ".js", ".mjs", ".cjs"],
235
205
  symlinks: true,
236
206
  modules: [this._workspaceRootPath, "node_modules"],
237
- mainFields: ["es2015", "default", "module", "main"],
238
- conditionNames: ["es2015", "..."]
207
+ mainFields: ["es2020", "default", "module", "main"],
208
+ conditionNames: ["es2020", "..."]
239
209
  },
240
210
  resolveLoader: {
241
211
  symlinks: true
@@ -251,17 +221,23 @@ export class SdCliServerBuilder extends EventEmitter {
251
221
  hashFunction: "xxhash64",
252
222
  clean: true,
253
223
  path: this._parsedTsconfig.options.outDir,
254
- filename: "[name].cjs",
255
- chunkFilename: "[name].cjs",
224
+ filename: "[name].mjs",
225
+ chunkFilename: "[name].mjs",
256
226
  assetModuleFilename: "res/[name][ext][query]",
257
- libraryTarget: "commonjs2"
227
+ library: {
228
+ type: "module"
229
+ },
230
+ module: true
231
+ },
232
+ experiments: {
233
+ outputModule: true
258
234
  },
259
235
  watch: false,
260
236
  watchOptions: { poll: undefined, ignored: undefined },
261
237
  performance: { hints: false },
262
238
  infrastructureLogging: { level: "error" },
263
239
  stats: "errors-warnings",
264
- externals: extModuleNames.toObject((item) => item, (item) => "commonjs2 " + item),
240
+ externals: extModules.toObject((item) => item.name, (item) => "node-commonjs " + item.name),
265
241
  cache: {
266
242
  type: "filesystem",
267
243
  profile: watch ? undefined : false,
@@ -276,6 +252,7 @@ export class SdCliServerBuilder extends EventEmitter {
276
252
  .update(watch.toString())
277
253
  .digest("hex")
278
254
  },
255
+ // cache: { type: "memory", maxGenerations: 1 },
279
256
  ...watch ? {
280
257
  snapshot: {
281
258
  immutablePaths: internalModuleCachePaths,
@@ -289,7 +266,7 @@ export class SdCliServerBuilder extends EventEmitter {
289
266
  extractComments: false,
290
267
  terserOptions: {
291
268
  compress: true,
292
- ecma: 2015,
269
+ ecma: 2020,
293
270
  sourceMap: false,
294
271
  keep_classnames: true,
295
272
  keep_fnames: true,
@@ -318,9 +295,6 @@ export class SdCliServerBuilder extends EventEmitter {
318
295
  test: /\.[cm]?[tj]sx?$/,
319
296
  resolve: {
320
297
  fullySpecified: false
321
- },
322
- use: {
323
- loader: "babel-loader"
324
298
  }
325
299
  },
326
300
  ...watch ? [
@@ -420,14 +394,18 @@ export class SdCliServerBuilder extends EventEmitter {
420
394
  }
421
395
  return resultMessages.join(os.EOL);
422
396
  }
423
- })/*,
397
+ }),
424
398
  new webpack.ProgressPlugin({
425
399
  handler: (per: number, msg: string, ...args: string[]) => {
426
400
  const phaseText = msg ? ` - phase: ${msg}` : "";
427
401
  const argsText = args.length > 0 ? ` - args: [${args.join(", ")}]` : "";
428
- this._logger.debug(`Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`);
402
+ const progressMessage = `Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`;
403
+ if (progressMessage !== prevProgressMessage) {
404
+ prevProgressMessage = progressMessage;
405
+ this._logger.debug(progressMessage);
406
+ }
429
407
  }
430
- })*/
408
+ })
431
409
  ]
432
410
  };
433
411
  }
@@ -455,6 +433,10 @@ export class SdCliServerBuilder extends EventEmitter {
455
433
  results.push({ name: moduleName, exists: true });
456
434
  }
457
435
 
436
+ if (this._config.externalNodeModules?.includes(moduleName)) {
437
+ results.push({ name: moduleName, exists: true });
438
+ }
439
+
458
440
  fn(modulePath);
459
441
  }
460
442
 
@@ -472,6 +454,10 @@ export class SdCliServerBuilder extends EventEmitter {
472
454
  results.push({ name: optModuleName, exists: true });
473
455
  }
474
456
 
457
+ if (this._config.externalNodeModules?.includes(optModuleName)) {
458
+ results.push({ name: optModuleName, exists: true });
459
+ }
460
+
475
461
  fn(optModulePath);
476
462
  }
477
463
  };
@@ -37,6 +37,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
37
37
  private readonly _npmConfigMap = new Map<string, INpmConfig>();
38
38
 
39
39
  private readonly _isAngular: boolean;
40
+ private readonly _hasAngularRoute: boolean;
40
41
 
41
42
  public constructor(private readonly _rootPath: string,
42
43
  private readonly _config: ISdCliLibPackageConfig,
@@ -52,6 +53,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
52
53
 
53
54
  // isAngular
54
55
  this._isAngular = SdCliNpmConfigUtil.getDependencies(npmConfig).defaults.includes("@angular/core");
56
+ this._hasAngularRoute = SdCliNpmConfigUtil.getDependencies(npmConfig).defaults.includes("@angular/router");
55
57
 
56
58
  // tsconfig
57
59
  this._tsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
@@ -71,11 +73,11 @@ export class SdCliTsLibBuilder extends EventEmitter {
71
73
  "print-templates",
72
74
  "toasts",
73
75
  "AppPage"
74
- ], {
76
+ ], this._hasAngularRoute ? {
75
77
  glob: "**/*Page.ts",
76
78
  fileEndsWith: "Page",
77
79
  rootClassName: "AppPage"
78
- });
80
+ } : undefined);
79
81
  }
80
82
 
81
83
  // index 생성기 초기화
@@ -117,12 +119,21 @@ export class SdCliTsLibBuilder extends EventEmitter {
117
119
  const changeFilePaths = changeInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
118
120
  if (changeFilePaths.length === 0) return;
119
121
 
120
- this._logger.debug("파일 변경 감지", changeInfos);
122
+ this._logger.debug("파일 변경 감지", changeFilePaths);
121
123
  this.emit("change");
122
124
 
123
125
  this._logger.debug("변경된 파일의 캐쉬 삭제...");
124
126
  for (const changeFilePath of changeFilePaths) {
125
- this._fileCache.delete(PathUtil.posix(changeFilePath));
127
+ const fileCache = this._fileCache.get(PathUtil.posix(changeFilePath));
128
+ if (fileCache) {
129
+ if (fileCache.importerSet) {
130
+ for (const importer of fileCache.importerSet.values()) {
131
+ this._fileCache.delete(importer);
132
+ }
133
+ }
134
+
135
+ this._fileCache.delete(PathUtil.posix(changeFilePath));
136
+ }
126
137
  }
127
138
 
128
139
  if (this._ngModuleGenerator) {
@@ -161,6 +172,10 @@ export class SdCliTsLibBuilder extends EventEmitter {
161
172
  this._logger.debug("린트...");
162
173
  buildResults.push(...await this._linter.lintAsync(relatedPaths, buildPack.program));
163
174
 
175
+ this._logger.debug("변경감지 대상목록 재구성...");
176
+ const watchRelatedPaths = await this.getAllRelatedPathsAsync();
177
+ watcher.add(watchRelatedPaths);
178
+
164
179
  this.emit("complete", buildResults);
165
180
  }
166
181
 
@@ -202,7 +217,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
202
217
  || (/node_modules[\\/]@simplysm[\\/]/).test(filePath)
203
218
  || workspaceRegex.test(filePath);
204
219
  });
205
- const mySourceGlobPath = path.resolve(this._rootPath, "**", "+(*.js|*.cjs|*.mjs|*.ts)");
220
+ const mySourceGlobPath = path.resolve(this._rootPath, "**", "+(*.js|*.cjs|*.mjs|*.ts|*.scss)");
206
221
  const mySourceFilePaths = await FsUtil.globAsync(mySourceGlobPath, {
207
222
  ignore: [
208
223
  "**/node_modules/**",
@@ -296,7 +311,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
296
311
  char: undefined,
297
312
  code: undefined,
298
313
  severity: "error",
299
- message: err.message
314
+ message: err.stack
300
315
  }];
301
316
  }
302
317
  }
@@ -419,6 +434,7 @@ interface IFileCache {
419
434
  sourceFile?: ts.SourceFile;
420
435
  content?: string;
421
436
  styleContent?: string;
437
+ importerSet?: Set<string>;
422
438
  }
423
439
 
424
440
  interface ISdBuildPack {
package/src/commons.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export interface INpmConfig {
2
2
  name: string;
3
3
  version: string;
4
+ type?: "module";
4
5
  workspaces?: string[];
5
6
  main?: string;
6
7
  types?: string;
@@ -56,7 +57,8 @@ export interface ISdCliServerPackageConfig {
56
57
  env?: Record<string, string>;
57
58
  configs?: Record<string, any>;
58
59
  pm2?: Record<string, any> | boolean;
59
- iis?: { serverExeFilePath?: string } | boolean;
60
+ externalNodeModules?: string[];
61
+ publish?: TSdCliPublishConfig;
60
62
  }
61
63
 
62
64
  export interface ISdCliClientPackageConfig {
@@ -65,6 +67,23 @@ export interface ISdCliClientPackageConfig {
65
67
  server: string;
66
68
  env?: Record<string, string>;
67
69
  configs?: Record<string, any>;
70
+ publish?: TSdCliPublishConfig;
71
+ }
72
+
73
+ export type TSdCliPublishConfig = ISdCliFtpPublishConfig | ISdCliLocalDirectoryPublishConfig;
74
+
75
+ export interface ISdCliFtpPublishConfig {
76
+ type: "ftp" | "ftps" | "sftp";
77
+ host: string;
78
+ port?: number;
79
+ path: string;
80
+ user: string;
81
+ pass: string;
82
+ }
83
+
84
+ export interface ISdCliLocalDirectoryPublishConfig {
85
+ type: "local-directory";
86
+ path: string;
68
87
  }
69
88
 
70
89
  export interface ISdCliClientPackageCordovaConfig {
@@ -88,4 +107,4 @@ export interface ISdCliClientPackageCordovaConfig {
88
107
  };
89
108
  }
90
109
 
91
- export type TSdCliCordovaPlatform = "browser" | "ios" | "android" | "windows";
110
+ export type TSdCliCordovaPlatform = "browser" | "ios" | "android" | "electron";
@@ -0,0 +1,87 @@
1
+ import readline from "readline";
2
+ import { Writable } from "stream";
3
+ import crypto from "crypto";
4
+ import { FsUtil } from "@simplysm/sd-core-node";
5
+ import os from "os";
6
+
7
+ export class SdCliFileCrypto {
8
+ public async encryptAsync(filePath: string): Promise<void> {
9
+ if (!FsUtil.exists(filePath)) {
10
+ throw new Error(`파일 '${filePath}'을 찾을 수 없습니다.`);
11
+ }
12
+
13
+ const key = await this._readKeyAsync("password: ");
14
+ if (!key) {
15
+ throw new Error("암호화키를 반드시 입력해야 합니다.");
16
+ }
17
+
18
+ const confirmKey = await this._readKeyAsync("confirm password: ");
19
+ if (key !== confirmKey) {
20
+ throw new Error("암호화키가 서로 다릅니다.");
21
+ }
22
+
23
+ this._encryptFile(filePath, key, filePath + ".enc");
24
+ }
25
+
26
+ public async decryptAsync(encFilePath: string): Promise<void> {
27
+ if (!FsUtil.exists(encFilePath)) {
28
+ throw new Error(`파일 '${encFilePath}'을 찾을 수 없습니다.`);
29
+ }
30
+
31
+ if (!encFilePath.endsWith(".enc")) {
32
+ throw new Error(`파일 ${encFilePath}의 확장자가 '.enc'가 아닙니다.`);
33
+ }
34
+
35
+ const resultFilePath = encFilePath.slice(0, -4);
36
+ if (FsUtil.exists(resultFilePath)) {
37
+ process.stdout.write(`복호화 시, 현재 존재하는 '${resultFilePath}'파일을 덮어씁니다.${os.EOL}`, "utf-8");
38
+ }
39
+
40
+ const key = await this._readKeyAsync("password: ");
41
+ if (!key) {
42
+ throw new Error("암호화키를 반드시 입력해야 합니다.");
43
+ }
44
+
45
+ this._decryptFile(encFilePath, key, resultFilePath);
46
+ }
47
+
48
+ private _encryptFile(filePath: string, key: string, encFilePath: string): void {
49
+ const iv = Buffer.alloc(16, 0);
50
+ const cipheriv = crypto.createCipheriv("aes-192-cbc", crypto.scryptSync(key, "salt", 24), iv);
51
+
52
+ const input = FsUtil.createReadStream(filePath);
53
+ const output = FsUtil.createWriteStream(encFilePath);
54
+ input.pipe(cipheriv).pipe(output);
55
+ }
56
+
57
+ private _decryptFile(encFilePath: string, key: string, filePath: string): void {
58
+ const iv = Buffer.alloc(16, 0);
59
+ const cipheriv = crypto.createDecipheriv("aes-192-cbc", crypto.scryptSync(key, "salt", 24), iv);
60
+
61
+ const input = FsUtil.createReadStream(encFilePath);
62
+ const output = FsUtil.createWriteStream(filePath);
63
+ input.pipe(cipheriv).pipe(output);
64
+ }
65
+
66
+ private async _readKeyAsync(message: string): Promise<string> {
67
+ process.stdout.write(message, "utf-8");
68
+
69
+ return await new Promise((resolve) => {
70
+ const rl = readline.createInterface({
71
+ input: process.stdin,
72
+ output: new Writable({
73
+ write: (chunk, encoding, callback) => {
74
+ callback();
75
+ }
76
+ }),
77
+ terminal: true
78
+ });
79
+
80
+ rl.question(message, (answer) => {
81
+ process.stdout.write(os.EOL, "utf-8");
82
+ resolve(answer);
83
+ rl.close();
84
+ });
85
+ });
86
+ }
87
+ }
@@ -14,7 +14,7 @@ export class SdCliLocalUpdate {
14
14
  if (!conf.localUpdates) return;
15
15
 
16
16
  const updatePathInfos = await this._getUpdatePathInfosAsync(conf.localUpdates);
17
- this._logger.debug("로컬 업데이트 구성", updatePathInfos);
17
+ this._logger.debug("로컬 업데이트 구성");
18
18
 
19
19
  this._logger.log("로컬 라이브러리 업데이트 시작...");
20
20
  for (const updatePathInfo of updatePathInfos) {
@@ -37,7 +37,7 @@ export class SdCliLocalUpdate {
37
37
  if (!conf.localUpdates) return;
38
38
 
39
39
  const updatePathInfos = await this._getUpdatePathInfosAsync(conf.localUpdates);
40
- this._logger.debug("로컬 업데이트 구성", updatePathInfos);
40
+ this._logger.debug("로컬 업데이트 구성");
41
41
 
42
42
  const watchPaths = (await updatePathInfos.mapManyAsync(async (item) => await this._getWatchPathsAsync(item.source))).distinct();
43
43
 
@@ -38,7 +38,7 @@ export class SdCliPrepare {
38
38
  if (node.hasChildPerformanceWarning) {
39
39
  node.parent.hasChildPerformanceWarning = true;
40
40
  }
41
- else if (usage.user + usage.system > 2000 * 1000 && node.kind !== 253) {
41
+ else if (usage.user + usage.system > 1000 * 1000 && node.kind !== 253) {
42
42
  error(node, {
43
43
  code: 9000,
44
44
  category: ts.DiagnosticCategory.Warning,
@@ -50,6 +50,6 @@ export class SdCliPrepare {
50
50
  }
51
51
  }`);
52
52
  await FsUtil.writeFileAsync(filePath, modifiedFileContent);
53
- return fileContent !== modifiedFileContent;
53
+ return modifiedFileContent.includes("const prevUsage = process.cpuUsage();");
54
54
  }
55
55
  }