@simplysm/sd-cli 6.0.26 → 6.0.30

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 (78) hide show
  1. package/dist-node/bin/sd-cli.js +19 -4
  2. package/dist-node/bin/sd-cli.js.map +1 -1
  3. package/dist-node/build-tools/SdCliNgModuleFilesGenerator.js +102 -49
  4. package/dist-node/build-tools/SdCliNgModuleFilesGenerator.js.map +1 -1
  5. package/dist-node/build-tools/metadata/SdAotMetadataReader.js +6 -2
  6. package/dist-node/build-tools/metadata/SdAotMetadataReader.js.map +1 -1
  7. package/dist-node/build-tools/metadata/SdIvyMetadataReader.js +22 -5
  8. package/dist-node/build-tools/metadata/SdIvyMetadataReader.js.map +1 -1
  9. package/dist-node/build-tools/metadata/SdMetadataError.js +13 -0
  10. package/dist-node/build-tools/metadata/SdMetadataError.js.map +1 -0
  11. package/dist-node/build-tools/metadata/SdMyMetadataReader.js +22 -5
  12. package/dist-node/build-tools/metadata/SdMyMetadataReader.js.map +1 -1
  13. package/dist-node/builders/SdCliNgClientBuilder.js +73 -66
  14. package/dist-node/builders/SdCliNgClientBuilder.js.map +1 -1
  15. package/dist-node/builders/SdCliNgLibraryBuilder.js +52 -24
  16. package/dist-node/builders/SdCliNgLibraryBuilder.js.map +1 -1
  17. package/dist-node/builders/SdCliServerBuilder.js +45 -44
  18. package/dist-node/builders/SdCliServerBuilder.js.map +1 -1
  19. package/dist-node/builders/SdCliTypescriptBuilder.js +64 -59
  20. package/dist-node/builders/SdCliTypescriptBuilder.js.map +1 -1
  21. package/dist-node/entry-points/SdCliLocalUpdater.js +30 -29
  22. package/dist-node/entry-points/SdCliLocalUpdater.js.map +1 -1
  23. package/dist-node/entry-points/SdCliProject.js +10 -16
  24. package/dist-node/entry-points/SdCliProject.js.map +1 -1
  25. package/dist-node/packages/SdCliClientPackage.js +4 -3
  26. package/dist-node/packages/SdCliClientPackage.js.map +1 -1
  27. package/dist-node/packages/SdCliLibraryPackage.js +3 -2
  28. package/dist-node/packages/SdCliLibraryPackage.js.map +1 -1
  29. package/dist-node/packages/SdCliServerPackage.js +4 -3
  30. package/dist-node/packages/SdCliServerPackage.js.map +1 -1
  31. package/dist-node/utils/SdProjectConfigUtil.js +1 -1
  32. package/dist-node/utils/SdProjectConfigUtil.js.map +1 -1
  33. package/dist-node/utils/SdWebpackUtil.js +33 -13
  34. package/dist-node/utils/SdWebpackUtil.js.map +1 -1
  35. package/dist-node/workers/client-ng-gen-worker.js +54 -0
  36. package/dist-node/workers/client-ng-gen-worker.js.map +1 -0
  37. package/dist-node/workers/lib-build-worker.js +7 -6
  38. package/dist-node/workers/lib-build-worker.js.map +1 -1
  39. package/dist-types/build-tools/metadata/SdIvyMetadataReader.d.ts +1 -0
  40. package/dist-types/build-tools/metadata/SdMetadataError.d.ts +6 -0
  41. package/dist-types/build-tools/metadata/SdMyMetadataReader.d.ts +1 -0
  42. package/dist-types/builders/SdCliNgClientBuilder.d.ts +6 -2
  43. package/dist-types/builders/SdCliNgLibraryBuilder.d.ts +3 -1
  44. package/dist-types/builders/SdCliServerBuilder.d.ts +2 -1
  45. package/dist-types/builders/SdCliTypescriptBuilder.d.ts +5 -5
  46. package/dist-types/commons.d.ts +3 -1
  47. package/dist-types/entry-points/SdCliLocalUpdater.d.ts +1 -1
  48. package/dist-types/entry-points/SdCliProject.d.ts +2 -0
  49. package/dist-types/packages/SdCliClientPackage.d.ts +2 -1
  50. package/dist-types/packages/SdCliLibraryPackage.d.ts +2 -1
  51. package/dist-types/packages/SdCliServerPackage.d.ts +2 -1
  52. package/dist-types/utils/SdWebpackUtil.d.ts +1 -1
  53. package/dist-types/workers/client-ng-gen-worker.d.ts +1 -0
  54. package/package.json +7 -7
  55. package/schema/schema.json +185 -122
  56. package/sd-tsconfig.node.json +2 -1
  57. package/src/bin/sd-cli.ts +19 -4
  58. package/src/build-tools/SdCliNgModuleFilesGenerator.ts +118 -60
  59. package/src/build-tools/metadata/SdAotMetadataReader.ts +8 -2
  60. package/src/build-tools/metadata/SdIvyMetadataReader.ts +27 -4
  61. package/src/build-tools/metadata/SdMetadataError.ts +7 -0
  62. package/src/build-tools/metadata/SdMyMetadataReader.ts +25 -3
  63. package/src/builders/SdCliNgClientBuilder.ts +122 -58
  64. package/src/builders/SdCliNgLibraryBuilder.ts +102 -43
  65. package/src/builders/SdCliServerBuilder.ts +61 -44
  66. package/src/builders/SdCliTypescriptBuilder.ts +92 -73
  67. package/src/commons.ts +1 -1
  68. package/src/entry-points/SdCliLocalUpdater.ts +40 -33
  69. package/src/entry-points/SdCliProject.ts +14 -13
  70. package/src/packages/SdCliClientPackage.ts +3 -2
  71. package/src/packages/SdCliLibraryPackage.ts +2 -1
  72. package/src/packages/SdCliServerPackage.ts +4 -3
  73. package/src/utils/SdCliFileCrypto.ts +1 -1
  74. package/src/utils/SdProjectConfigUtil.ts +1 -1
  75. package/src/utils/SdWebpackUtil.ts +42 -22
  76. package/src/workers/client-ng-gen-worker.ts +60 -0
  77. package/src/workers/lib-build-worker.ts +7 -6
  78. package/tsconfig.json +2 -1
@@ -3,18 +3,16 @@ import { INpmConfig, ISdClientPackageConfig, ISdPackageBuildResult, ITsconfig }
3
3
  import * as webpack from "webpack";
4
4
  import * as path from "path";
5
5
  import * as ts from "typescript";
6
- import { FsUtil, PathUtil } from "@simplysm/sd-core-node";
6
+ import { FsUtil, Logger, PathUtil, SdProcessWorker } from "@simplysm/sd-core-node";
7
7
  import { JavaScriptOptimizerPlugin } from "@angular-devkit/build-angular/src/webpack/plugins/javascript-optimizer-plugin";
8
8
  import * as CssMinimizerPlugin from "css-minimizer-webpack-plugin";
9
9
  import * as esbuild from "esbuild";
10
10
  import {
11
- AnyComponentStyleBudgetChecker,
12
11
  CommonJsUsageWarnPlugin,
13
12
  DedupeModuleResolvePlugin,
14
13
  SuppressExtractedTextChunksWebpackPlugin
15
14
  } from "@angular-devkit/build-angular/src/webpack/plugins";
16
15
  import { LicenseWebpackPlugin } from "license-webpack-plugin";
17
- import { Type } from "@angular-devkit/build-angular";
18
16
  import { SassWorkerImplementation } from "@angular-devkit/build-angular/src/sass/sass-service";
19
17
  import * as MiniCssExtractPlugin from "mini-css-extract-plugin";
20
18
  import { AngularWebpackPlugin } from "@ngtools/webpack";
@@ -28,7 +26,6 @@ import { StringUtil, Wait } from "@simplysm/sd-core-common";
28
26
  import { augmentAppWithServiceWorker } from "@angular-devkit/build-angular/src/utils/service-worker";
29
27
  import * as browserslist from "browserslist";
30
28
  import * as WebpackHotMiddleware from "webpack-hot-middleware";
31
- import { SdCliNgLibraryBuilder } from "./SdCliNgLibraryBuilder";
32
29
  import * as os from "os";
33
30
  import { LintResult } from "eslint-webpack-plugin/declarations/options";
34
31
 
@@ -39,10 +36,15 @@ export class SdCliNgClientBuilder extends EventEmitter {
39
36
  public parsedTsconfig: ts.ParsedCommandLine;
40
37
  public readonly npmConfig: INpmConfig;
41
38
 
39
+ private _ngGenWorker?: SdProcessWorker;
40
+
41
+ protected readonly _logger: Logger;
42
+
42
43
  public constructor(public rootPath: string,
43
44
  public tsconfigFilePath: string,
44
45
  public projectRootPath: string,
45
- public config: ISdClientPackageConfig) {
46
+ public config: ISdClientPackageConfig,
47
+ public useCache: boolean) {
46
48
  super();
47
49
 
48
50
  const tsconfig: ITsconfig = FsUtil.readJson(this.tsconfigFilePath);
@@ -56,6 +58,8 @@ export class SdCliNgClientBuilder extends EventEmitter {
56
58
  };
57
59
 
58
60
  this.npmConfig = FsUtil.readJson(path.resolve(this.rootPath, "package.json"));
61
+
62
+ this._logger = Logger.get(["simplysm", "sd-cli", this.constructor.name, this.npmConfig.name]);
59
63
  }
60
64
 
61
65
  public on(event: "change", listener: () => void): this;
@@ -72,12 +76,16 @@ export class SdCliNgClientBuilder extends EventEmitter {
72
76
  const buildResult: ISdPackageBuildResult[] = [];
73
77
 
74
78
  // GENERATOR
75
- const ngGenerator = new SdCliNgLibraryBuilder(this.rootPath, this.tsconfigFilePath, ["emit", "check", "lint"], undefined);
76
- const genFilePaths = await ngGenerator.reloadProgramAsync(false);
77
- const genResult = await ngGenerator.generateAdditionalFilesAsync(genFilePaths, false);
79
+ this._logger.debug("NgGen 수행");
80
+ const genResult = await this._runNgGenWorkerAsync(false);
81
+ // const ngGenerator = new SdCliNgLibraryBuilder(this.rootPath, this.tsconfigFilePath, ["emit", "check", "lint"], undefined);
82
+ // const genFilePaths = await ngGenerator.reloadProgramAsync(false);
83
+ // const genResult = await ngGenerator.generateAdditionalFilesAsync(genFilePaths, false);
78
84
  buildResult.push(...genResult.result);
85
+ this._logger.debug("NgGen 수행 결과", genResult);
79
86
 
80
87
  // WEBPACK 빌드
88
+ this._logger.debug("Webpack 빌드 수행");
81
89
  const webpackConfig = this._getWebpackConfig(false);
82
90
  const compiler = webpack(webpackConfig);
83
91
  const webpackBuildResults = await new Promise<ISdPackageBuildResult[]>((resolve, reject) => {
@@ -91,6 +99,7 @@ export class SdCliNgClientBuilder extends EventEmitter {
91
99
  resolve(results);
92
100
  });
93
101
  });
102
+ this._logger.debug("Webpack 빌드 결과", webpackBuildResults);
94
103
  buildResult.push(...webpackBuildResults);
95
104
 
96
105
  // .config.json 파일 쓰기
@@ -100,14 +109,16 @@ export class SdCliNgClientBuilder extends EventEmitter {
100
109
 
101
110
  // service-worker 처리
102
111
 
103
- const packageKey = this.npmConfig.name.split("/").last()!;
104
- await augmentAppWithServiceWorker(
105
- PathUtil.posix(this.projectRootPath) as any,
106
- PathUtil.posix(path.relative(this.projectRootPath, this.rootPath)) as any,
107
- PathUtil.posix(path.relative(this.projectRootPath, this.parsedTsconfig.options.outDir!)) as any,
108
- `/${packageKey}/`,
109
- PathUtil.posix(path.relative(this.projectRootPath, path.resolve(this.rootPath, "ngsw-config.json")))
110
- );
112
+ if (FsUtil.exists(path.resolve(this.rootPath, "ngsw-config.json"))) {
113
+ const packageKey = this.npmConfig.name.split("/").last()!;
114
+ await augmentAppWithServiceWorker(
115
+ PathUtil.posix(this.projectRootPath) as any,
116
+ PathUtil.posix(path.relative(this.projectRootPath, this.rootPath)) as any,
117
+ PathUtil.posix(path.relative(this.projectRootPath, this.parsedTsconfig.options.outDir!)) as any,
118
+ `/${packageKey}/`,
119
+ PathUtil.posix(path.relative(this.projectRootPath, path.resolve(this.rootPath, "ngsw-config.json")))
120
+ );
121
+ }
111
122
 
112
123
  this.emit("complete", buildResult.distinct());
113
124
  }
@@ -115,66 +126,73 @@ export class SdCliNgClientBuilder extends EventEmitter {
115
126
  public async watchAsync(serverPath?: string): Promise<NextHandleFunction[]> {
116
127
  await FsUtil.removeAsync(this.parsedTsconfig.options.outDir!);
117
128
 
118
- let buildResult: ISdPackageBuildResult[] = [];
129
+ let ngGenResults: ISdPackageBuildResult[] = [];
119
130
 
120
- const ngGenerator = new SdCliNgLibraryBuilder(this.rootPath, this.tsconfigFilePath, ["emit", "check", "lint"], undefined);
131
+ // const ngGenerator = new SdCliNgLibraryBuilder(this.rootPath, this.tsconfigFilePath, ["emit", "check", "lint", "genIndex"], undefined);
121
132
 
122
133
  // WEBPACK 빌드
134
+ let invalidFiles = new Set<string>();
123
135
  const webpackConfig = this._getWebpackConfig(true);
124
136
  const compiler = webpack(webpackConfig);
125
137
  return await new Promise<NextHandleFunction[]>((resolve, reject) => {
138
+ compiler.hooks.invalid.tap(this.constructor.name, (fileName) => {
139
+ if (fileName != null) {
140
+ invalidFiles.add(fileName);
141
+ }
142
+ });
143
+
126
144
  compiler.hooks.watchRun.tapAsync(this.constructor.name, async (compiler1, callback) => {
127
145
  this.emit("change");
128
146
 
129
- buildResult = [];
147
+ ngGenResults = [];
130
148
 
131
149
  // -- FIRST
132
150
 
133
151
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition,@typescript-eslint/strict-boolean-expressions
134
- if (!compiler.modifiedFiles || !compiler.removedFiles) {
135
- const genDirtyFilePaths = await ngGenerator.reloadProgramAsync(true);
136
- const genResult = await ngGenerator.generateAdditionalFilesAsync(genDirtyFilePaths, true);
137
- buildResult.push(...genResult.result);
138
-
152
+ if (invalidFiles.size === 0) {
153
+ this._logger.debug("첫 NgGen 수행");
154
+ const genResult = await this._runNgGenWorkerAsync(true);
155
+ // const genDirtyFilePaths = await ngGenerator.reloadProgramAsync(true);
156
+ // const genResult = await ngGenerator.generateAdditionalFilesAsync(genDirtyFilePaths, true);
157
+ ngGenResults.push(...genResult.result);
158
+ this._logger.debug("첫 NgGen 수행 결과", genResult);
159
+
160
+ this._logger.debug("Webpack 빌드 수행");
139
161
  callback();
140
162
  }
141
163
 
142
164
  // -- NOT FIRST
143
-
144
165
  else {
145
- const changedFiles = [...compiler.modifiedFiles, ...compiler.removedFiles];
146
-
147
166
  // 변경파일 중, 유효한 파일 추출
148
- const watchPaths = ngGenerator.getWatchPaths();
167
+ const changedFiles = [...invalidFiles];
168
+ invalidFiles = new Set<string>();
169
+
149
170
  const changedFilePaths = changedFiles
150
171
  .map((item) => PathUtil.posix(item))
151
172
  .filter((item) => path.basename(item).includes("."))
152
- .filter((item) => watchPaths.includes(path.dirname(item)))
153
173
  .distinct();
154
174
  if (changedFilePaths.length === 0) {
175
+ this._logger.debug("Webpack 빌드 수행");
155
176
  callback();
156
177
  return;
157
178
  }
158
179
 
159
- // 변경파일에 대한 프로그램 리로드
160
- const genDirtyFilePaths = await ngGenerator.reloadChangedProgramAsync(changedFilePaths, true);
161
- if (genDirtyFilePaths.length === 0) {
162
- callback();
163
- return;
164
- }
180
+ this._logger.debug("변경파일에 대한, NgGen 수행", changedFilePaths);
165
181
 
166
- // 변경파일에 대해 자동생성할 소스파일들 생성 및 리로드 (index.ts등)
167
- const genResult = await ngGenerator.generateAdditionalFilesAsync(genDirtyFilePaths, true);
168
- buildResult.push(...genResult.result);
182
+ const genResult = await this._runNgGenWorkerAsync(true, changedFilePaths);
183
+ ngGenResults.push(...genResult.result);
169
184
  if (genResult.dirtyFilePaths.length === 0) {
185
+ this._logger.debug("변경파일에 대한, NgGen 결과", genResult);
186
+ this._logger.debug("Webpack 빌드 수행");
170
187
  callback();
171
188
  return;
172
189
  }
173
- genDirtyFilePaths.push(...genResult.dirtyFilePaths);
190
+
191
+ this._logger.debug("변경파일에 대한, NgGen 결과", genResult);
174
192
 
175
193
  const modifiedFiles = [...compiler.modifiedFiles];
176
194
  const removedFiles = [...compiler.removedFiles];
177
- for (const genFilePath of genDirtyFilePaths.distinct()) {
195
+ for (const genFilePath of genResult.dirtyFilePaths.distinct()) {
178
196
  if (FsUtil.exists(genFilePath)) {
179
197
  modifiedFiles.push(genFilePath);
180
198
  }
@@ -185,6 +203,7 @@ export class SdCliNgClientBuilder extends EventEmitter {
185
203
  compiler.modifiedFiles = new Set(modifiedFiles.distinct());
186
204
  compiler.removedFiles = new Set(removedFiles.distinct());
187
205
 
206
+ this._logger.debug("Webpack 빌드 수행");
188
207
  callback();
189
208
  }
190
209
  });
@@ -196,7 +215,9 @@ export class SdCliNgClientBuilder extends EventEmitter {
196
215
 
197
216
  compiler.hooks.failed.tap(this.constructor.name, (err) => {
198
217
  const results = SdWebpackUtil.getWebpackResults(err);
199
- this.emit("complete", [...buildResult, ...results].distinct());
218
+ const allResults = [...ngGenResults, ...results].distinct();
219
+ this.emit("complete", allResults);
220
+ this._logger.debug("Webpack 빌드 수행 결과", allResults);
200
221
  reject(err);
201
222
  });
202
223
 
@@ -212,7 +233,9 @@ export class SdCliNgClientBuilder extends EventEmitter {
212
233
 
213
234
 
214
235
  await Wait.true(() => devMiddleware !== undefined && hotMiddleware !== undefined);
215
- this.emit("complete", [...buildResult, ...results].distinct());
236
+ const allResults = [...ngGenResults, ...results].distinct();
237
+ this.emit("complete", allResults);
238
+ this._logger.debug("Webpack 빌드 수행 결과", allResults);
216
239
  resolve([devMiddleware!, hotMiddleware!]);
217
240
  });
218
241
 
@@ -242,19 +265,16 @@ export class SdCliNgClientBuilder extends EventEmitter {
242
265
  profile: false,
243
266
  resolve: {
244
267
  roots: [this.rootPath],
245
- extensions: [".ts", ".tsx", ".mjs", ".js"],
268
+ extensions: [".ts", ".js"],
246
269
  symlinks: true,
247
- modules: [this.projectRootPath, "node_modules"],
270
+ // modules: allNodeModulePaths,
248
271
  mainFields: ["es2015", "browser", "module", "main"],
249
272
  fallback: this.config.resolveFallback
250
273
  },
251
- resolveLoader: {
274
+ /*resolveLoader: {
252
275
  symlinks: true,
253
- modules: [
254
- "node_modules",
255
- ...this._findAllNodeModules(__dirname, this.projectRootPath)
256
- ]
257
- },
276
+ modules: allNodeModulePaths
277
+ },*/
258
278
  context: this.projectRootPath,
259
279
  entry: {
260
280
  main: [
@@ -278,6 +298,7 @@ export class SdCliNgClientBuilder extends EventEmitter {
278
298
  // chunkFilename: watch ? "[name].js" : "[name].[chunkhash:20].js",
279
299
  filename: "[name].js",
280
300
  chunkFilename: "[name].js",
301
+ assetModuleFilename: "resources/[name][ext][query]",
281
302
  crossOriginLoading: false,
282
303
  trustedTypes: "angular#bundler"
283
304
  },
@@ -377,18 +398,22 @@ export class SdCliNgClientBuilder extends EventEmitter {
377
398
  {
378
399
  test: /\.[jt]sx?$/,
379
400
  loader: "@ngtools/webpack"
401
+ },
402
+ {
403
+ test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf|xlsx?|pptx?|docx?|zip|pfx|pkl)$/,
404
+ type: "asset/resource"
380
405
  }
381
406
  ]
382
407
  },
383
408
  experiments: { syncWebAssembly: true, asyncWebAssembly: true },
384
- cache: watch ? { type: "memory", maxGenerations: 1 } : false,
409
+ cache: (watch && this.useCache) ? { type: "memory", maxGenerations: 1 } : false,
385
410
  optimization: {
386
411
  minimizer: watch ? [] : [
387
412
  new JavaScriptOptimizerPlugin({
388
413
  define: { ngDevMode: false, ngI18nClosureMode: false, ngJitMode: false },
389
414
  sourcemap: false,
390
415
  target: this.parsedTsconfig.options.target,
391
- keepNames: false,
416
+ keepNames: true,
392
417
  removeLicenses: true,
393
418
  advanced: true
394
419
  }) as any,
@@ -403,9 +428,9 @@ export class SdCliNgClientBuilder extends EventEmitter {
403
428
  minify: true,
404
429
  sourcefile,
405
430
  target: browserslist([
406
- "last 2 Chrome versions",
431
+ "last 1 Chrome versions",
407
432
  "last 2 Edge major versions"
408
- ])
433
+ ]).map((item) => item.replace(/ /g, ""))
409
434
  });
410
435
 
411
436
  return {
@@ -437,7 +462,7 @@ export class SdCliNgClientBuilder extends EventEmitter {
437
462
  ],
438
463
  moduleIds: "deterministic",
439
464
  chunkIds: watch ? "named" : "deterministic",
440
- emitOnErrors: false,
465
+ emitOnErrors: watch,
441
466
  runtimeChunk: "single",
442
467
  splitChunks: {
443
468
  maxAsyncRequests: Infinity,
@@ -504,13 +529,13 @@ export class SdCliNgClientBuilder extends EventEmitter {
504
529
  append: undefined
505
530
  })
506
531
  ] : [],
507
- new AnyComponentStyleBudgetChecker(watch ? [] : [
532
+ /*new AnyComponentStyleBudgetChecker(watch ? [] : [
508
533
  {
509
534
  type: Type.Any,
510
535
  maximumWarning: "2kb",
511
536
  maximumError: "4kb"
512
537
  }
513
- ]),
538
+ ]),*/
514
539
  {
515
540
  apply(compiler) {
516
541
  compiler.hooks.shutdown.tap("sass-worker", () => {
@@ -584,13 +609,37 @@ export class SdCliNgClientBuilder extends EventEmitter {
584
609
  }),
585
610
  ...watch ? [
586
611
  new webpack.HotModuleReplacementPlugin()
587
- ] : []
612
+ ] : [],
613
+ /*...watch ? [
614
+ new webpack.WatchIgnorePlugin({
615
+ paths: [
616
+ path.resolve(this.rootPath, "src/_modules/"),
617
+ path.resolve(this.rootPath, "src/_routes.ts")
618
+ ]
619
+ })
620
+ ] : [],*/
621
+ new webpack.ProgressPlugin({
622
+ handler: (per: number, msg: string, ...args: string[]) => {
623
+ const phaseText = msg ? ` - phase: ${msg}` : "";
624
+ const argsText = args.length > 0 ? ` - args: [${args.join(", ")}]` : "";
625
+ this._logger.debug(`Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`);
626
+ }
627
+ })
588
628
  ],
589
629
  node: false,
590
630
  stats: "errors-warnings"
591
631
  };
592
632
  }
593
633
 
634
+ /*private _findAllInternalModuleCachePaths(): string[] {
635
+ return this._findAllNodeModules(this.rootPath, this.projectRootPath)
636
+ .mapMany((item) => (
637
+ FsUtil.readdir(item)
638
+ .filter((item1) => !item1.startsWith("@simplysm"))
639
+ .map((item1) => path.resolve(item, item1))
640
+ ));
641
+ }
642
+
594
643
  private _findAllNodeModules(from: string, root: string): string[] {
595
644
  const nodeModules: string[] = [];
596
645
 
@@ -609,5 +658,20 @@ export class SdCliNgClientBuilder extends EventEmitter {
609
658
  }
610
659
 
611
660
  return nodeModules;
661
+ }*/
662
+
663
+ private async _runNgGenWorkerAsync(watch: boolean, changedFilePaths?: string[]): Promise<{ dirtyFilePaths: string[]; result: ISdPackageBuildResult[] }> {
664
+ if (!this._ngGenWorker) {
665
+ this._ngGenWorker = await SdProcessWorker.createAsync(path.resolve(__dirname, `../workers/client-ng-gen-worker`), []);
666
+ }
667
+
668
+ const sender = this._ngGenWorker.createWorkSender();
669
+ return await new Promise<{ dirtyFilePaths: string[]; result: ISdPackageBuildResult[] }>(async (resolve) => {
670
+ await sender
671
+ .on("complete", (result) => {
672
+ resolve(result);
673
+ })
674
+ .sendAsync(this.rootPath, watch, changedFilePaths);
675
+ });
612
676
  }
613
677
  }
@@ -6,7 +6,7 @@ import { NgccProcessingCache } from "ng-packagr/lib/ng-package/ngcc-cache";
6
6
  import { NgccProcessor } from "ng-packagr/lib/ngc/ngcc-processor";
7
7
  import { EntryPointNode } from "ng-packagr/lib/ng-package/nodes";
8
8
  import { ngccTransformCompilerHost } from "ng-packagr/lib/ts/ngcc-transform-compiler-host";
9
- import { NgtscProgram } from "@angular/compiler-cli";
9
+ import { MetadataCollector, NgtscProgram } from "@angular/compiler-cli";
10
10
  import { ITsBuildFileCache, ITsGenResult, SdCliTypescriptBuilder } from "./SdCliTypescriptBuilder";
11
11
  import { NgCompiler } from "@angular/compiler-cli/src/ngtsc/core";
12
12
  import { NeverEntryError, StringUtil } from "@simplysm/sd-core-common";
@@ -16,6 +16,7 @@ import * as nodeSass from "node-sass";
16
16
  import { createHash } from "crypto";
17
17
  import { SdCliNgModuleFilesGenerator } from "../build-tools/SdCliNgModuleFilesGenerator";
18
18
  import { OptimizeFor } from "@angular/compiler-cli/src/ngtsc/typecheck/api";
19
+ import { ModuleMetadata } from "@angular/compiler-cli/src/metadata/schema";
19
20
 
20
21
  export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
21
22
  protected _ngProgram?: NgtscProgram;
@@ -28,17 +29,17 @@ export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
28
29
 
29
30
  public constructor(public rootPath: string,
30
31
  public tsconfigFilePath: string,
31
- ignoreProcesses: ("emit" | "check" | "lint" | "genIndex" | "genNgModule")[],
32
+ skipProcesses: ("emit" | "check" | "lint" | "genIndex" | "genNgModule")[],
32
33
  public autoIndexConfig: ISdAutoIndexConfig | undefined) {
33
- super(rootPath, tsconfigFilePath, ignoreProcesses as ("emit" | "check" | "lint" | "genIndex")[], autoIndexConfig);
34
- this.ignoreProcesses = ignoreProcesses;
34
+ super(rootPath, tsconfigFilePath, skipProcesses as ("emit" | "check" | "lint" | "genIndex")[], autoIndexConfig);
35
+ this.skipProcesses = skipProcesses;
35
36
 
36
37
  const tsconfig: ITsconfig = FsUtil.readJson(this.tsconfigFilePath);
37
- this._parsedTsconfig = {
38
- ...this._parsedTsconfig,
39
- options: {
38
+ this._tsconfig = {
39
+ ...tsconfig,
40
+ compilerOptions: {
40
41
  baseUrl: path.dirname(this.tsconfigFilePath),
41
- ...this._parsedTsconfig.options,
42
+ ...tsconfig.compilerOptions,
42
43
  ...tsconfig.angularCompilerOptions
43
44
  }
44
45
  };
@@ -52,7 +53,7 @@ export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
52
53
 
53
54
  const diagnostics: ts.Diagnostic[] = [];
54
55
 
55
- if (!this.ignoreProcesses.includes("check")) {
56
+ if (!this.skipProcesses.includes("check")) {
56
57
  diagnostics.push(
57
58
  ...this._ngCompiler.getOptionDiagnostics(),
58
59
  ...this._builder.getOptionsDiagnostics(),
@@ -61,9 +62,10 @@ export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
61
62
 
62
63
  await this._ngCompiler.analyzeAsync();
63
64
 
64
- for (const filePath of dirtyFilePaths) {
65
- const sourceFile = this._builder.getSourceFile(filePath);
66
- if (!sourceFile) continue;
65
+ for (const sourceFile of this._builder.getSourceFiles()) {
66
+ /*for (const filePath of dirtyFilePaths) {
67
+ const sourceFile = this._builder.getSourceFile(filePath);
68
+ if (!sourceFile) continue;*/
67
69
 
68
70
  if (this._ngCompiler.ignoreForDiagnostics.has(sourceFile)) continue;
69
71
  if (sourceFile.isDeclarationFile) continue;
@@ -76,7 +78,7 @@ export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
76
78
  }
77
79
  }
78
80
 
79
- if (!this.ignoreProcesses.includes("emit")) {
81
+ if (!this.skipProcesses.includes("emit")) {
80
82
  const transformers = this._ngCompiler.prepareEmit().transformers;
81
83
 
82
84
  for (const filePath of dirtyFilePaths) {
@@ -86,7 +88,21 @@ export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
86
88
  if (sourceFile.isDeclarationFile) continue;
87
89
 
88
90
  this._builder.emit(sourceFile, undefined, undefined, undefined, transformers);
91
+
92
+ // TODO: sd-metadata를 ModuleMetadata로 만들게 아니고, 아예 IAotModuleDefMetadata형식으로 만들어지게 해야함
93
+ if (this._program!.getCompilerOptions().declaration) {
94
+ if (sourceFile.fileName.endsWith(".ts") && !sourceFile.fileName.endsWith(".d.ts")) {
95
+ const metadata = this._getMetadata(sourceFile);
96
+ if (metadata) {
97
+ this._writeMetadataFile(sourceFile, metadata);
98
+ }
99
+ }
100
+ }
89
101
  }
102
+
103
+ /*if (this._parsedTsconfig.options.declaration) {
104
+ this._writeMetadataBundles();
105
+ }*/
90
106
  }
91
107
 
92
108
  const result = diagnostics.map((item) => SdTsDiagnosticUtil.convertDiagnosticsToResult(item)).filterExists();
@@ -96,15 +112,54 @@ export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
96
112
  return result;
97
113
  }
98
114
 
115
+ private _writeMetadataFile(sourceFile: ts.SourceFile, metadata: ModuleMetadata): void {
116
+ const metadataText = JSON.stringify({
117
+ ...metadata,
118
+ importAs: this.npmConfig.name
119
+ });
120
+ const outFileName = path.resolve(
121
+ this._program!.getCompilerOptions().declarationDir!,
122
+ path.relative(path.resolve(this.rootPath, "src"), sourceFile.fileName)
123
+ ).replace(/\.ts$/, ".sd-metadata.json");
124
+ this._cacheCompilerHost!.writeFile(outFileName, metadataText, false, undefined, [sourceFile]);
125
+ }
126
+
127
+ private _getMetadata(sourceFile: ts.SourceFile): ModuleMetadata | undefined {
128
+ return new MetadataCollector().getMetadata(sourceFile, true);
129
+ }
130
+
131
+ /*private _writeMetadataBundles(): void {
132
+ for (const rootFilePath of this._parsedTsconfig.fileNames) {
133
+ const metadata = new MetadataBundler(
134
+ rootFilePath.replace(/\.ts$/, ""),
135
+ rootFilePath.endsWith("/index.ts") ? this.npmConfig.name
136
+ : (this.npmConfig.name + "/" + PathUtil.posix(path.relative(path.resolve(this.rootPath, "src"), rootFilePath)).replace(/\.ts/, "")),
137
+ new CompilerHostAdapter(this._cacheCompilerHost!, null, this._parsedTsconfig.options),
138
+ undefined
139
+ ).getMetadataBundle().metadata;
140
+
141
+ const outFileName = path.resolve(
142
+ this._parsedTsconfig.options.declarationDir!,
143
+ path.relative(path.resolve(this.rootPath, "src"), rootFilePath)
144
+ ).replace(/\.ts$/, ".sd-metadata.json");
145
+
146
+ const fileCache = this._fileCache.get(PathUtil.posix(rootFilePath));
147
+ if (!fileCache?.sourceFile) throw new NeverEntryError();
148
+ this._cacheCompilerHost!.writeFile(outFileName, JSON.stringify(metadata), false, undefined, [fileCache.sourceFile]);
149
+ }
150
+ }*/
151
+
99
152
  public override async reloadProgramAsync(watch: boolean): Promise<string[]> {
100
153
  this._logger.debug("프로그램 리로드");
101
154
 
102
- this._moduleResolutionCache = ts.createModuleResolutionCache(this.rootPath, (s) => s, this._parsedTsconfig.options);
103
- this._cacheCompilerHost = await this._createCacheCompilerHostAsync(this._parsedTsconfig, this._moduleResolutionCache);
155
+ const parsedTsconfig = ts.parseJsonConfigFileContent(this._tsconfig, ts.sys, this.rootPath);
156
+
157
+ this._moduleResolutionCache = ts.createModuleResolutionCache(this.rootPath, (s) => s, parsedTsconfig.options);
158
+ this._cacheCompilerHost = await this._createCacheCompilerHostAsync(parsedTsconfig, this._moduleResolutionCache);
104
159
 
105
160
  this._ngProgram = new NgtscProgram(
106
- this._parsedTsconfig.fileNames,
107
- this._parsedTsconfig.options,
161
+ parsedTsconfig.fileNames,
162
+ parsedTsconfig.options,
108
163
  this._cacheCompilerHost,
109
164
  this._ngProgram
110
165
  );
@@ -126,15 +181,27 @@ export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
126
181
  };
127
182
 
128
183
  if (watch) {
129
- const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
130
- this._program,
131
- this._cacheCompilerHost,
132
- this._builder as ts.EmitAndSemanticDiagnosticsBuilderProgram
133
- );
134
- this._builder = builder;
184
+ if (this.skipProcesses.includes("emit")) {
185
+ this._builder = ts.createSemanticDiagnosticsBuilderProgram(
186
+ this._program,
187
+ this._cacheCompilerHost,
188
+ this._builder as ts.SemanticDiagnosticsBuilderProgram
189
+ );
190
+ }
191
+ else {
192
+ this._builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
193
+ this._program,
194
+ this._cacheCompilerHost,
195
+ this._builder as ts.EmitAndSemanticDiagnosticsBuilderProgram
196
+ );
197
+ }
198
+ const builder = this._builder as ts.SemanticDiagnosticsBuilderProgram;
135
199
 
136
- const affectedFilePaths: string[] = [];
200
+ let seq = 0;
201
+ const affectedFilePathSet = new Set<string>();
137
202
  while (true) {
203
+ const affectedFilePaths: string[] = [];
204
+ this._logger.debug(`프로그램 리로드 > 변경파일 목록구성: [SEQ: ${++seq}]`);
138
205
  const result = builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
139
206
  if (this._ngCompiler!.ignoreForDiagnostics.has(sourceFile) && sourceFile.fileName.endsWith(".ngtypecheck.ts")) {
140
207
  const originalFilename = sourceFile.fileName.slice(0, -15) + ".ts";
@@ -147,14 +214,20 @@ export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
147
214
  return false;
148
215
  });
149
216
 
150
- if (!result) break;
151
-
152
- if ("fileName" in result.affected) {
217
+ if (result && "fileName" in result.affected) {
153
218
  affectedFilePaths.push(result.affected.fileName);
154
219
  }
220
+
221
+ affectedFilePaths.distinctThis();
222
+
223
+ this._logger.debug(`프로그램 리로드 > 변경파일 목록구성: [SEQ: ${seq}, SIZE:${affectedFilePathSet.size}]\n` + affectedFilePaths.map((item) => "- " + item).join("\n"));
224
+
225
+ affectedFilePathSet.adds(...affectedFilePaths.distinct());
226
+
227
+ if (!result) break;
155
228
  }
156
229
 
157
- const result = affectedFilePaths.distinct();
230
+ const result = Array.from(affectedFilePathSet.values());
158
231
 
159
232
  this._logger.debug("프로그램 리로드", result);
160
233
 
@@ -208,7 +281,7 @@ export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
208
281
  this._logger.debug("Angular Module, RoutingModule 생성", dirtyFilePaths);
209
282
 
210
283
  const result: ITsGenResult = { dirtyFilePaths: [], result: [] };
211
- if (this.ignoreProcesses.includes("genNgModule")) return result;
284
+ if (this.skipProcesses.includes("genNgModule")) return result;
212
285
 
213
286
  const reloadResult = await this._moduleFilesGenerator.reloadSourceFilesAsync(
214
287
  dirtyFilePaths,
@@ -231,20 +304,6 @@ export class SdCliNgLibraryBuilder extends SdCliTypescriptBuilder {
231
304
  return result;
232
305
  }
233
306
 
234
- /*protected override _getAllDependencies(filePath: string): string[] {
235
- this._logger.debug("파일 의존성 확인", filePath);
236
-
237
- const sourceFile = this._builder!.getSourceFile(filePath);
238
- if (!sourceFile) {
239
- this._logger.debug("파일 의존성 확인 결과", []);
240
- return [];
241
- }
242
- const result = [...this._builder!.getAllDependencies(sourceFile), ...(this._fileCache.get(PathUtil.posix(filePath))?.scssDependencies ?? [])];
243
-
244
- this._logger.debug("파일 의존성 확인 결과", result);
245
- return result;
246
- }*/
247
-
248
307
  protected override async _createCacheCompilerHostAsync(parsedTsconfig: ts.ParsedCommandLine, moduleResolutionCache: ts.ModuleResolutionCache): Promise<ts.CompilerHost> {
249
308
  const tsCacheCompilerHost = await super._createCacheCompilerHostAsync(parsedTsconfig, moduleResolutionCache);
250
309