@simplysm/sd-cli 6.0.28 → 6.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 (70) 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 +106 -57
  4. package/dist-node/build-tools/SdCliNgModuleFilesGenerator.js.map +1 -1
  5. package/dist-node/build-tools/metadata/SdAotMetadataReader.js +13 -11
  6. package/dist-node/build-tools/metadata/SdAotMetadataReader.js.map +1 -1
  7. package/dist-node/build-tools/metadata/SdIvyMetadataReader.js +23 -6
  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 +74 -74
  14. package/dist-node/builders/SdCliNgClientBuilder.js.map +1 -1
  15. package/dist-node/builders/SdCliNgLibraryBuilder.js +49 -45
  16. package/dist-node/builders/SdCliNgLibraryBuilder.js.map +1 -1
  17. package/dist-node/builders/SdCliServerBuilder.js +33 -23
  18. package/dist-node/builders/SdCliServerBuilder.js.map +1 -1
  19. package/dist-node/builders/SdCliTypescriptBuilder.js +75 -65
  20. package/dist-node/builders/SdCliTypescriptBuilder.js.map +1 -1
  21. package/dist-node/entry-points/SdCliProject.js +17 -17
  22. package/dist-node/entry-points/SdCliProject.js.map +1 -1
  23. package/dist-node/packages/SdCliClientPackage.js +6 -4
  24. package/dist-node/packages/SdCliClientPackage.js.map +1 -1
  25. package/dist-node/packages/SdCliLibraryPackage.js +5 -3
  26. package/dist-node/packages/SdCliLibraryPackage.js.map +1 -1
  27. package/dist-node/packages/SdCliServerPackage.js +4 -3
  28. package/dist-node/packages/SdCliServerPackage.js.map +1 -1
  29. package/dist-node/utils/SdWebpackUtil.js +33 -13
  30. package/dist-node/utils/SdWebpackUtil.js.map +1 -1
  31. package/dist-node/workers/client-ng-gen-worker.js +54 -0
  32. package/dist-node/workers/client-ng-gen-worker.js.map +1 -0
  33. package/dist-node/workers/lib-build-worker.js +7 -6
  34. package/dist-node/workers/lib-build-worker.js.map +1 -1
  35. package/dist-types/build-tools/metadata/SdIvyMetadataReader.d.ts +1 -0
  36. package/dist-types/build-tools/metadata/SdMetadataError.d.ts +6 -0
  37. package/dist-types/build-tools/metadata/SdMyMetadataReader.d.ts +1 -0
  38. package/dist-types/builders/SdCliNgClientBuilder.d.ts +6 -2
  39. package/dist-types/builders/SdCliNgLibraryBuilder.d.ts +3 -2
  40. package/dist-types/builders/SdCliServerBuilder.d.ts +2 -1
  41. package/dist-types/builders/SdCliTypescriptBuilder.d.ts +5 -5
  42. package/dist-types/commons.d.ts +4 -1
  43. package/dist-types/entry-points/SdCliProject.d.ts +5 -1
  44. package/dist-types/packages/SdCliClientPackage.d.ts +2 -1
  45. package/dist-types/packages/SdCliLibraryPackage.d.ts +2 -1
  46. package/dist-types/packages/SdCliServerPackage.d.ts +2 -1
  47. package/dist-types/utils/SdWebpackUtil.d.ts +1 -1
  48. package/dist-types/workers/client-ng-gen-worker.d.ts +1 -0
  49. package/package.json +7 -7
  50. package/schema/schema.json +72 -1
  51. package/sd-tsconfig.node.json +2 -1
  52. package/src/bin/sd-cli.ts +19 -4
  53. package/src/build-tools/SdCliNgModuleFilesGenerator.ts +122 -70
  54. package/src/build-tools/metadata/SdAotMetadataReader.ts +4 -1
  55. package/src/build-tools/metadata/SdIvyMetadataReader.ts +27 -4
  56. package/src/build-tools/metadata/SdMetadataError.ts +7 -0
  57. package/src/build-tools/metadata/SdMyMetadataReader.ts +25 -3
  58. package/src/builders/SdCliNgClientBuilder.ts +123 -66
  59. package/src/builders/SdCliNgLibraryBuilder.ts +79 -42
  60. package/src/builders/SdCliServerBuilder.ts +48 -19
  61. package/src/builders/SdCliTypescriptBuilder.ts +102 -78
  62. package/src/commons.ts +2 -1
  63. package/src/entry-points/SdCliProject.ts +22 -14
  64. package/src/packages/SdCliClientPackage.ts +5 -3
  65. package/src/packages/SdCliLibraryPackage.ts +4 -2
  66. package/src/packages/SdCliServerPackage.ts +4 -3
  67. package/src/utils/SdWebpackUtil.ts +42 -22
  68. package/src/workers/client-ng-gen-worker.ts +60 -0
  69. package/src/workers/lib-build-worker.ts +7 -6
  70. package/tsconfig.json +2 -1
@@ -1,5 +1,5 @@
1
1
  import { ISdPackageBuildResult } from "../commons";
2
- import { NeverEntryError, ObjectUtil, StringUtil } from "@simplysm/sd-core-common";
2
+ import { NeverEntryError, ObjectUtil, SdError, StringUtil } from "@simplysm/sd-core-common";
3
3
  import * as ts from "typescript";
4
4
  import { FsUtil, PathUtil } from "@simplysm/sd-core-node";
5
5
  import * as path from "path";
@@ -25,7 +25,9 @@ import {
25
25
  } from "./metadata/SdAotMetadataReader";
26
26
  import { IIvyNgModuleMetadata, SdIvyMetadataReader, TIvyNgMetadata } from "./metadata/SdIvyMetadataReader";
27
27
  import { NgtscProgram } from "@angular/compiler-cli";
28
+ import { SdMetadataError } from "./metadata/SdMetadataError";
28
29
 
30
+ // TODO: LazyPage??
29
31
  export class SdCliNgModuleFilesGenerator {
30
32
  private readonly _fileCache = new Map<string, TFileCache>();
31
33
 
@@ -57,61 +59,86 @@ export class SdCliNgModuleFilesGenerator {
57
59
 
58
60
  let changed = false;
59
61
  for (const dirtyFilePath of dirtyFilePaths) {
60
- if (PathUtil.isChildPath(dirtyFilePath, this._moduleDistDirPath)) continue;
62
+ try {
63
+ if (PathUtil.isChildPath(dirtyFilePath, this._moduleDistDirPath)) continue;
61
64
 
62
- this._fileCache.delete(dirtyFilePath);
65
+ this._fileCache.delete(dirtyFilePath);
63
66
 
64
- // 내 소스코드
65
- if (dirtyFilePath.endsWith(".ts") && !dirtyFilePath.endsWith(".d.ts")) {
66
- const sourceFile = this._program.getSourceFile(dirtyFilePath);
67
- if (!sourceFile) throw new NeverEntryError();
68
-
69
- this._fileCache.set(dirtyFilePath, {
70
- type: "my",
71
- sourceFile,
72
- metadata: new SdMyMetadataReader(sourceFile, this._program).getMetadatas()
73
- });
74
- changed = true;
75
- }
76
-
77
- // AOT 라이브러리
78
- else if (dirtyFilePath.endsWith(".d.ts")) {
79
- const sourceFile = this._program.getSourceFile(dirtyFilePath);
80
- if (!sourceFile) throw new NeverEntryError();
81
-
82
- // from METADATA
83
- const metadataFilePath = dirtyFilePath.replace(/\.d\.ts$/, ".metadata.json");
84
- const sdMetadataFilePath = dirtyFilePath.replace(/\.d\.ts$/, ".sd-metadata.json");
85
-
86
- if (FsUtil.exists(metadataFilePath)) {
87
- const metadataRoot = await FsUtil.readJsonAsync(metadataFilePath);
67
+ // 내 소스코드
68
+ if (dirtyFilePath.endsWith(".ts") && !dirtyFilePath.endsWith(".d.ts")) {
69
+ const sourceFile = this._program.getSourceFile(dirtyFilePath);
70
+ if (!sourceFile) { // 삭제된 파일
71
+ changed = true;
72
+ continue;
73
+ }
88
74
 
89
75
  this._fileCache.set(dirtyFilePath, {
90
- type: "aot",
76
+ type: "my",
91
77
  sourceFile,
92
- ...new SdAotMetadataReader(metadataRoot).getMetadatas()
78
+ metadata: new SdMyMetadataReader(sourceFile, this._program).getMetadatas()
93
79
  });
94
80
  changed = true;
95
81
  }
96
- else if (FsUtil.exists(sdMetadataFilePath)) {
97
- const metadataRoot = await FsUtil.readJsonAsync(sdMetadataFilePath);
98
82
 
99
- this._fileCache.set(dirtyFilePath, {
100
- type: "aot",
101
- sourceFile,
102
- ...new SdAotMetadataReader(metadataRoot).getMetadatas()
83
+ // AOT 라이브러리
84
+ else if (dirtyFilePath.endsWith(".d.ts")) {
85
+ const sourceFile = this._program.getSourceFile(dirtyFilePath);
86
+ if (!sourceFile) { // 삭제된 파일
87
+ changed = true;
88
+ continue;
89
+ }
90
+
91
+ // from METADATA
92
+ const metadataFilePath = dirtyFilePath.replace(/\.d\.ts$/, ".metadata.json");
93
+ const sdMetadataFilePath = dirtyFilePath.replace(/\.d\.ts$/, ".sd-metadata.json");
94
+
95
+ if (FsUtil.exists(metadataFilePath)) {
96
+ const metadataRoot = await FsUtil.readJsonAsync(metadataFilePath);
97
+
98
+ this._fileCache.set(dirtyFilePath, {
99
+ type: "aot",
100
+ sourceFile,
101
+ ...new SdAotMetadataReader(metadataRoot).getMetadatas()
102
+ });
103
+ changed = true;
104
+ }
105
+ else if (FsUtil.exists(sdMetadataFilePath)) {
106
+ const metadataRoot = await FsUtil.readJsonAsync(sdMetadataFilePath);
107
+
108
+ this._fileCache.set(dirtyFilePath, {
109
+ type: "aot",
110
+ sourceFile,
111
+ ...new SdAotMetadataReader(metadataRoot).getMetadatas()
112
+ });
113
+ changed = true;
114
+ }
115
+
116
+ // from IVY
117
+ else {
118
+ this._fileCache.set(dirtyFilePath, {
119
+ type: "ivy",
120
+ sourceFile,
121
+ ...new SdIvyMetadataReader(sourceFile, ngProgram).getMetadatas()
122
+ });
123
+ changed = true;
124
+ }
125
+ }
126
+ }
127
+ catch (err) {
128
+ if (err instanceof SdMetadataError) {
129
+ result.push({
130
+ filePath: err.fileName,
131
+ severity: "error",
132
+ message: err.message
103
133
  });
104
- changed = true;
105
134
  }
106
-
107
- // from IVY
108
135
  else {
109
- this._fileCache.set(dirtyFilePath, {
110
- type: "ivy",
111
- sourceFile,
112
- ...new SdIvyMetadataReader(sourceFile, ngProgram).getMetadatas()
113
- });
114
- changed = true;
136
+ if (err instanceof Error) {
137
+ throw new SdError(err, "'" + dirtyFilePath + "' 메타데이터 로딩중 에러");
138
+ }
139
+ else {
140
+ throw err;
141
+ }
115
142
  }
116
143
  }
117
144
  }
@@ -120,29 +147,49 @@ export class SdCliNgModuleFilesGenerator {
120
147
  }
121
148
 
122
149
  public async generateAsync(): Promise<{ result: ISdPackageBuildResult[]; changedFilePaths: string[] }> {
123
- let moduleInfoMap = this._getModuleInfoMap();
124
- moduleInfoMap = this._mergeCircularModuleInfoMap(moduleInfoMap);
125
-
126
- const routingModuleMap = await this._getRoutingModuleInfoMapAsync();
127
-
128
- const changedFilePaths = [
129
- ...await this._writeNgModuleFileAsync(moduleInfoMap),
130
- ...await this._writeRoutingModuleFileAsync(moduleInfoMap, routingModuleMap)
131
- ];
132
-
133
- const genFilePaths = [...moduleInfoMap.keys(), ...routingModuleMap.keys()].distinct();
134
- const delFilePaths = (await FsUtil.globAsync(path.resolve(this._moduleDistDirPath, "**", "*")))
135
- .filter((item) => (
136
- !genFilePaths.includes(PathUtil.posix(item))
137
- && !genFilePaths.some((item1) => PathUtil.isChildPath(item1, item))
138
- ))
139
- .map((item) => PathUtil.posix(item));
140
- for (const delFilePath of delFilePaths) {
141
- changedFilePaths.push(delFilePath);
142
- await FsUtil.removeAsync(delFilePath);
143
- }
150
+ try {
151
+ let moduleInfoMap = this._getModuleInfoMap();
152
+ moduleInfoMap = this._mergeCircularModuleInfoMap(moduleInfoMap);
153
+
154
+ const routingModuleMap = await this._getRoutingModuleInfoMapAsync();
155
+
156
+ const changedFilePaths = [
157
+ ...await this._writeNgModuleFileAsync(moduleInfoMap),
158
+ ...await this._writeRoutingModuleFileAsync(moduleInfoMap, routingModuleMap)
159
+ ];
160
+
161
+ const genFilePaths = [...moduleInfoMap.keys(), ...routingModuleMap.keys()].distinct();
162
+ const delFilePaths = (await FsUtil.globAsync(path.resolve(this._moduleDistDirPath, "**", "*")))
163
+ .filter((item) => (
164
+ !genFilePaths.includes(PathUtil.posix(item))
165
+ && !genFilePaths.some((item1) => PathUtil.isChildPath(item1, item))
166
+ ))
167
+ .map((item) => PathUtil.posix(item));
168
+ for (const delFilePath of delFilePaths) {
169
+ changedFilePaths.push(delFilePath);
170
+ await FsUtil.removeAsync(delFilePath);
171
+ }
144
172
 
145
- return { result: [], changedFilePaths: changedFilePaths.distinct() };
173
+ return { result: [], changedFilePaths: changedFilePaths.distinct() };
174
+ }
175
+ catch (err) {
176
+ if (err instanceof SdMetadataError) {
177
+ return {
178
+ result: [{
179
+ filePath: err.fileName,
180
+ severity: "error",
181
+ message: err.message
182
+ }],
183
+ changedFilePaths: []
184
+ };
185
+ }
186
+ else if (err instanceof Error) {
187
+ throw new SdError(err, "메타데이터 생성중 에러");
188
+ }
189
+ else {
190
+ throw err;
191
+ }
192
+ }
146
193
  }
147
194
 
148
195
  // region NgModule
@@ -512,7 +559,10 @@ export class ${moduleInfo.className} {
512
559
  const expFilePath = this._getResolvedFilePath(filePath, moduleName);
513
560
  if (StringUtil.isNullOrEmpty(expFilePath)) throw new NeverEntryError();
514
561
  const expFileCache = this._fileCache.get(expFilePath);
515
- if (!expFileCache || expFileCache.type !== "aot") throw new NeverEntryError();
562
+ if (!expFileCache) {
563
+ throw new SdMetadataError(filePath, `'${filePath}'에서 '${moduleName}'에 대한 파일 '${expFilePath}'을 찾을 수 없습니다.`);
564
+ }
565
+ if (expFileCache.type !== "aot") throw new NeverEntryError();
516
566
  const newExp = expFileCache.exports.single((item) => (item.as ?? item.name) === targetName);
517
567
  if (!newExp) throw new NeverEntryError();
518
568
 
@@ -612,7 +662,7 @@ export class ${moduleInfo.className} {
612
662
  if (fileCache.type !== "my") continue;
613
663
 
614
664
  for (const metadata of fileCache.metadata) {
615
- if (metadata.type !== "Component" || !metadata.className.endsWith("Page")) continue;
665
+ if (!metadata.className.endsWith("Page")) continue;
616
666
 
617
667
  const newRoutingModuleInfo: IGenRoutingModuleInfo = {
618
668
  path: "",
@@ -681,7 +731,6 @@ export class ${moduleInfo.className} {
681
731
  const moduleImportTexts: string[] = [];
682
732
 
683
733
  const pageFilePath = this._getModuleModuleImportName(path.dirname(distFilePath), routingModuleInfo.page.moduleFilePath);
684
- moduleImportTexts.push(`import { ${routingModuleInfo.page.name} } from "${pageFilePath}";`);
685
734
 
686
735
  let content: string;
687
736
  if (path.basename(distFilePath) === "_routes.ts") {
@@ -697,6 +746,7 @@ export const routes: Routes = [
697
746
  else {
698
747
  moduleImportTexts.push(`import { NgModule } from "@angular/core";`);
699
748
  moduleImportTexts.push(`import { RouterModule } from "@angular/router";`);
749
+ moduleImportTexts.push(`import { ${routingModuleInfo.page.name} } from "${pageFilePath}";`);
700
750
 
701
751
  const thisPageModuleInfoEntry = Array.from(moduleInfoMap.entries())
702
752
  .single((item) => item[1].exports.some((exp) => exp.moduleFilePath === routingModuleInfo.page.moduleFilePath && exp.name === routingModuleInfo.page.name));
@@ -745,7 +795,9 @@ export class ${routingModuleInfo.page.name}RoutingModule {
745
795
  private _getRoutingModuleText(routingModuleInfo: IGenRoutingModuleInfo, distFilePath: string): string {
746
796
  let content = "{\r\n";
747
797
  content += ` path: "${routingModuleInfo.path}",\r\n`;
748
- content += ` component: ${routingModuleInfo.page.name},\r\n`;
798
+ if (path.basename(distFilePath) !== "_routes.ts") {
799
+ content += ` component: ${routingModuleInfo.page.name},\r\n`;
800
+ }
749
801
  if (routingModuleInfo.children.length > 0) {
750
802
  content += ` children: [\r\n`;
751
803
  for (const child of routingModuleInfo.children) {
@@ -4,6 +4,7 @@ import {
4
4
  isMetadataGlobalReferenceExpression,
5
5
  isMetadataImportedSymbolReferenceExpression,
6
6
  isMetadataSymbolicCallExpression,
7
+ isMetadataSymbolicIfExpression,
7
8
  isMetadataSymbolicReferenceExpression,
8
9
  MetadataEntry,
9
10
  MetadataObject,
@@ -18,7 +19,6 @@ export class SdAotMetadataReader {
18
19
 
19
20
  public getMetadatas(): IAotModuleDefMetadata {
20
21
  if (StringUtil.isNullOrEmpty(this._metadataRoot.importAs)) {
21
- console.log(this._metadataRoot);
22
22
  throw new NeverEntryError();
23
23
  }
24
24
 
@@ -258,6 +258,9 @@ export class SdAotMetadataReader {
258
258
  );
259
259
  result.push(...subProvRefTargets);
260
260
  }
261
+ else if (isMetadataSymbolicIfExpression(refTarget.metadata)) {
262
+ result.push(...this._getProviderReferenceTargets([refTarget.metadata.thenExpression, refTarget.metadata.elseExpression]));
263
+ }
261
264
  else {
262
265
  throw new NeverEntryError();
263
266
  }
@@ -7,6 +7,8 @@ import { Reference } from "@angular/compiler-cli/src/ngtsc/imports";
7
7
  import { NgtscProgram } from "@angular/compiler-cli";
8
8
  import * as path from "path";
9
9
  import { INpmConfig } from "../../commons";
10
+ import { IMyModuleImport } from "./SdMyMetadataReader";
11
+ import { SdMetadataError } from "./SdMetadataError";
10
12
 
11
13
  export class SdIvyMetadataReader {
12
14
  private readonly _typeChecker: ts.TypeChecker;
@@ -76,7 +78,7 @@ export class SdIvyMetadataReader {
76
78
  return result;
77
79
  }
78
80
 
79
- private _getAliasedModuleImportInfo(identifier: ts.Identifier): IIvyModuleImport {
81
+ private _getAliasedModuleImportInfo(identifier: ts.Identifier): IMyModuleImport {
80
82
  const moduleSymbol = this._typeChecker.getSymbolAtLocation(identifier);
81
83
  if (!moduleSymbol) throw new NeverEntryError();
82
84
  if (moduleSymbol.valueDeclaration) {
@@ -86,9 +88,18 @@ export class SdIvyMetadataReader {
86
88
  else {
87
89
  const aliasedTargetSymbol = this._typeChecker.getAliasedSymbol(moduleSymbol);
88
90
  const declarations = aliasedTargetSymbol.getDeclarations();
89
- if (!declarations) throw new NeverEntryError();
90
- const moduleFilePaths = declarations.map((item) => PathUtil.posix(item.getSourceFile().fileName)).distinct();
91
- if (moduleFilePaths.length !== 1) throw new NeverEntryError();
91
+ if (!declarations || declarations.length < 1) {
92
+ throw new SdMetadataError(this._sourceFile.fileName, `'${identifier.text}'를 찾을 수 없습니다.`);
93
+ }
94
+ const moduleFilePaths = declarations
95
+ .filter((item) => (
96
+ this._hasParentsOrMe(item, (item1) => Boolean(item1.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.ExportKeyword)))
97
+ ))
98
+ .map((item) => PathUtil.posix(item.getSourceFile().fileName))
99
+ .distinct();
100
+ if (moduleFilePaths.length !== 1) {
101
+ throw new SdMetadataError(this._sourceFile.fileName, `'${identifier.text}'의 경로가 중복되었습니다.\n${moduleFilePaths.join("\n")}`);
102
+ }
92
103
 
93
104
  const moduleFilePath = moduleFilePaths[0];
94
105
 
@@ -96,6 +107,18 @@ export class SdIvyMetadataReader {
96
107
  }
97
108
  }
98
109
 
110
+ private _hasParentsOrMe(node: ts.Node, fn: (node1: ts.Node) => boolean): boolean {
111
+ let currNode = node as ts.Node | undefined;
112
+ while (true) {
113
+ if (!currNode) return false;
114
+
115
+ const result = fn(currNode);
116
+ if (result) return true;
117
+
118
+ currNode = currNode.parent as ts.Node | undefined;
119
+ }
120
+ }
121
+
99
122
  private getPackageName(): string {
100
123
  let cursorDirPath = path.dirname(this._sourceFile.fileName);
101
124
  while (true) {
@@ -0,0 +1,7 @@
1
+ import { SdError } from "@simplysm/sd-core-common";
2
+
3
+ export class SdMetadataError extends SdError {
4
+ public constructor(public fileName: string, public message: string) {
5
+ super(message);
6
+ }
7
+ }
@@ -1,6 +1,7 @@
1
1
  import * as ts from "typescript";
2
2
  import { NeverEntryError } from "@simplysm/sd-core-common";
3
3
  import { PathUtil } from "@simplysm/sd-core-node";
4
+ import { SdMetadataError } from "./SdMetadataError";
4
5
 
5
6
  export class SdMyMetadataReader {
6
7
  private readonly _typeChecker: ts.TypeChecker;
@@ -150,9 +151,18 @@ export class SdMyMetadataReader {
150
151
  else {
151
152
  const aliasedTargetSymbol = this._typeChecker.getAliasedSymbol(moduleSymbol);
152
153
  const declarations = aliasedTargetSymbol.getDeclarations();
153
- if (!declarations) throw new NeverEntryError();
154
- const moduleFilePaths = declarations.map((item) => PathUtil.posix(item.getSourceFile().fileName)).distinct();
155
- if (moduleFilePaths.length !== 1) throw new NeverEntryError();
154
+ if (!declarations || declarations.length < 1) {
155
+ throw new SdMetadataError(this._sourceFile.fileName, `'${identifier.text}'를 찾을 수 없습니다.`);
156
+ }
157
+ const moduleFilePaths = declarations
158
+ .filter((item) => (
159
+ this._hasParentsOrMe(item, (item1) => Boolean(item1.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.ExportKeyword)))
160
+ ))
161
+ .map((item) => PathUtil.posix(item.getSourceFile().fileName))
162
+ .distinct();
163
+ if (moduleFilePaths.length !== 1) {
164
+ throw new SdMetadataError(this._sourceFile.fileName, `'${identifier.text}'의 경로가 중복되었습니다.\n${moduleFilePaths.join("\n")}`);
165
+ }
156
166
 
157
167
  const moduleFilePath = moduleFilePaths[0];
158
168
 
@@ -160,6 +170,18 @@ export class SdMyMetadataReader {
160
170
  }
161
171
  }
162
172
 
173
+ private _hasParentsOrMe(node: ts.Node, fn: (node1: ts.Node) => boolean): boolean {
174
+ let currNode = node as ts.Node | undefined;
175
+ while (true) {
176
+ if (!currNode) return false;
177
+
178
+ const result = fn(currNode);
179
+ if (result) return true;
180
+
181
+ currNode = currNode.parent as ts.Node | undefined;
182
+ }
183
+ }
184
+
163
185
  private _getNgModuleDecoratorParam(decExp: ts.CallExpression): { exports: IMyModuleImport[]; providers: IMyModuleImport[] } {
164
186
  const result: { exports: IMyModuleImport[]; providers: IMyModuleImport[] } = {
165
187
  exports: [],