@gqlkit-ts/cli 0.4.1 → 0.5.1

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 (31) hide show
  1. package/README.md +3 -1
  2. package/dist/commands/gen.d.ts +11 -0
  3. package/dist/commands/gen.d.ts.map +1 -1
  4. package/dist/commands/gen.js +11 -2
  5. package/dist/commands/gen.js.map +1 -1
  6. package/dist/config-loader/loader.d.ts +1 -0
  7. package/dist/config-loader/loader.d.ts.map +1 -1
  8. package/dist/config-loader/loader.js +40 -2
  9. package/dist/config-loader/loader.js.map +1 -1
  10. package/dist/gen-orchestrator/orchestrator.d.ts.map +1 -1
  11. package/dist/gen-orchestrator/orchestrator.js +4 -0
  12. package/dist/gen-orchestrator/orchestrator.js.map +1 -1
  13. package/dist/schema-generator/emitter/code-emitter.d.ts.map +1 -1
  14. package/dist/schema-generator/emitter/code-emitter.js +52 -16
  15. package/dist/schema-generator/emitter/code-emitter.js.map +1 -1
  16. package/dist/type-extractor/extractor/type-name-collector.d.ts +6 -0
  17. package/dist/type-extractor/extractor/type-name-collector.d.ts.map +1 -1
  18. package/dist/type-extractor/extractor/type-name-collector.js +54 -18
  19. package/dist/type-extractor/extractor/type-name-collector.js.map +1 -1
  20. package/dist/type-extractor/types/diagnostics.d.ts +1 -1
  21. package/dist/type-extractor/types/diagnostics.d.ts.map +1 -1
  22. package/docs/configuration.md +24 -0
  23. package/docs/index.md +6 -6
  24. package/package.json +1 -1
  25. package/src/commands/gen.ts +12 -2
  26. package/src/config-loader/loader.ts +43 -2
  27. package/src/gen-orchestrator/orchestrator.ts +7 -0
  28. package/src/schema-generator/emitter/code-emitter.ts +86 -21
  29. package/src/type-extractor/extractor/type-name-collector.ts +80 -18
  30. package/src/type-extractor/types/diagnostics.ts +3 -1
  31. /package/docs/schema/{index.md → conventions.md} +0 -0
@@ -1,8 +1,28 @@
1
1
  import ts from "typescript";
2
+ import {
3
+ getSourceLocationFromNode,
4
+ type SourceLocation,
5
+ } from "../../shared/source-location.js";
2
6
  import {
3
7
  isExported,
4
8
  resolveOriginalSymbol,
5
9
  } from "../../shared/typescript-utils.js";
10
+ import type { Diagnostic } from "../types/index.js";
11
+
12
+ /**
13
+ * Tracks location and symbol of a type declaration for duplicate detection.
14
+ */
15
+ interface TypeDeclarationLocation {
16
+ readonly location: SourceLocation;
17
+ readonly symbol: ts.Symbol | null;
18
+ }
19
+
20
+ /**
21
+ * Formats a source location as a human-readable string.
22
+ */
23
+ function formatLocation(location: SourceLocation): string {
24
+ return `${location.file}:${location.line}`;
25
+ }
6
26
 
7
27
  export interface TypeNameCollectionResult {
8
28
  readonly typeNames: ReadonlySet<string>;
@@ -13,6 +33,11 @@ export interface TypeNameCollectionResult {
13
33
  * This allows recognizing `ExternalUser` as `User` in field types.
14
34
  */
15
35
  readonly underlyingSymbolToTypeName: ReadonlyMap<ts.Symbol, string>;
36
+ /**
37
+ * Diagnostics collected during type name collection.
38
+ * Contains errors for duplicate type exports.
39
+ */
40
+ readonly diagnostics: ReadonlyArray<Diagnostic>;
16
41
  }
17
42
 
18
43
  /**
@@ -33,8 +58,51 @@ export function collectDeclaredTypeNames(
33
58
  const typeNames = new Set<string>();
34
59
  const typeSymbols = new Map<string, ts.Symbol>();
35
60
  const underlyingSymbolToTypeName = new Map<ts.Symbol, string>();
61
+ const diagnostics: Diagnostic[] = [];
36
62
  const checker = program.getTypeChecker();
37
63
 
64
+ const typeLocations = new Map<string, TypeDeclarationLocation>();
65
+
66
+ /**
67
+ * Registers a type name and checks for duplicates.
68
+ * Returns true if this is a new type, false if it's a duplicate.
69
+ */
70
+ function registerTypeName(
71
+ name: string,
72
+ location: SourceLocation,
73
+ symbol: ts.Symbol | undefined,
74
+ ): boolean {
75
+ const resolvedSymbol = symbol
76
+ ? resolveOriginalSymbol(symbol, checker)
77
+ : null;
78
+ const existing = typeLocations.get(name);
79
+ if (existing) {
80
+ // Check if both symbols resolve to the same underlying type
81
+ // (e.g., re-exports of the same type from different files)
82
+ if (
83
+ resolvedSymbol &&
84
+ existing.symbol &&
85
+ resolvedSymbol === existing.symbol
86
+ ) {
87
+ // Same underlying type - not a true duplicate, skip silently
88
+ return false;
89
+ }
90
+ diagnostics.push({
91
+ code: "DUPLICATE_TYPE_EXPORT",
92
+ message: `Type '${name}' is exported from multiple files. First defined at ${formatLocation(existing.location)}.`,
93
+ severity: "error",
94
+ location,
95
+ });
96
+ return false;
97
+ }
98
+ typeLocations.set(name, { location, symbol: resolvedSymbol });
99
+ typeNames.add(name);
100
+ if (resolvedSymbol) {
101
+ typeSymbols.set(name, resolvedSymbol);
102
+ }
103
+ return true;
104
+ }
105
+
38
106
  for (const filePath of sourceFiles) {
39
107
  const sourceFile = program.getSourceFile(filePath);
40
108
  if (!sourceFile) continue;
@@ -43,11 +111,11 @@ export function collectDeclaredTypeNames(
43
111
  // Direct type declarations
44
112
  if (ts.isTypeAliasDeclaration(node) && isExported(node)) {
45
113
  const name = node.name.getText(sourceFile);
46
- typeNames.add(name);
114
+ const location = getSourceLocationFromNode(node)!;
47
115
  const symbol = checker.getSymbolAtLocation(node.name);
48
- if (symbol) {
49
- typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
116
+ const isNew = registerTypeName(name, location, symbol);
50
117
 
118
+ if (isNew && symbol) {
51
119
  // For type aliases like `type User = ExternalUser;`,
52
120
  // also track the underlying type's symbol
53
121
  const type = checker.getTypeAtLocation(node.type);
@@ -62,19 +130,15 @@ export function collectDeclaredTypeNames(
62
130
  }
63
131
  if (ts.isInterfaceDeclaration(node) && isExported(node)) {
64
132
  const name = node.name.getText(sourceFile);
65
- typeNames.add(name);
133
+ const location = getSourceLocationFromNode(node)!;
66
134
  const symbol = checker.getSymbolAtLocation(node.name);
67
- if (symbol) {
68
- typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
69
- }
135
+ registerTypeName(name, location, symbol);
70
136
  }
71
137
  if (ts.isEnumDeclaration(node) && isExported(node)) {
72
138
  const name = node.name.getText(sourceFile);
73
- typeNames.add(name);
139
+ const location = getSourceLocationFromNode(node)!;
74
140
  const symbol = checker.getSymbolAtLocation(node.name);
75
- if (symbol) {
76
- typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
77
- }
141
+ registerTypeName(name, location, symbol);
78
142
  }
79
143
 
80
144
  // Re-exports: `export type { ... } from "..."` or `export type * from "..."`
@@ -85,11 +149,9 @@ export function collectDeclaredTypeNames(
85
149
  for (const element of node.exportClause.elements) {
86
150
  // Use the exported name (element.name), not the property name
87
151
  const name = element.name.getText(sourceFile);
88
- typeNames.add(name);
152
+ const location = getSourceLocationFromNode(element)!;
89
153
  const symbol = checker.getSymbolAtLocation(element.name);
90
- if (symbol) {
91
- typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
92
- }
154
+ registerTypeName(name, location, symbol);
93
155
  }
94
156
  }
95
157
  } else if (node.moduleSpecifier) {
@@ -103,8 +165,8 @@ export function collectDeclaredTypeNames(
103
165
  const name = exp.getName();
104
166
  // Check if the export is a type (not a value)
105
167
  if (isTypeExport(exp)) {
106
- typeNames.add(name);
107
- typeSymbols.set(name, resolveOriginalSymbol(exp, checker));
168
+ const location = getSourceLocationFromNode(node)!;
169
+ registerTypeName(name, location, exp);
108
170
  }
109
171
  }
110
172
  }
@@ -113,7 +175,7 @@ export function collectDeclaredTypeNames(
113
175
  });
114
176
  }
115
177
 
116
- return { typeNames, typeSymbols, underlyingSymbolToTypeName };
178
+ return { typeNames, typeSymbols, underlyingSymbolToTypeName, diagnostics };
117
179
  }
118
180
 
119
181
  /**
@@ -28,6 +28,7 @@ export type DiagnosticCode =
28
28
  | "CONFLICTING_SCALAR_TYPE"
29
29
  | "INVALID_DEFINE_CALL"
30
30
  | "CONFIG_SYNTAX_ERROR"
31
+ | "CONFIG_FILE_NOT_FOUND"
31
32
  | "CONFIG_MISSING_PROPERTY"
32
33
  | "CONFIG_INVALID_TYPE"
33
34
  | "CONFIG_INVALID_PATH"
@@ -95,7 +96,8 @@ export type DiagnosticCode =
95
96
  | "TYPENAME_FIELD_STRUCTURE_MISMATCH"
96
97
  | "DUPLICATE_TYPENAME_VALUE"
97
98
  | "IGNORE_FIELD_NOT_FOUND"
98
- | "IGNORE_ALL_FIELDS";
99
+ | "IGNORE_ALL_FIELDS"
100
+ | "DUPLICATE_TYPE_EXPORT";
99
101
 
100
102
  export interface Diagnostic {
101
103
  readonly code: DiagnosticCode;
File without changes