@simplysm/sd-cli 7.0.66 → 7.0.74

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.
@@ -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) => {
@@ -130,6 +130,7 @@ export class SdCliServerBuilder extends EventEmitter {
130
130
  {
131
131
  "name": npmConfig.name.replace(/@/g, "").replace(/\//g, "-"),
132
132
  "script": path.basename(path.resolve(this._parsedTsconfig.options.outDir!, "main.mjs")),
133
+ "node_args": "--experimental-specifier-resolution=node --experimental-import-meta-resolve",
133
134
  "watch": true,
134
135
  "watch_delay": 2000,
135
136
  "ignore_watch": [
@@ -211,7 +212,7 @@ export class SdCliServerBuilder extends EventEmitter {
211
212
  ].map((p) => path.dirname(p));
212
213
  }
213
214
 
214
- private _getWebpackConfig(watch: boolean, extModuleNames: string[]): webpack.Configuration {
215
+ private _getWebpackConfig(watch: boolean, extModules: { name: string; exists: boolean }[]): webpack.Configuration {
215
216
  const workspaceNpmConfig = this._getNpmConfig(this._workspaceRootPath)!;
216
217
  const workspaceName = workspaceNpmConfig.name;
217
218
 
@@ -227,15 +228,15 @@ export class SdCliServerBuilder extends EventEmitter {
227
228
  return {
228
229
  mode: watch ? "development" : "production",
229
230
  devtool: false,
230
- target: ["node", "es2015"],
231
+ target: ["node", "es2020"],
231
232
  profile: false,
232
233
  resolve: {
233
234
  roots: [this._rootPath],
234
235
  extensions: [".ts", ".js", ".mjs", ".cjs"],
235
236
  symlinks: true,
236
237
  modules: [this._workspaceRootPath, "node_modules"],
237
- mainFields: ["es2015", "default", "module", "main"],
238
- conditionNames: ["es2015", "..."]
238
+ mainFields: ["es2020", "default", "module", "main"],
239
+ conditionNames: ["es2020", "..."]
239
240
  },
240
241
  resolveLoader: {
241
242
  symlinks: true
@@ -251,17 +252,23 @@ export class SdCliServerBuilder extends EventEmitter {
251
252
  hashFunction: "xxhash64",
252
253
  clean: true,
253
254
  path: this._parsedTsconfig.options.outDir,
254
- filename: "[name].cjs",
255
- chunkFilename: "[name].cjs",
255
+ filename: "[name].mjs",
256
+ chunkFilename: "[name].mjs",
256
257
  assetModuleFilename: "res/[name][ext][query]",
257
- libraryTarget: "commonjs2"
258
+ library: {
259
+ type: "module"
260
+ },
261
+ module: true
262
+ },
263
+ experiments: {
264
+ outputModule: true
258
265
  },
259
266
  watch: false,
260
267
  watchOptions: { poll: undefined, ignored: undefined },
261
268
  performance: { hints: false },
262
269
  infrastructureLogging: { level: "error" },
263
270
  stats: "errors-warnings",
264
- externals: extModuleNames.toObject((item) => item, (item) => "commonjs2 " + item),
271
+ externals: extModules.toObject((item) => item.name, (item) => "node-commonjs " + item.name),
265
272
  cache: {
266
273
  type: "filesystem",
267
274
  profile: watch ? undefined : false,
@@ -276,6 +283,7 @@ export class SdCliServerBuilder extends EventEmitter {
276
283
  .update(watch.toString())
277
284
  .digest("hex")
278
285
  },
286
+ // cache: { type: "memory", maxGenerations: 1 },
279
287
  ...watch ? {
280
288
  snapshot: {
281
289
  immutablePaths: internalModuleCachePaths,
@@ -289,7 +297,7 @@ export class SdCliServerBuilder extends EventEmitter {
289
297
  extractComments: false,
290
298
  terserOptions: {
291
299
  compress: true,
292
- ecma: 2015,
300
+ ecma: 2020,
293
301
  sourceMap: false,
294
302
  keep_classnames: true,
295
303
  keep_fnames: true,
@@ -318,9 +326,6 @@ export class SdCliServerBuilder extends EventEmitter {
318
326
  test: /\.[cm]?[tj]sx?$/,
319
327
  resolve: {
320
328
  fullySpecified: false
321
- },
322
- use: {
323
- loader: "babel-loader"
324
329
  }
325
330
  },
326
331
  ...watch ? [
@@ -455,6 +460,10 @@ export class SdCliServerBuilder extends EventEmitter {
455
460
  results.push({ name: moduleName, exists: true });
456
461
  }
457
462
 
463
+ if (this._config.externalNodeModules?.includes(moduleName)) {
464
+ results.push({ name: moduleName, exists: true });
465
+ }
466
+
458
467
  fn(modulePath);
459
468
  }
460
469
 
@@ -472,6 +481,10 @@ export class SdCliServerBuilder extends EventEmitter {
472
481
  results.push({ name: optModuleName, exists: true });
473
482
  }
474
483
 
484
+ if (this._config.externalNodeModules?.includes(optModuleName)) {
485
+ results.push({ name: optModuleName, exists: true });
486
+ }
487
+
475
488
  fn(optModulePath);
476
489
  }
477
490
  };
@@ -296,7 +296,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
296
296
  char: undefined,
297
297
  code: undefined,
298
298
  severity: "error",
299
- message: err.message
299
+ message: err.stack
300
300
  }];
301
301
  }
302
302
  }
package/src/commons.ts CHANGED
@@ -57,6 +57,8 @@ export interface ISdCliServerPackageConfig {
57
57
  configs?: Record<string, any>;
58
58
  pm2?: Record<string, any> | boolean;
59
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 {
@@ -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
  }
@@ -1,4 +1,4 @@
1
- import { FsUtil, Logger, SdProcess } from "@simplysm/sd-core-node";
1
+ import { FsUtil, Logger, PathUtil, SdProcess } from "@simplysm/sd-core-node";
2
2
  import path from "path";
3
3
  import { INpmConfig, ISdCliConfig, ISdCliPackageBuildResult } from "../commons";
4
4
  import { SdCliPackage } from "../packages/SdCliPackage";
@@ -23,7 +23,7 @@ export class SdCliWorkspace {
23
23
  this._npmConfig = FsUtil.readJson(npmConfigFilePath);
24
24
  }
25
25
 
26
- public async watchAsync(opt: { confFileRelPath: string; optNames: string[] }): Promise<void> {
26
+ public async watchAsync(opt: { confFileRelPath: string; optNames: string[]; pkgs: string[] }): Promise<void> {
27
27
  this._logger.debug("프로젝트 설정 가져오기...");
28
28
  const config = await SdCliConfigUtil.loadConfigAsync(path.resolve(this._rootPath, opt.confFileRelPath), true, opt.optNames);
29
29
 
@@ -33,7 +33,7 @@ export class SdCliWorkspace {
33
33
  }
34
34
 
35
35
  this._logger.debug("패키지 목록 구성...");
36
- const pkgs = await this._getPackagesAsync(config);
36
+ const pkgs = await this._getPackagesAsync(config, opt.pkgs);
37
37
 
38
38
  this._logger.debug("패키지 이벤트 설정...");
39
39
  let changeCount = 0;
@@ -72,22 +72,29 @@ export class SdCliWorkspace {
72
72
  }
73
73
  changeCount++;
74
74
 
75
- const pkgNames = pkgs.map((item) => item.name);
76
- const buildCompletedPackageNames: string[] = [];
77
- await pkgs.parallelAsync(async (pkg) => {
78
- await Wait.until(() => !pkg.allDependencies.some((dep) => pkgNames.includes(dep) && !buildCompletedPackageNames.includes(dep)));
79
- if (pkg.config.type === "client") {
80
- const middlewares = (await pkg.watchAsync()) as NextHandleFunction[];
81
-
82
- const serverInfo = this._serverInfoMap.getOrCreate(pkg.config.server, { middlewares: [], clientInfos: [] });
83
- serverInfo.middlewares.push(...middlewares);
84
- serverInfo.clientInfos.push({ pkgKey: pkg.name.split("/").last()! });
85
- }
86
- else {
87
- await pkg.watchAsync();
88
- }
89
- buildCompletedPackageNames.push(pkg.name);
90
- });
75
+ try {
76
+ const pkgNames = pkgs.map((item) => item.name);
77
+ const buildCompletedPackageNames: string[] = [];
78
+ await pkgs.parallelAsync(async (pkg) => {
79
+ await Wait.until(() => !pkg.allDependencies.some((dep) => pkgNames.includes(dep) && !buildCompletedPackageNames.includes(dep)));
80
+ if (pkg.config.type === "client") {
81
+ const middlewares = (await pkg.watchAsync()) as NextHandleFunction[];
82
+
83
+ const serverInfo = this._serverInfoMap.getOrCreate(pkg.config.server, { middlewares: [], clientInfos: [] });
84
+ serverInfo.middlewares.push(...middlewares);
85
+ serverInfo.clientInfos.push({ pkgKey: pkg.name.split("/").last()! });
86
+ }
87
+ else {
88
+ await pkg.watchAsync();
89
+ }
90
+ buildCompletedPackageNames.push(pkg.name);
91
+ });
92
+ }
93
+ catch (err) {
94
+ this._loggingResults(totalResultMap);
95
+ this._loggingOpenClientHrefs();
96
+ throw err;
97
+ }
91
98
 
92
99
  changeCount--;
93
100
  if (changeCount === 0) {
@@ -111,18 +118,22 @@ export class SdCliWorkspace {
111
118
  }
112
119
  const entryFilePath = path.resolve(pkg.rootPath, entryFileRelPath);
113
120
 
114
- this._logger.log(`서버(${pkg.name}) 재시작...`);
115
121
  try {
116
122
  const serverInfo = this._serverInfoMap.getOrCreate(path.basename(pkg.rootPath), {
117
123
  middlewares: [],
118
124
  clientInfos: []
119
125
  });
120
126
  if (serverInfo.server) {
127
+ this._logger.log(`[${pkg.name}] 기존 서버 중지...`);
121
128
  await serverInfo.server.closeAsync();
122
129
  delete serverInfo.server;
123
130
  }
124
131
 
125
- serverInfo.server = (await import("file:///" + entryFilePath + "?update=" + Uuid.new().toString())).default.default as SdServiceServer | undefined;
132
+ this._logger.log(`[${pkg.name}] 서버 시작중...`);
133
+
134
+ const serverMainPath = "file:///" + PathUtil.posix(entryFilePath) + "?update=" + Uuid.new().toString().replace(/-/g, "");
135
+ const serverModule = await import(serverMainPath);
136
+ serverInfo.server = serverModule.default as SdServiceServer | undefined;
126
137
  if (!serverInfo.server) {
127
138
  this._logger.error(`${entryFilePath}(0, 0): 'SdServiceServer'를 'export'해야 합니다.`);
128
139
  this._isServerRestarting = false;
@@ -131,7 +142,7 @@ export class SdCliWorkspace {
131
142
  serverInfo.server.devMiddlewares = serverInfo.middlewares;
132
143
  await Wait.until(() => serverInfo.server!.isOpen);
133
144
 
134
- this._logger.log(`서버(${pkg.name}) 재시작 완료`);
145
+ this._logger.log(`[${pkg.name}] 서버가 시작되었습니다.`);
135
146
  }
136
147
  catch (err) {
137
148
  this._logger.error(`서버(${pkg.name}) 재시작중 오류 발생`, err);
@@ -140,12 +151,12 @@ export class SdCliWorkspace {
140
151
  this._isServerRestarting = false;
141
152
  }
142
153
 
143
- public async buildAsync(opt: { confFileRelPath: string; optNames: string[] }): Promise<void> {
154
+ public async buildAsync(opt: { confFileRelPath: string; optNames: string[]; pkgs: string[] }): Promise<void> {
144
155
  this._logger.debug("프로젝트 설정 가져오기...");
145
156
  const config = await SdCliConfigUtil.loadConfigAsync(path.resolve(this._rootPath, opt.confFileRelPath), false, opt.optNames);
146
157
 
147
158
  this._logger.debug("패키지 목록 구성...");
148
- const pkgs = await this._getPackagesAsync(config);
159
+ const pkgs = await this._getPackagesAsync(config, opt.pkgs);
149
160
 
150
161
  this._logger.debug("프로젝트 및 패키지 버전 설정...");
151
162
  await this._upgradeVersionAsync(pkgs);
@@ -159,26 +170,32 @@ export class SdCliWorkspace {
159
170
  const pkgNames = pkgs.map((item) => item.name);
160
171
  const buildCompletedPackageNames: string[] = [];
161
172
  const totalResultMap = new Map<string, ISdCliPackageBuildResult[]>();
162
- await pkgs.parallelAsync(async (pkg) => {
163
- await Wait.until(() => !pkg.allDependencies.some((dep) => pkgNames.includes(dep) && !buildCompletedPackageNames.includes(dep)));
173
+ try {
174
+ await pkgs.parallelAsync(async (pkg) => {
175
+ await Wait.until(() => !pkg.allDependencies.some((dep) => pkgNames.includes(dep) && !buildCompletedPackageNames.includes(dep)));
164
176
 
165
- this._logger.debug(`[${pkg.name}] 빌드를 시작합니다...`);
166
- totalResultMap.set(pkg.name, await pkg.buildAsync());
167
- this._logger.debug(`[${pkg.name}] 빌드가 완료되었습니다.`);
177
+ this._logger.debug(`[${pkg.name}] 빌드를 시작합니다...`);
178
+ totalResultMap.set(pkg.name, await pkg.buildAsync());
179
+ this._logger.debug(`[${pkg.name}] 빌드가 완료되었습니다.`);
168
180
 
169
- buildCompletedPackageNames.push(pkg.name);
170
- });
181
+ buildCompletedPackageNames.push(pkg.name);
182
+ });
171
183
 
172
- this._loggingResults(totalResultMap);
173
- if (Array.from(totalResultMap.values()).mapMany().some((item) => item.severity === "error")) {
174
- throw new Error("빌드중 오류가 발생하였습니다.");
184
+ this._loggingResults(totalResultMap);
185
+ if (Array.from(totalResultMap.values()).mapMany().some((item) => item.severity === "error")) {
186
+ throw new Error("빌드중 오류가 발생하였습니다.");
187
+ }
188
+ else {
189
+ this._logger.info("모든 빌드가 완료되었습니다.");
190
+ }
175
191
  }
176
- else {
177
- this._logger.info("모든 빌드가 완료되었습니다.");
192
+ catch (err) {
193
+ this._loggingResults(totalResultMap);
194
+ throw new err;
178
195
  }
179
196
  }
180
197
 
181
- public async publishAsync(opt: { noBuild: boolean; confFileRelPath: string; optNames: string[] }): Promise<void> {
198
+ public async publishAsync(opt: { noBuild: boolean; confFileRelPath: string; optNames: string[]; pkgs: string[] }): Promise<void> {
182
199
  this._logger.debug("프로젝트 설정 가져오기...");
183
200
  const config = await SdCliConfigUtil.loadConfigAsync(path.resolve(this._rootPath, opt.confFileRelPath), false, opt.optNames);
184
201
 
@@ -197,7 +214,7 @@ export class SdCliWorkspace {
197
214
  }
198
215
 
199
216
  this._logger.debug("패키지 목록 구성...");
200
- const pkgs = await this._getPackagesAsync(config);
217
+ const pkgs = await this._getPackagesAsync(config, opt.pkgs);
201
218
 
202
219
  this._logger.debug("프로젝트 및 패키지 버전 설정...");
203
220
  await this._upgradeVersionAsync(pkgs);
@@ -270,16 +287,17 @@ export class SdCliWorkspace {
270
287
  process.stdout.clearLine(0);
271
288
  }
272
289
 
273
- private async _getPackagesAsync(conf: ISdCliConfig): Promise<SdCliPackage[]> {
290
+ private async _getPackagesAsync(conf: ISdCliConfig, pkgs: string[]): Promise<SdCliPackage[]> {
274
291
  const pkgRootPaths = await this._npmConfig.workspaces?.mapManyAsync(async (item) => await FsUtil.globAsync(item));
275
292
  if (!pkgRootPaths) {
276
293
  throw new Error("최상위 'package.json'에서 'workspaces'를 찾을 수 없습니다.");
277
294
  }
278
295
 
279
-
280
296
  return pkgRootPaths.map((pkgRootPath) => {
297
+ if (pkgs.length > 0 && !pkgs.includes(path.basename(pkgRootPath))) return undefined;
281
298
  const pkgConfig = conf.packages[path.basename(pkgRootPath)];
282
- return pkgConfig ? new SdCliPackage(this._rootPath, pkgRootPath, pkgConfig) : undefined;
299
+ if (!pkgConfig) return undefined;
300
+ return new SdCliPackage(this._rootPath, pkgRootPath, pkgConfig);
283
301
  }).filterExists();
284
302
  }
285
303
 
@@ -7,17 +7,21 @@ import {
7
7
  SdCliBbNgModuleMetadata,
8
8
  SdCliBbNgPipeMetadata
9
9
  } from "./babel/TSdCliBbNgMetadata";
10
- import { NeverEntryError, StringUtil } from "@simplysm/sd-core-common";
10
+ import { NeverEntryError, ObjectUtil, StringUtil } from "@simplysm/sd-core-common";
11
11
  import { TSdCliMetaRef } from "./commons";
12
12
  import { SdCliTsNgInjectableMetadata } from "./typescript/SdCliTsFileMetadata";
13
13
  import path from "path";
14
14
  import { JSDOM } from "jsdom";
15
- import { FsUtil, PathUtil } from "@simplysm/sd-core-node";
15
+ import { FsUtil, Logger, PathUtil } from "@simplysm/sd-core-node";
16
16
  import os from "os";
17
+ import { INpmConfig } from "../commons";
17
18
 
18
19
  // TODO: 에러 메시지 처리
19
20
  // TODO: forRoot에 있는 Provider 처리??
21
+ // TODO: CircularDependency
20
22
  export class SdCliNgModuleGenerator {
23
+ private readonly _logger: Logger;
24
+
21
25
  private readonly _bbMeta: SdCliBbRootMetadata;
22
26
  private readonly _tsMeta: SdCliTsRootMetadata;
23
27
 
@@ -44,6 +48,9 @@ export class SdCliNgModuleGenerator {
44
48
  const content = FsUtil.readFile(currFilePath);
45
49
  this._fileWriterCache.set(currFilePath, content);
46
50
  }
51
+
52
+ const npmConfig: INpmConfig = FsUtil.readJson(path.resolve(packagePath, "package.json"));
53
+ this._logger = Logger.get(["simplysm", "sd-cli", this.constructor.name, npmConfig.name]);
47
54
  }
48
55
 
49
56
  public removeCaches(changedFilePaths: string[]): void {
@@ -57,20 +64,118 @@ export class SdCliNgModuleGenerator {
57
64
  }
58
65
 
59
66
  public async runAsync(): Promise<void> {
67
+ this._logger.debug("Babel NgModule 정의 가져오기...");
60
68
  const bbNgModules = this._getBbNgModuleDefs();
69
+
70
+ this._logger.debug("NgModule Preset 구성...");
61
71
  const tsPreset = this._getTsGenNgModulePreset();
62
72
 
73
+ this._logger.debug("RoutingModule 정의 구성...");
63
74
  const genNgRoutingModules = await this._getGenNgRoutingModuleDefsAsync();
75
+
76
+ this._logger.debug("NgModule 정의 구성...");
64
77
  const genNgModules = this._getGenNgModuleDefs(bbNgModules, tsPreset.modules, tsPreset.sources);
65
78
 
79
+ this._logger.debug("NgModule 순환참조 병합...");
80
+ const mergedGenNgModules = this._mergeNgModuleCircularDependency(genNgModules);
81
+
82
+ this._logger.debug("생성할 파일 내용 구성...");
66
83
  const files = [
67
84
  ...this._getGenNgRoutingModuleFiles(genNgRoutingModules),
68
- ...this._getGenNgModuleFiles(genNgModules)
85
+ ...this._getGenNgModuleFiles(mergedGenNgModules)
69
86
  ];
70
87
 
88
+ this._logger.debug("파일 쓰기...");
71
89
  await this._writeFilesAsync(files);
72
90
  }
73
91
 
92
+ private _mergeNgModuleCircularDependency(mods: ITsGenNgModuleDef[]): ITsGenNgModuleDef[] {
93
+ // 중복 'check'목록 구성
94
+ const check = (mod: ITsGenNgModuleDef, ref: { filePath: string; name: string }, chain: ITsGenNgModuleDef[]): ITsGenNgModuleDef[][] => {
95
+ const result: ITsGenNgModuleDef[][] = [];
96
+ for (const imp of mod.imports) {
97
+ if ("filePath" in imp) {
98
+ if (imp.filePath === ref.filePath || imp.name === ref.name) {
99
+ result.push(chain.concat([mod]));
100
+ }
101
+ else {
102
+ const childMod = mods.single((item) => item.filePath === imp.filePath && item.name === imp.name);
103
+ if (childMod) {
104
+ if (chain.some((item) => item.filePath === childMod.filePath && item.name === childMod.name)) {
105
+ continue;
106
+ }
107
+
108
+ result.push(
109
+ ...check(childMod, ref, chain.concat([mod]))
110
+ );
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ return result;
117
+ };
118
+
119
+ const checkedList: ITsGenNgModuleDef[][] = [];
120
+ for (const mod of mods) {
121
+ const circularDepModules = check(mod, { filePath: mod.filePath, name: mod.name }, []);
122
+ if (circularDepModules.length > 0) {
123
+ checkedList.push(...circularDepModules);
124
+ }
125
+ }
126
+ const mergeModPacks = checkedList.map((item) => item.orderBy((item1) => item1.filePath)).distinct();
127
+
128
+ // 'merge'목록 구성
129
+ const currMergedList: ITsGenNgModuleDef[] = [];
130
+ for (const mergeModPack of mergeModPacks) {
131
+ let currMerged = mergeModPack[0];
132
+ for (const mergeMod of mergeModPack.slice(1)) {
133
+ currMerged = {
134
+ filePath: currMerged.filePath,
135
+ name: currMerged.name,
136
+ imports: currMerged.imports.concat(mergeMod.imports).distinct(),
137
+ exports: currMerged.exports.concat(mergeMod.exports).distinct(),
138
+ providers: currMerged.providers.concat(mergeMod.providers).distinct()
139
+ };
140
+ }
141
+ currMerged.imports.remove((imp) => (
142
+ "filePath" in imp &&
143
+ mergeModPack.some((mergeMod) => (
144
+ imp.filePath === mergeMod.filePath &&
145
+ imp.name === mergeMod.name
146
+ ))
147
+ ));
148
+
149
+ currMergedList.push(currMerged);
150
+ }
151
+
152
+ // 기존 'check'된 것들 모두 삭제
153
+ const result: ITsGenNgModuleDef[] = ObjectUtil.clone(mods);
154
+ for (const delMod of mergeModPacks.mapMany()) {
155
+ result.remove((item) => item.filePath === delMod.filePath && item.name === delMod.name);
156
+ }
157
+
158
+ // 'merge'된 것들 등록
159
+ result.push(...currMergedList);
160
+
161
+ // 모든 결과물에서 'import'에 'merge'된것들이 있으면 'merge'결과물 파일로 변경후, distinct
162
+ for (const resultItem of result) {
163
+ for (const imp of resultItem.imports) {
164
+ if (!("filePath" in imp)) continue;
165
+
166
+ for (const mergeModPack of mergeModPacks) {
167
+ if (mergeModPack.some((mergeModPackItem) => mergeModPackItem.filePath === imp.filePath && mergeModPackItem.name === imp.name)) {
168
+ imp.filePath = mergeModPack[0].filePath;
169
+ imp.name = mergeModPack[0].name;
170
+ }
171
+ }
172
+ }
173
+ resultItem.imports = resultItem.imports.distinct();
174
+ }
175
+
176
+ return result;
177
+ }
178
+
74
179
  private _findBbMetasFromMeta(srcMeta: TSdCliBbMetadata): TSdCliBbMetadata[] {
75
180
  const metaTmp = typeof srcMeta !== "string" && "__TSdCliMetaRef__" in srcMeta ? this._bbMeta.findMeta(srcMeta) : srcMeta;
76
181
  return metaTmp instanceof Array ? metaTmp : [metaTmp];
@@ -187,7 +292,7 @@ export class SdCliNgModuleGenerator {
187
292
  const moduleFilePath = path.resolve(this._modulesPath, path.relative(this._srcPath, path.dirname(fileMeta.filePath)), moduleClassName);
188
293
 
189
294
  result.sources.push({
190
- filePath: fileMeta.filePath,
295
+ filePath: fileMeta.filePath.replace(/\.[^.]*$/, ""),
191
296
  name: cls.exportedName,
192
297
  module: {
193
298
  filePath: moduleFilePath,
@@ -202,7 +307,7 @@ export class SdCliNgModuleGenerator {
202
307
  filePath: moduleFilePath,
203
308
  name: moduleClassName,
204
309
  source: {
205
- filePath: fileMeta.filePath,
310
+ filePath: fileMeta.filePath.replace(/\.[^.]*$/, ""),
206
311
  name: cls.exportedName
207
312
  },
208
313
  selector: "selector" in cls.target.ngDecl ? cls.target.ngDecl.selector : undefined,
@@ -218,9 +323,9 @@ export class SdCliNgModuleGenerator {
218
323
  private _getGenNgModuleDefs(bbModules: IBbNgModuleDef[], presetModules: ITsNgPresetModuleDef[], presetSources: ITsNgPresetSourceDef[]): ITsGenNgModuleDef[] {
219
324
  const result: ITsGenNgModuleDef[] = [];
220
325
 
221
- for (const el of presetSources) {
326
+ for (const presetSource of presetSources) {
222
327
  const imports: TSdCliMetaRef[] = [];
223
- for (const imp of el.imports) {
328
+ for (const imp of presetSource.imports) {
224
329
  if ("moduleName" in imp) {
225
330
  for (const mod of bbModules) {
226
331
  if (mod.exports.some((item) => item.moduleName === imp.moduleName && item.name === imp.name)) {
@@ -240,8 +345,8 @@ export class SdCliNgModuleGenerator {
240
345
  }
241
346
  }
242
347
 
243
- if (el.template !== undefined) {
244
- const templateDOM = new JSDOM(el.template);
348
+ if (presetSource.template !== undefined) {
349
+ const templateDOM = new JSDOM(presetSource.template);
245
350
 
246
351
  for (const mod of bbModules) {
247
352
  for (const selector of mod.selectors) {
@@ -257,7 +362,7 @@ export class SdCliNgModuleGenerator {
257
362
  }
258
363
 
259
364
  for (const pipeName of mod.pipeNames) {
260
- if (new RegExp("| *" + pipeName + "[^\\w]").test(el.template)) {
365
+ if (new RegExp("| *" + pipeName + "[^\\w]").test(presetSource.template)) {
261
366
  imports.push({ moduleName: mod.moduleName, name: mod.name, __TSdCliMetaRef__: "__TSdCliMetaRef__" });
262
367
  }
263
368
  }
@@ -277,7 +382,7 @@ export class SdCliNgModuleGenerator {
277
382
  }
278
383
 
279
384
  if (mod.pipeName !== undefined) {
280
- if (new RegExp("| *" + mod.pipeName + "[^\\w]").test(el.template)) {
385
+ if (new RegExp("| *" + mod.pipeName + "[^\\w]").test(presetSource.template)) {
281
386
  imports.push({ filePath: mod.filePath, name: mod.name, __TSdCliMetaRef__: "__TSdCliMetaRef__" });
282
387
  }
283
388
  }
@@ -285,11 +390,11 @@ export class SdCliNgModuleGenerator {
285
390
  }
286
391
 
287
392
  result.push({
288
- filePath: el.module.filePath,
289
- name: el.module.name,
393
+ filePath: presetSource.module.filePath,
394
+ name: presetSource.module.name,
290
395
  imports: imports.distinct(),
291
- exports: el.isProvider ? [] : [{ filePath: el.filePath, name: el.name }],
292
- providers: el.isProvider ? [{ filePath: el.filePath, name: el.name }] : []
396
+ exports: presetSource.isProvider ? [] : [{ filePath: presetSource.filePath, name: presetSource.name }],
397
+ providers: presetSource.isProvider ? [{ filePath: presetSource.filePath, name: presetSource.name }] : []
293
398
  });
294
399
  }
295
400