@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
@@ -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,49 +59,81 @@ 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
- if (FsUtil.exists(metadataFilePath)) {
85
- 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
+ }
86
74
 
87
75
  this._fileCache.set(dirtyFilePath, {
88
- type: "aot",
76
+ type: "my",
89
77
  sourceFile,
90
- ...new SdAotMetadataReader(metadataRoot).getMetadatas()
78
+ metadata: new SdMyMetadataReader(sourceFile, this._program).getMetadatas()
91
79
  });
92
80
  changed = true;
93
81
  }
94
82
 
95
- // from IVY
96
- else {
97
- this._fileCache.set(dirtyFilePath, {
98
- type: "ivy",
99
- sourceFile,
100
- ...new SdIvyMetadataReader(sourceFile, ngProgram).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
101
133
  });
102
- changed = true;
134
+ }
135
+ else {
136
+ throw new SdError(err, "'" + dirtyFilePath + "' 메타데이터 로딩중 에러");
103
137
  }
104
138
  }
105
139
  }
@@ -108,29 +142,48 @@ export class SdCliNgModuleFilesGenerator {
108
142
  }
109
143
 
110
144
  public async generateAsync(): Promise<{ result: ISdPackageBuildResult[]; changedFilePaths: string[] }> {
111
- let moduleInfoMap = this._getModuleInfoMap();
112
- moduleInfoMap = this._mergeCircularModuleInfoMap(moduleInfoMap);
113
-
114
- const routingModuleMap = await this._getRoutingModuleInfoMapAsync();
115
-
116
- const changedFilePaths = [
117
- ...await this._writeNgModuleFileAsync(moduleInfoMap),
118
- ...await this._writeRoutingModuleFileAsync(moduleInfoMap, routingModuleMap)
119
- ];
120
-
121
- const genFilePaths = [...moduleInfoMap.keys(), ...routingModuleMap.keys()].distinct();
122
- const delFilePaths = (await FsUtil.globAsync(path.resolve(this._moduleDistDirPath, "**", "*")))
123
- .filter((item) => (
124
- !genFilePaths.includes(PathUtil.posix(item))
125
- && !genFilePaths.some((item1) => PathUtil.isChildPath(item1, item))
126
- ))
127
- .map((item) => PathUtil.posix(item));
128
- for (const delFilePath of delFilePaths) {
129
- changedFilePaths.push(delFilePath);
130
- await FsUtil.removeAsync(delFilePath);
131
- }
145
+ try {
146
+ let moduleInfoMap = this._getModuleInfoMap();
147
+ moduleInfoMap = this._mergeCircularModuleInfoMap(moduleInfoMap);
148
+
149
+ const routingModuleMap = await this._getRoutingModuleInfoMapAsync();
150
+
151
+ const changedFilePaths = [
152
+ ...await this._writeNgModuleFileAsync(moduleInfoMap),
153
+ ...await this._writeRoutingModuleFileAsync(moduleInfoMap, routingModuleMap)
154
+ ];
155
+
156
+ const genFilePaths = [...moduleInfoMap.keys(), ...routingModuleMap.keys()].distinct();
157
+ const delFilePaths = (await FsUtil.globAsync(path.resolve(this._moduleDistDirPath, "**", "*")))
158
+ .filter((item) => (
159
+ !genFilePaths.includes(PathUtil.posix(item))
160
+ && !genFilePaths.some((item1) => PathUtil.isChildPath(item1, item))
161
+ ))
162
+ .map((item) => PathUtil.posix(item));
163
+ for (const delFilePath of delFilePaths) {
164
+ changedFilePaths.push(delFilePath);
165
+ await FsUtil.removeAsync(delFilePath);
166
+ }
132
167
 
133
- return { result: [], changedFilePaths: changedFilePaths.distinct() };
168
+ return { result: [], changedFilePaths: changedFilePaths.distinct() };
169
+ }
170
+ catch (err) {
171
+ if (err instanceof SdMetadataError) {
172
+ return {
173
+ result: [
174
+ {
175
+ filePath: err.fileName,
176
+ severity: "error",
177
+ message: err.message
178
+ }
179
+ ],
180
+ changedFilePaths: []
181
+ };
182
+ }
183
+ else {
184
+ throw new SdError(err, "메타데이터 생성중 에러");
185
+ }
186
+ }
134
187
  }
135
188
 
136
189
  // region NgModule
@@ -500,7 +553,10 @@ export class ${moduleInfo.className} {
500
553
  const expFilePath = this._getResolvedFilePath(filePath, moduleName);
501
554
  if (StringUtil.isNullOrEmpty(expFilePath)) throw new NeverEntryError();
502
555
  const expFileCache = this._fileCache.get(expFilePath);
503
- if (!expFileCache || expFileCache.type !== "aot") throw new NeverEntryError();
556
+ if (!expFileCache) {
557
+ throw new SdMetadataError(filePath, `파일 '${expFilePath}'을 찾을 수 없습니다.`);
558
+ }
559
+ if (expFileCache.type !== "aot") throw new NeverEntryError();
504
560
  const newExp = expFileCache.exports.single((item) => (item.as ?? item.name) === targetName);
505
561
  if (!newExp) throw new NeverEntryError();
506
562
 
@@ -600,7 +656,7 @@ export class ${moduleInfo.className} {
600
656
  if (fileCache.type !== "my") continue;
601
657
 
602
658
  for (const metadata of fileCache.metadata) {
603
- if (metadata.type !== "Component" || !metadata.className.endsWith("Page")) continue;
659
+ if (!metadata.className.endsWith("Page")) continue;
604
660
 
605
661
  const newRoutingModuleInfo: IGenRoutingModuleInfo = {
606
662
  path: "",
@@ -669,7 +725,6 @@ export class ${moduleInfo.className} {
669
725
  const moduleImportTexts: string[] = [];
670
726
 
671
727
  const pageFilePath = this._getModuleModuleImportName(path.dirname(distFilePath), routingModuleInfo.page.moduleFilePath);
672
- moduleImportTexts.push(`import { ${routingModuleInfo.page.name} } from "${pageFilePath}";`);
673
728
 
674
729
  let content: string;
675
730
  if (path.basename(distFilePath) === "_routes.ts") {
@@ -685,6 +740,7 @@ export const routes: Routes = [
685
740
  else {
686
741
  moduleImportTexts.push(`import { NgModule } from "@angular/core";`);
687
742
  moduleImportTexts.push(`import { RouterModule } from "@angular/router";`);
743
+ moduleImportTexts.push(`import { ${routingModuleInfo.page.name} } from "${pageFilePath}";`);
688
744
 
689
745
  const thisPageModuleInfoEntry = Array.from(moduleInfoMap.entries())
690
746
  .single((item) => item[1].exports.some((exp) => exp.moduleFilePath === routingModuleInfo.page.moduleFilePath && exp.name === routingModuleInfo.page.name));
@@ -733,7 +789,9 @@ export class ${routingModuleInfo.page.name}RoutingModule {
733
789
  private _getRoutingModuleText(routingModuleInfo: IGenRoutingModuleInfo, distFilePath: string): string {
734
790
  let content = "{\r\n";
735
791
  content += ` path: "${routingModuleInfo.path}",\r\n`;
736
- content += ` component: ${routingModuleInfo.page.name},\r\n`;
792
+ if (path.basename(distFilePath) !== "_routes.ts") {
793
+ content += ` component: ${routingModuleInfo.page.name},\r\n`;
794
+ }
737
795
  if (routingModuleInfo.children.length > 0) {
738
796
  content += ` children: [\r\n`;
739
797
  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,
@@ -17,7 +18,9 @@ export class SdAotMetadataReader {
17
18
  }
18
19
 
19
20
  public getMetadatas(): IAotModuleDefMetadata {
20
- if (StringUtil.isNullOrEmpty(this._metadataRoot.importAs)) throw new NeverEntryError();
21
+ if (StringUtil.isNullOrEmpty(this._metadataRoot.importAs)) {
22
+ throw new NeverEntryError();
23
+ }
21
24
 
22
25
  const result: IAotModuleDefMetadata = {
23
26
  moduleName: this._metadataRoot.importAs,
@@ -186,7 +189,7 @@ export class SdAotMetadataReader {
186
189
  if (dec.arguments && dec.arguments.length !== 0) {
187
190
  const paramObj = dec.arguments[0] as MetadataObject;
188
191
 
189
- if (paramObj.providedIn !== undefined) {
192
+ if (paramObj.providedIn != null) {
190
193
  if (typeof paramObj.providedIn !== "string") throw new NeverEntryError();
191
194
  result.providedIn = paramObj.providedIn;
192
195
  }
@@ -255,6 +258,9 @@ export class SdAotMetadataReader {
255
258
  );
256
259
  result.push(...subProvRefTargets);
257
260
  }
261
+ else if (isMetadataSymbolicIfExpression(refTarget.metadata)) {
262
+ result.push(...this._getProviderReferenceTargets([refTarget.metadata.thenExpression, refTarget.metadata.elseExpression]));
263
+ }
258
264
  else {
259
265
  throw new NeverEntryError();
260
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: [],