@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.
- package/README.md +3 -1
- package/dist/commands/gen.d.ts +11 -0
- package/dist/commands/gen.d.ts.map +1 -1
- package/dist/commands/gen.js +11 -2
- package/dist/commands/gen.js.map +1 -1
- package/dist/config-loader/loader.d.ts +1 -0
- package/dist/config-loader/loader.d.ts.map +1 -1
- package/dist/config-loader/loader.js +40 -2
- package/dist/config-loader/loader.js.map +1 -1
- package/dist/gen-orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/gen-orchestrator/orchestrator.js +4 -0
- package/dist/gen-orchestrator/orchestrator.js.map +1 -1
- package/dist/schema-generator/emitter/code-emitter.d.ts.map +1 -1
- package/dist/schema-generator/emitter/code-emitter.js +52 -16
- package/dist/schema-generator/emitter/code-emitter.js.map +1 -1
- package/dist/type-extractor/extractor/type-name-collector.d.ts +6 -0
- package/dist/type-extractor/extractor/type-name-collector.d.ts.map +1 -1
- package/dist/type-extractor/extractor/type-name-collector.js +54 -18
- package/dist/type-extractor/extractor/type-name-collector.js.map +1 -1
- package/dist/type-extractor/types/diagnostics.d.ts +1 -1
- package/dist/type-extractor/types/diagnostics.d.ts.map +1 -1
- package/docs/configuration.md +24 -0
- package/docs/index.md +6 -6
- package/package.json +1 -1
- package/src/commands/gen.ts +12 -2
- package/src/config-loader/loader.ts +43 -2
- package/src/gen-orchestrator/orchestrator.ts +7 -0
- package/src/schema-generator/emitter/code-emitter.ts +86 -21
- package/src/type-extractor/extractor/type-name-collector.ts +80 -18
- package/src/type-extractor/types/diagnostics.ts +3 -1
- /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
|
-
|
|
114
|
+
const location = getSourceLocationFromNode(node)!;
|
|
47
115
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
48
|
-
|
|
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
|
-
|
|
133
|
+
const location = getSourceLocationFromNode(node)!;
|
|
66
134
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
67
|
-
|
|
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
|
-
|
|
139
|
+
const location = getSourceLocationFromNode(node)!;
|
|
74
140
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
75
|
-
|
|
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
|
-
|
|
152
|
+
const location = getSourceLocationFromNode(element)!;
|
|
89
153
|
const symbol = checker.getSymbolAtLocation(element.name);
|
|
90
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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
|