@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,5 +1,12 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
+
import { getSourceLocationFromNode, } from "../../shared/source-location.js";
|
|
2
3
|
import { isExported, resolveOriginalSymbol, } from "../../shared/typescript-utils.js";
|
|
4
|
+
/**
|
|
5
|
+
* Formats a source location as a human-readable string.
|
|
6
|
+
*/
|
|
7
|
+
function formatLocation(location) {
|
|
8
|
+
return `${location.file}:${location.line}`;
|
|
9
|
+
}
|
|
3
10
|
/**
|
|
4
11
|
* Collects all declared type names from exported type declarations.
|
|
5
12
|
*
|
|
@@ -15,7 +22,42 @@ export function collectDeclaredTypeNames(program, sourceFiles) {
|
|
|
15
22
|
const typeNames = new Set();
|
|
16
23
|
const typeSymbols = new Map();
|
|
17
24
|
const underlyingSymbolToTypeName = new Map();
|
|
25
|
+
const diagnostics = [];
|
|
18
26
|
const checker = program.getTypeChecker();
|
|
27
|
+
const typeLocations = new Map();
|
|
28
|
+
/**
|
|
29
|
+
* Registers a type name and checks for duplicates.
|
|
30
|
+
* Returns true if this is a new type, false if it's a duplicate.
|
|
31
|
+
*/
|
|
32
|
+
function registerTypeName(name, location, symbol) {
|
|
33
|
+
const resolvedSymbol = symbol
|
|
34
|
+
? resolveOriginalSymbol(symbol, checker)
|
|
35
|
+
: null;
|
|
36
|
+
const existing = typeLocations.get(name);
|
|
37
|
+
if (existing) {
|
|
38
|
+
// Check if both symbols resolve to the same underlying type
|
|
39
|
+
// (e.g., re-exports of the same type from different files)
|
|
40
|
+
if (resolvedSymbol &&
|
|
41
|
+
existing.symbol &&
|
|
42
|
+
resolvedSymbol === existing.symbol) {
|
|
43
|
+
// Same underlying type - not a true duplicate, skip silently
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
diagnostics.push({
|
|
47
|
+
code: "DUPLICATE_TYPE_EXPORT",
|
|
48
|
+
message: `Type '${name}' is exported from multiple files. First defined at ${formatLocation(existing.location)}.`,
|
|
49
|
+
severity: "error",
|
|
50
|
+
location,
|
|
51
|
+
});
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
typeLocations.set(name, { location, symbol: resolvedSymbol });
|
|
55
|
+
typeNames.add(name);
|
|
56
|
+
if (resolvedSymbol) {
|
|
57
|
+
typeSymbols.set(name, resolvedSymbol);
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
19
61
|
for (const filePath of sourceFiles) {
|
|
20
62
|
const sourceFile = program.getSourceFile(filePath);
|
|
21
63
|
if (!sourceFile)
|
|
@@ -24,10 +66,10 @@ export function collectDeclaredTypeNames(program, sourceFiles) {
|
|
|
24
66
|
// Direct type declarations
|
|
25
67
|
if (ts.isTypeAliasDeclaration(node) && isExported(node)) {
|
|
26
68
|
const name = node.name.getText(sourceFile);
|
|
27
|
-
|
|
69
|
+
const location = getSourceLocationFromNode(node);
|
|
28
70
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
29
|
-
|
|
30
|
-
|
|
71
|
+
const isNew = registerTypeName(name, location, symbol);
|
|
72
|
+
if (isNew && symbol) {
|
|
31
73
|
// For type aliases like `type User = ExternalUser;`,
|
|
32
74
|
// also track the underlying type's symbol
|
|
33
75
|
const type = checker.getTypeAtLocation(node.type);
|
|
@@ -39,19 +81,15 @@ export function collectDeclaredTypeNames(program, sourceFiles) {
|
|
|
39
81
|
}
|
|
40
82
|
if (ts.isInterfaceDeclaration(node) && isExported(node)) {
|
|
41
83
|
const name = node.name.getText(sourceFile);
|
|
42
|
-
|
|
84
|
+
const location = getSourceLocationFromNode(node);
|
|
43
85
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
44
|
-
|
|
45
|
-
typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
|
|
46
|
-
}
|
|
86
|
+
registerTypeName(name, location, symbol);
|
|
47
87
|
}
|
|
48
88
|
if (ts.isEnumDeclaration(node) && isExported(node)) {
|
|
49
89
|
const name = node.name.getText(sourceFile);
|
|
50
|
-
|
|
90
|
+
const location = getSourceLocationFromNode(node);
|
|
51
91
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
52
|
-
|
|
53
|
-
typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
|
|
54
|
-
}
|
|
92
|
+
registerTypeName(name, location, symbol);
|
|
55
93
|
}
|
|
56
94
|
// Re-exports: `export type { ... } from "..."` or `export type * from "..."`
|
|
57
95
|
if (ts.isExportDeclaration(node) && node.isTypeOnly) {
|
|
@@ -61,11 +99,9 @@ export function collectDeclaredTypeNames(program, sourceFiles) {
|
|
|
61
99
|
for (const element of node.exportClause.elements) {
|
|
62
100
|
// Use the exported name (element.name), not the property name
|
|
63
101
|
const name = element.name.getText(sourceFile);
|
|
64
|
-
|
|
102
|
+
const location = getSourceLocationFromNode(element);
|
|
65
103
|
const symbol = checker.getSymbolAtLocation(element.name);
|
|
66
|
-
|
|
67
|
-
typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
|
|
68
|
-
}
|
|
104
|
+
registerTypeName(name, location, symbol);
|
|
69
105
|
}
|
|
70
106
|
}
|
|
71
107
|
}
|
|
@@ -78,8 +114,8 @@ export function collectDeclaredTypeNames(program, sourceFiles) {
|
|
|
78
114
|
const name = exp.getName();
|
|
79
115
|
// Check if the export is a type (not a value)
|
|
80
116
|
if (isTypeExport(exp)) {
|
|
81
|
-
|
|
82
|
-
|
|
117
|
+
const location = getSourceLocationFromNode(node);
|
|
118
|
+
registerTypeName(name, location, exp);
|
|
83
119
|
}
|
|
84
120
|
}
|
|
85
121
|
}
|
|
@@ -87,7 +123,7 @@ export function collectDeclaredTypeNames(program, sourceFiles) {
|
|
|
87
123
|
}
|
|
88
124
|
});
|
|
89
125
|
}
|
|
90
|
-
return { typeNames, typeSymbols, underlyingSymbolToTypeName };
|
|
126
|
+
return { typeNames, typeSymbols, underlyingSymbolToTypeName, diagnostics };
|
|
91
127
|
}
|
|
92
128
|
/**
|
|
93
129
|
* Checks if a symbol represents a type export (not a value export).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-name-collector.js","sourceRoot":"","sources":["../../../src/type-extractor/extractor/type-name-collector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EACL,UAAU,EACV,qBAAqB,GACtB,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"type-name-collector.js","sourceRoot":"","sources":["../../../src/type-extractor/extractor/type-name-collector.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ export interface SourceLocation {
|
|
|
3
3
|
readonly line: number;
|
|
4
4
|
readonly column: number;
|
|
5
5
|
}
|
|
6
|
-
export type DiagnosticCode = "DIRECTORY_NOT_FOUND" | "PARSE_ERROR" | "UNSUPPORTED_SYNTAX" | "RESERVED_TYPE_NAME" | "UNRESOLVED_REFERENCE" | "NAMING_CONVENTION_MISMATCH" | "INVALID_RESOLVER_SIGNATURE" | "UNSUPPORTED_RETURN_TYPE" | "UNSUPPORTED_ARG_TYPE" | "UNKNOWN_TARGET_TYPE" | "PARENT_TYPE_MISMATCH" | "MISSING_PARENT_TYPE" | "UNSUPPORTED_ENUM_TYPE" | "INVALID_ENUM_MEMBER" | "INVALID_INPUT_TYPE" | "UNKNOWN_ARGUMENT_TYPE" | "OUTPUT_TYPE_IN_INPUT" | "CIRCULAR_INPUT_REFERENCE" | "UNKNOWN_BRANDED_SCALAR" | "INVALID_SCALAR_IMPORT" | "CONFLICTING_SCALAR_TYPE" | "INVALID_DEFINE_CALL" | "CONFIG_SYNTAX_ERROR" | "CONFIG_MISSING_PROPERTY" | "CONFIG_INVALID_TYPE" | "CONFIG_INVALID_PATH" | "CONFIG_BUILTIN_OVERRIDE" | "CONFIG_DUPLICATE_MAPPING" | "CONFIG_DUPLICATE_TYPE" | "CONFIG_INVALID_OUTPUT_TYPE" | "CONFIG_INVALID_OUTPUT_PATH" | "CONFIG_INVALID_SOURCE_DIR" | "CONFIG_INVALID_IGNORE_GLOBS" | "CONFIG_INVALID_HOOK_TYPE" | "CONFIG_INVALID_HOOK_COMMAND" | "CONFIG_INVALID_ONLY_VALUE" | "CONFIG_INVALID_IMPORT_EXTENSION" | "CUSTOM_SCALAR_TYPE_NOT_FOUND" | "TSCONFIG_NOT_FOUND" | "TSCONFIG_PARSE_ERROR" | "TSCONFIG_INVALID" | "ONEOF_FIELD_NAME_CONFLICT" | "ONEOF_MULTIPLE_PROPERTIES" | "ONEOF_EMPTY_OBJECT" | "ONEOF_INVALID_FIELD_TYPE" | "ONEOF_DUPLICATE_PROPERTY" | "ONEOF_MIXED_MEMBERS" | "ONEOF_NAMED_TYPE_UNION" | "MULTIPLE_INPUT_TYPES" | "MISSING_INPUT_TYPE" | "MISSING_OUTPUT_TYPE" | "EMPTY_DIRECTIVE_NAME" | "UNRESOLVABLE_ARGUMENT" | "UNRESOLVABLE_ARG_TYPE" | "INVALID_LOCATION" | "INVALID_DIRECTIVE_LOCATION" | "UNSUPPORTED_DIRECTIVE_LOCATION" | "UNDEFINED_DIRECTIVE" | "INTERFACE_NOT_FOUND" | "INTERFACE_NOT_INTERFACE" | "INTERFACE_MISSING_FIELD" | "INTERFACE_FIELD_TYPE_MISMATCH" | "INTERFACE_CIRCULAR_REFERENCE" | "UNRESOLVABLE_DEFAULT_VALUE" | "AUTO_TYPE_NAME_COLLISION_USER" | "AUTO_TYPE_NAME_COLLISION_AUTO" | "MODULE_RESOLUTION_ERROR" | "SKIPPED_FIELD" | "SKIPPED_ENUM_VALUE" | "INDEX_SIGNATURE_ONLY" | "UNKNOWN_ABSTRACT_TYPE" | "INVALID_ABSTRACT_TYPE_KIND" | "INVALID_OBJECT_TYPE_KIND" | "DUPLICATE_RESOLVE_TYPE" | "DUPLICATE_IS_TYPE_OF" | "MISSING_ABSTRACT_TYPE_RESOLVER" | "DUPLICATE_ENUM_VALUE" | "DUPLICATE_ENUM_VALUE_AFTER_CONVERSION" | "INVALID_ENUM_MEMBER_NAME" | "AMBIGUOUS_SCALAR_BASE_TYPE" | "INLINE_UNION_PRIMITIVE_MEMBER" | "INLINE_UNION_ENUM_MEMBER" | "INLINE_UNION_UNRESOLVABLE_MEMBER" | "MISSING_TYPENAME_PROPERTY" | "INVALID_TYPENAME_TYPE" | "OPTIONAL_TYPENAME_PROPERTY" | "NULLABLE_TYPENAME_PROPERTY" | "TYPENAME_FIELD_STRUCTURE_MISMATCH" | "DUPLICATE_TYPENAME_VALUE" | "IGNORE_FIELD_NOT_FOUND" | "IGNORE_ALL_FIELDS";
|
|
6
|
+
export type DiagnosticCode = "DIRECTORY_NOT_FOUND" | "PARSE_ERROR" | "UNSUPPORTED_SYNTAX" | "RESERVED_TYPE_NAME" | "UNRESOLVED_REFERENCE" | "NAMING_CONVENTION_MISMATCH" | "INVALID_RESOLVER_SIGNATURE" | "UNSUPPORTED_RETURN_TYPE" | "UNSUPPORTED_ARG_TYPE" | "UNKNOWN_TARGET_TYPE" | "PARENT_TYPE_MISMATCH" | "MISSING_PARENT_TYPE" | "UNSUPPORTED_ENUM_TYPE" | "INVALID_ENUM_MEMBER" | "INVALID_INPUT_TYPE" | "UNKNOWN_ARGUMENT_TYPE" | "OUTPUT_TYPE_IN_INPUT" | "CIRCULAR_INPUT_REFERENCE" | "UNKNOWN_BRANDED_SCALAR" | "INVALID_SCALAR_IMPORT" | "CONFLICTING_SCALAR_TYPE" | "INVALID_DEFINE_CALL" | "CONFIG_SYNTAX_ERROR" | "CONFIG_FILE_NOT_FOUND" | "CONFIG_MISSING_PROPERTY" | "CONFIG_INVALID_TYPE" | "CONFIG_INVALID_PATH" | "CONFIG_BUILTIN_OVERRIDE" | "CONFIG_DUPLICATE_MAPPING" | "CONFIG_DUPLICATE_TYPE" | "CONFIG_INVALID_OUTPUT_TYPE" | "CONFIG_INVALID_OUTPUT_PATH" | "CONFIG_INVALID_SOURCE_DIR" | "CONFIG_INVALID_IGNORE_GLOBS" | "CONFIG_INVALID_HOOK_TYPE" | "CONFIG_INVALID_HOOK_COMMAND" | "CONFIG_INVALID_ONLY_VALUE" | "CONFIG_INVALID_IMPORT_EXTENSION" | "CUSTOM_SCALAR_TYPE_NOT_FOUND" | "TSCONFIG_NOT_FOUND" | "TSCONFIG_PARSE_ERROR" | "TSCONFIG_INVALID" | "ONEOF_FIELD_NAME_CONFLICT" | "ONEOF_MULTIPLE_PROPERTIES" | "ONEOF_EMPTY_OBJECT" | "ONEOF_INVALID_FIELD_TYPE" | "ONEOF_DUPLICATE_PROPERTY" | "ONEOF_MIXED_MEMBERS" | "ONEOF_NAMED_TYPE_UNION" | "MULTIPLE_INPUT_TYPES" | "MISSING_INPUT_TYPE" | "MISSING_OUTPUT_TYPE" | "EMPTY_DIRECTIVE_NAME" | "UNRESOLVABLE_ARGUMENT" | "UNRESOLVABLE_ARG_TYPE" | "INVALID_LOCATION" | "INVALID_DIRECTIVE_LOCATION" | "UNSUPPORTED_DIRECTIVE_LOCATION" | "UNDEFINED_DIRECTIVE" | "INTERFACE_NOT_FOUND" | "INTERFACE_NOT_INTERFACE" | "INTERFACE_MISSING_FIELD" | "INTERFACE_FIELD_TYPE_MISMATCH" | "INTERFACE_CIRCULAR_REFERENCE" | "UNRESOLVABLE_DEFAULT_VALUE" | "AUTO_TYPE_NAME_COLLISION_USER" | "AUTO_TYPE_NAME_COLLISION_AUTO" | "MODULE_RESOLUTION_ERROR" | "SKIPPED_FIELD" | "SKIPPED_ENUM_VALUE" | "INDEX_SIGNATURE_ONLY" | "UNKNOWN_ABSTRACT_TYPE" | "INVALID_ABSTRACT_TYPE_KIND" | "INVALID_OBJECT_TYPE_KIND" | "DUPLICATE_RESOLVE_TYPE" | "DUPLICATE_IS_TYPE_OF" | "MISSING_ABSTRACT_TYPE_RESOLVER" | "DUPLICATE_ENUM_VALUE" | "DUPLICATE_ENUM_VALUE_AFTER_CONVERSION" | "INVALID_ENUM_MEMBER_NAME" | "AMBIGUOUS_SCALAR_BASE_TYPE" | "INLINE_UNION_PRIMITIVE_MEMBER" | "INLINE_UNION_ENUM_MEMBER" | "INLINE_UNION_UNRESOLVABLE_MEMBER" | "MISSING_TYPENAME_PROPERTY" | "INVALID_TYPENAME_TYPE" | "OPTIONAL_TYPENAME_PROPERTY" | "NULLABLE_TYPENAME_PROPERTY" | "TYPENAME_FIELD_STRUCTURE_MISMATCH" | "DUPLICATE_TYPENAME_VALUE" | "IGNORE_FIELD_NOT_FOUND" | "IGNORE_ALL_FIELDS" | "DUPLICATE_TYPE_EXPORT";
|
|
7
7
|
export interface Diagnostic {
|
|
8
8
|
readonly code: DiagnosticCode;
|
|
9
9
|
readonly message: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../src/type-extractor/types/diagnostics.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,cAAc,GACtB,qBAAqB,GACrB,aAAa,GACb,oBAAoB,GACpB,oBAAoB,GACpB,sBAAsB,GACtB,4BAA4B,GAC5B,4BAA4B,GAC5B,yBAAyB,GACzB,sBAAsB,GACtB,qBAAqB,GACrB,sBAAsB,GACtB,qBAAqB,GACrB,uBAAuB,GACvB,qBAAqB,GACrB,oBAAoB,GACpB,uBAAuB,GACvB,sBAAsB,GACtB,0BAA0B,GAC1B,wBAAwB,GACxB,uBAAuB,GACvB,yBAAyB,GACzB,qBAAqB,GACrB,qBAAqB,GACrB,yBAAyB,GACzB,qBAAqB,GACrB,qBAAqB,GACrB,yBAAyB,GACzB,0BAA0B,GAC1B,uBAAuB,GACvB,4BAA4B,GAC5B,4BAA4B,GAC5B,2BAA2B,GAC3B,6BAA6B,GAC7B,0BAA0B,GAC1B,6BAA6B,GAC7B,2BAA2B,GAC3B,iCAAiC,GACjC,8BAA8B,GAC9B,oBAAoB,GACpB,sBAAsB,GACtB,kBAAkB,GAClB,2BAA2B,GAC3B,2BAA2B,GAC3B,oBAAoB,GACpB,0BAA0B,GAC1B,0BAA0B,GAC1B,qBAAqB,GACrB,wBAAwB,GACxB,sBAAsB,GACtB,oBAAoB,GACpB,qBAAqB,GACrB,sBAAsB,GACtB,uBAAuB,GACvB,uBAAuB,GACvB,kBAAkB,GAClB,4BAA4B,GAC5B,gCAAgC,GAChC,qBAAqB,GACrB,qBAAqB,GACrB,yBAAyB,GACzB,yBAAyB,GACzB,+BAA+B,GAC/B,8BAA8B,GAC9B,4BAA4B,GAC5B,+BAA+B,GAC/B,+BAA+B,GAC/B,yBAAyB,GACzB,eAAe,GACf,oBAAoB,GACpB,sBAAsB,GACtB,uBAAuB,GACvB,4BAA4B,GAC5B,0BAA0B,GAC1B,wBAAwB,GACxB,sBAAsB,GACtB,gCAAgC,GAChC,sBAAsB,GACtB,uCAAuC,GACvC,0BAA0B,GAC1B,4BAA4B,GAC5B,+BAA+B,GAC/B,0BAA0B,GAC1B,kCAAkC,GAClC,2BAA2B,GAC3B,uBAAuB,GACvB,4BAA4B,GAC5B,4BAA4B,GAC5B,mCAAmC,GACnC,0BAA0B,GAC1B,wBAAwB,GACxB,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../src/type-extractor/types/diagnostics.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,cAAc,GACtB,qBAAqB,GACrB,aAAa,GACb,oBAAoB,GACpB,oBAAoB,GACpB,sBAAsB,GACtB,4BAA4B,GAC5B,4BAA4B,GAC5B,yBAAyB,GACzB,sBAAsB,GACtB,qBAAqB,GACrB,sBAAsB,GACtB,qBAAqB,GACrB,uBAAuB,GACvB,qBAAqB,GACrB,oBAAoB,GACpB,uBAAuB,GACvB,sBAAsB,GACtB,0BAA0B,GAC1B,wBAAwB,GACxB,uBAAuB,GACvB,yBAAyB,GACzB,qBAAqB,GACrB,qBAAqB,GACrB,uBAAuB,GACvB,yBAAyB,GACzB,qBAAqB,GACrB,qBAAqB,GACrB,yBAAyB,GACzB,0BAA0B,GAC1B,uBAAuB,GACvB,4BAA4B,GAC5B,4BAA4B,GAC5B,2BAA2B,GAC3B,6BAA6B,GAC7B,0BAA0B,GAC1B,6BAA6B,GAC7B,2BAA2B,GAC3B,iCAAiC,GACjC,8BAA8B,GAC9B,oBAAoB,GACpB,sBAAsB,GACtB,kBAAkB,GAClB,2BAA2B,GAC3B,2BAA2B,GAC3B,oBAAoB,GACpB,0BAA0B,GAC1B,0BAA0B,GAC1B,qBAAqB,GACrB,wBAAwB,GACxB,sBAAsB,GACtB,oBAAoB,GACpB,qBAAqB,GACrB,sBAAsB,GACtB,uBAAuB,GACvB,uBAAuB,GACvB,kBAAkB,GAClB,4BAA4B,GAC5B,gCAAgC,GAChC,qBAAqB,GACrB,qBAAqB,GACrB,yBAAyB,GACzB,yBAAyB,GACzB,+BAA+B,GAC/B,8BAA8B,GAC9B,4BAA4B,GAC5B,+BAA+B,GAC/B,+BAA+B,GAC/B,yBAAyB,GACzB,eAAe,GACf,oBAAoB,GACpB,sBAAsB,GACtB,uBAAuB,GACvB,4BAA4B,GAC5B,0BAA0B,GAC1B,wBAAwB,GACxB,sBAAsB,GACtB,gCAAgC,GAChC,sBAAsB,GACtB,uCAAuC,GACvC,0BAA0B,GAC1B,4BAA4B,GAC5B,+BAA+B,GAC/B,0BAA0B,GAC1B,kCAAkC,GAClC,2BAA2B,GAC3B,uBAAuB,GACvB,4BAA4B,GAC5B,4BAA4B,GAC5B,mCAAmC,GACnC,0BAA0B,GAC1B,wBAAwB,GACxB,mBAAmB,GACnB,uBAAuB,CAAC;AAE5B,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;CAC9C"}
|
package/docs/configuration.md
CHANGED
|
@@ -7,6 +7,30 @@ description: Configure gqlkit via gqlkit.config.ts in your project root.
|
|
|
7
7
|
|
|
8
8
|
gqlkit can be configured via `gqlkit.config.ts` in your project root.
|
|
9
9
|
|
|
10
|
+
## CLI Options
|
|
11
|
+
|
|
12
|
+
The `gqlkit gen` command accepts the following options:
|
|
13
|
+
|
|
14
|
+
| Option | Short | Description |
|
|
15
|
+
|--------|-------|-------------|
|
|
16
|
+
| `--config <path>` | `-c` | Path to config file (default: `gqlkit.config.ts`) |
|
|
17
|
+
| `--cwd <path>` | | Working directory for code generation |
|
|
18
|
+
|
|
19
|
+
### Using Multiple Config Files
|
|
20
|
+
|
|
21
|
+
You can use different config files for different GraphQL schemas:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
# Generate with default config (gqlkit.config.ts)
|
|
25
|
+
gqlkit gen
|
|
26
|
+
|
|
27
|
+
# Generate with a specific config file
|
|
28
|
+
gqlkit gen --config gqlkit.public.config.ts
|
|
29
|
+
gqlkit gen -c gqlkit.admin.config.ts
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This is useful when you need to generate multiple GraphQL schemas from the same project, such as separate public and internal APIs.
|
|
33
|
+
|
|
10
34
|
## Basic Configuration
|
|
11
35
|
|
|
12
36
|
```ts
|
package/docs/index.md
CHANGED
|
@@ -18,15 +18,10 @@ gqlkit generates GraphQL schema and resolver maps from TypeScript types and func
|
|
|
18
18
|
|
|
19
19
|
- [What is gqlkit?](./what-is-gqlkit.md): Just types and functions — write TypeScript, generate GraphQL.
|
|
20
20
|
- [Getting Started](./getting-started.md): Install gqlkit and create your first GraphQL schema from TypeScript.
|
|
21
|
-
- [Coding Agents](./coding-agents.md): Set up AI coding agents like Claude Code and Codex to understand gqlkit conventions. (skip if reading via `gqlkit-guide` skill)
|
|
22
|
-
|
|
23
|
-
## CLI
|
|
24
|
-
|
|
25
|
-
- [Configuration](./configuration.md): Configure gqlkit via gqlkit.config.ts in your project root.
|
|
26
21
|
|
|
27
22
|
## Schema Definition
|
|
28
23
|
|
|
29
|
-
- [Schema Definition](./schema.md): gqlkit generates GraphQL schema from your TypeScript types.
|
|
24
|
+
- [Schema Definition](./schema/conventions.md): gqlkit generates GraphQL schema from your TypeScript types.
|
|
30
25
|
- [Defining Object Types](./schema/objects.md): Plain TypeScript type exports become GraphQL Object types.
|
|
31
26
|
- [Defining Input Types](./schema/inputs.md): TypeScript types with Input suffix are treated as GraphQL input types.
|
|
32
27
|
- [Defining Queries and Mutations](./schema/queries-mutations.md): Define Query and Mutation fields using the @gqlkit-ts/runtime API.
|
|
@@ -45,3 +40,8 @@ gqlkit generates GraphQL schema and resolver maps from TypeScript types and func
|
|
|
45
40
|
- [Integration with Apollo Server](./integration/apollo.md): Use gqlkit with Apollo Server, a popular GraphQL server.
|
|
46
41
|
- [Integration with Drizzle ORM](./integration/drizzle.md): Derive GraphQL types from Drizzle table definitions.
|
|
47
42
|
- [Integration with Prisma](./integration/prisma.md): Derive GraphQL types from Prisma model types.
|
|
43
|
+
|
|
44
|
+
## Guides
|
|
45
|
+
|
|
46
|
+
- [Configuration](./configuration.md): Configure gqlkit via gqlkit.config.ts in your project root.
|
|
47
|
+
- [Coding Agents](./coding-agents.md): Set up AI coding agents like Claude Code and Codex to understand gqlkit conventions. (skip if reading via `gqlkit-guide` skill)
|
package/package.json
CHANGED
package/src/commands/gen.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { createProgressReporter } from "../gen-orchestrator/reporter/progress-re
|
|
|
12
12
|
|
|
13
13
|
export interface RunGenCommandOptions {
|
|
14
14
|
readonly cwd: string;
|
|
15
|
+
readonly configPath: string | null;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export interface RunGenCommandResult {
|
|
@@ -29,7 +30,10 @@ export async function runGenCommand(
|
|
|
29
30
|
const progressReporter = createProgressReporter(writer);
|
|
30
31
|
const diagnosticReporter = createDiagnosticReporter(writer);
|
|
31
32
|
|
|
32
|
-
const configResult = await loadConfig({
|
|
33
|
+
const configResult = await loadConfig({
|
|
34
|
+
cwd: options.cwd,
|
|
35
|
+
configPath: options.configPath,
|
|
36
|
+
});
|
|
33
37
|
|
|
34
38
|
if (configResult.diagnostics.length > 0) {
|
|
35
39
|
diagnosticReporter.reportDiagnostics(configResult.diagnostics);
|
|
@@ -130,10 +134,16 @@ export const genCommand = define({
|
|
|
130
134
|
type: "string",
|
|
131
135
|
description: "Working directory for code generation",
|
|
132
136
|
},
|
|
137
|
+
config: {
|
|
138
|
+
type: "string",
|
|
139
|
+
short: "c",
|
|
140
|
+
description: "Path to config file",
|
|
141
|
+
},
|
|
133
142
|
},
|
|
134
143
|
run: async (ctx) => {
|
|
135
144
|
const cwd = ctx.values.cwd ?? process.cwd();
|
|
136
|
-
const
|
|
145
|
+
const configPath = ctx.values.config ?? null;
|
|
146
|
+
const result = await runGenCommand({ cwd, configPath });
|
|
137
147
|
if (result.exitCode !== 0) {
|
|
138
148
|
process.exitCode = result.exitCode;
|
|
139
149
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { isAbsolute, join } from "node:path";
|
|
3
3
|
import { createJiti } from "jiti";
|
|
4
4
|
import type { ImportExtension } from "../config/types.js";
|
|
5
5
|
import type { Diagnostic } from "../type-extractor/types/index.js";
|
|
@@ -7,6 +7,7 @@ import { validateConfig } from "./validator.js";
|
|
|
7
7
|
|
|
8
8
|
export interface LoadConfigOptions {
|
|
9
9
|
readonly cwd: string;
|
|
10
|
+
readonly configPath: string | null;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export interface ResolvedScalarMapping {
|
|
@@ -86,9 +87,35 @@ const DEFAULT_RESOLVED_CONFIG: ResolvedConfig = {
|
|
|
86
87
|
export async function loadConfig(
|
|
87
88
|
options: LoadConfigOptions,
|
|
88
89
|
): Promise<LoadConfigResult> {
|
|
89
|
-
const configPath =
|
|
90
|
+
const configPath = resolveConfigPath(options);
|
|
91
|
+
|
|
92
|
+
if (configPath === null) {
|
|
93
|
+
return {
|
|
94
|
+
config: DEFAULT_RESOLVED_CONFIG,
|
|
95
|
+
configPath: undefined,
|
|
96
|
+
diagnostics: [],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
90
99
|
|
|
91
100
|
if (!existsSync(configPath)) {
|
|
101
|
+
if (options.configPath !== null) {
|
|
102
|
+
return {
|
|
103
|
+
config: DEFAULT_RESOLVED_CONFIG,
|
|
104
|
+
configPath,
|
|
105
|
+
diagnostics: [
|
|
106
|
+
{
|
|
107
|
+
code: "CONFIG_FILE_NOT_FOUND",
|
|
108
|
+
message: `Config file not found: ${configPath}`,
|
|
109
|
+
severity: "error",
|
|
110
|
+
location: {
|
|
111
|
+
file: configPath,
|
|
112
|
+
line: 1,
|
|
113
|
+
column: 1,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
92
119
|
return {
|
|
93
120
|
config: DEFAULT_RESOLVED_CONFIG,
|
|
94
121
|
configPath: undefined,
|
|
@@ -145,3 +172,17 @@ export async function loadConfig(
|
|
|
145
172
|
};
|
|
146
173
|
}
|
|
147
174
|
}
|
|
175
|
+
|
|
176
|
+
function resolveConfigPath(options: LoadConfigOptions): string | null {
|
|
177
|
+
if (options.configPath !== null) {
|
|
178
|
+
if (isAbsolute(options.configPath)) {
|
|
179
|
+
return options.configPath;
|
|
180
|
+
}
|
|
181
|
+
return join(options.cwd, options.configPath);
|
|
182
|
+
}
|
|
183
|
+
const defaultPath = join(options.cwd, CONFIG_FILE_NAME);
|
|
184
|
+
if (existsSync(defaultPath)) {
|
|
185
|
+
return defaultPath;
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
@@ -514,11 +514,18 @@ function collectTypeNamesStep(ctx: PipelineContext): PipelineContext {
|
|
|
514
514
|
|
|
515
515
|
const result = collectDeclaredTypeNames(ctx.program, ctx.sourceFiles);
|
|
516
516
|
|
|
517
|
+
const diagnostics = [...ctx.diagnostics, ...result.diagnostics];
|
|
518
|
+
const hasDiagnosticErrors = result.diagnostics.some(
|
|
519
|
+
(d) => d.severity === "error",
|
|
520
|
+
);
|
|
521
|
+
|
|
517
522
|
return {
|
|
518
523
|
...ctx,
|
|
519
524
|
knownTypeNames: result.typeNames,
|
|
520
525
|
knownTypeSymbols: result.typeSymbols,
|
|
521
526
|
underlyingSymbolToTypeName: result.underlyingSymbolToTypeName,
|
|
527
|
+
diagnostics,
|
|
528
|
+
aborted: hasDiagnosticErrors,
|
|
522
529
|
};
|
|
523
530
|
}
|
|
524
531
|
|
|
@@ -170,31 +170,80 @@ function buildScalarResolverEntries(
|
|
|
170
170
|
);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Information about a resolver import with a unique alias.
|
|
175
|
+
*/
|
|
176
|
+
interface ResolverImportInfo {
|
|
177
|
+
/** The original export name from the source file */
|
|
178
|
+
readonly originalName: string;
|
|
179
|
+
/** The unique local name using the pattern <Parent>$<field> */
|
|
180
|
+
readonly localName: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Creates a unique local name for a resolver import.
|
|
185
|
+
* Format: <ParentType>$<FieldName>
|
|
186
|
+
*/
|
|
187
|
+
function makeResolverLocalName(parentType: string, fieldName: string): string {
|
|
188
|
+
return `${parentType}$${fieldName}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Collects resolver imports with unique aliases.
|
|
193
|
+
* Uses the pattern <Parent>$<field> for alias names.
|
|
194
|
+
*
|
|
195
|
+
* Note: Uniqueness assumes no duplicate resolver definitions for the same
|
|
196
|
+
* (parentType, fieldName) pair. Duplicate field definitions are caught
|
|
197
|
+
* by GraphQL schema validation at build time.
|
|
198
|
+
*/
|
|
199
|
+
function collectResolverImports(
|
|
174
200
|
resolverInfo: ResolverInfo,
|
|
175
|
-
): Map<string,
|
|
176
|
-
const importsByFile = new Map<string,
|
|
201
|
+
): Map<string, ResolverImportInfo[]> {
|
|
202
|
+
const importsByFile = new Map<string, ResolverImportInfo[]>();
|
|
203
|
+
// Track seen localNames per file to avoid duplicate import specifiers
|
|
204
|
+
const seenByFile = new Map<string, Set<string>>();
|
|
177
205
|
|
|
178
206
|
for (const type of resolverInfo.types) {
|
|
179
207
|
for (const field of type.fields) {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
208
|
+
const localName = makeResolverLocalName(type.typeName, field.fieldName);
|
|
209
|
+
|
|
210
|
+
const seen = seenByFile.get(field.sourceFile) ?? new Set<string>();
|
|
211
|
+
if (seen.has(localName)) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
seen.add(localName);
|
|
215
|
+
seenByFile.set(field.sourceFile, seen);
|
|
216
|
+
|
|
217
|
+
const infos = importsByFile.get(field.sourceFile) ?? [];
|
|
218
|
+
infos.push({ originalName: field.resolverValueName, localName });
|
|
219
|
+
importsByFile.set(field.sourceFile, infos);
|
|
183
220
|
}
|
|
184
221
|
}
|
|
185
222
|
|
|
186
223
|
for (const abstractResolver of resolverInfo.abstractTypeResolvers) {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
224
|
+
const localName = makeResolverLocalName(
|
|
225
|
+
abstractResolver.typeName,
|
|
226
|
+
abstractResolver.resolverKey,
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
const seen =
|
|
230
|
+
seenByFile.get(abstractResolver.sourceFile) ?? new Set<string>();
|
|
231
|
+
if (seen.has(localName)) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
seen.add(localName);
|
|
235
|
+
seenByFile.set(abstractResolver.sourceFile, seen);
|
|
236
|
+
|
|
237
|
+
const infos = importsByFile.get(abstractResolver.sourceFile) ?? [];
|
|
238
|
+
infos.push({ originalName: abstractResolver.exportName, localName });
|
|
239
|
+
importsByFile.set(abstractResolver.sourceFile, infos);
|
|
191
240
|
}
|
|
192
241
|
|
|
193
242
|
return importsByFile;
|
|
194
243
|
}
|
|
195
244
|
|
|
196
245
|
function buildResolverImports(
|
|
197
|
-
importsByFile: Map<string,
|
|
246
|
+
importsByFile: Map<string, ResolverImportInfo[]>,
|
|
198
247
|
outputDir: string,
|
|
199
248
|
importExtension: ImportExtension,
|
|
200
249
|
): string[] {
|
|
@@ -202,17 +251,25 @@ function buildResolverImports(
|
|
|
202
251
|
const sortedFiles = [...importsByFile.keys()].sort();
|
|
203
252
|
|
|
204
253
|
for (const sourceFile of sortedFiles) {
|
|
205
|
-
const
|
|
254
|
+
const importInfos = importsByFile.get(sourceFile) ?? [];
|
|
206
255
|
const importPath = computeRelativeImportPath(
|
|
207
256
|
outputDir,
|
|
208
257
|
sourceFile,
|
|
209
258
|
importExtension,
|
|
210
259
|
);
|
|
211
|
-
const sortedValues = [...valueNames].sort();
|
|
212
260
|
|
|
213
|
-
|
|
261
|
+
// Sort by localName for consistent output
|
|
262
|
+
const sortedInfos = [...importInfos].sort((a, b) =>
|
|
263
|
+
a.localName.localeCompare(b.localName),
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
if (sortedInfos.length > 0) {
|
|
267
|
+
const importSpecifiers = sortedInfos.map(
|
|
268
|
+
(info) => `${info.originalName} as ${info.localName}`,
|
|
269
|
+
);
|
|
270
|
+
|
|
214
271
|
imports.push(
|
|
215
|
-
`import { ${
|
|
272
|
+
`import { ${importSpecifiers.join(", ")} } from "${importPath}";`,
|
|
216
273
|
);
|
|
217
274
|
}
|
|
218
275
|
}
|
|
@@ -223,7 +280,11 @@ function buildResolverImports(
|
|
|
223
280
|
function buildAbstractOnlyTypeEntry(
|
|
224
281
|
abstractResolver: AbstractTypeResolverInfo,
|
|
225
282
|
): string {
|
|
226
|
-
|
|
283
|
+
const localName = makeResolverLocalName(
|
|
284
|
+
abstractResolver.typeName,
|
|
285
|
+
abstractResolver.resolverKey,
|
|
286
|
+
);
|
|
287
|
+
return ` ${abstractResolver.typeName}: {\n ${abstractResolver.resolverKey}: ${localName},\n },`;
|
|
227
288
|
}
|
|
228
289
|
|
|
229
290
|
function buildNumericEnumResolver(enumInfo: NumericEnumInfo): string {
|
|
@@ -261,19 +322,23 @@ function buildTypeResolverEntry(
|
|
|
261
322
|
const entries: string[] = [];
|
|
262
323
|
|
|
263
324
|
for (const field of type.fields) {
|
|
325
|
+
const localName = makeResolverLocalName(type.typeName, field.fieldName);
|
|
326
|
+
|
|
264
327
|
if (field.isDirectExport) {
|
|
265
|
-
entries.push(` ${field.fieldName}: ${
|
|
328
|
+
entries.push(` ${field.fieldName}: ${localName},`);
|
|
266
329
|
} else {
|
|
267
330
|
entries.push(
|
|
268
|
-
` ${field.fieldName}: ${
|
|
331
|
+
` ${field.fieldName}: ${localName}.${field.fieldName},`,
|
|
269
332
|
);
|
|
270
333
|
}
|
|
271
334
|
}
|
|
272
335
|
|
|
273
336
|
if (abstractResolverForType !== null) {
|
|
274
|
-
|
|
275
|
-
|
|
337
|
+
const localName = makeResolverLocalName(
|
|
338
|
+
abstractResolverForType.typeName,
|
|
339
|
+
abstractResolverForType.resolverKey,
|
|
276
340
|
);
|
|
341
|
+
entries.push(` ${abstractResolverForType.resolverKey}: ${localName},`);
|
|
277
342
|
}
|
|
278
343
|
|
|
279
344
|
return ` ${type.typeName}: {\n${entries.join("\n")}\n },`;
|
|
@@ -379,7 +444,7 @@ export function emitResolversCode(params: EmitResolversCodeParams): string {
|
|
|
379
444
|
imports.push('import { GraphQLScalarType } from "graphql";');
|
|
380
445
|
}
|
|
381
446
|
|
|
382
|
-
const importsByFile =
|
|
447
|
+
const importsByFile = collectResolverImports(resolverInfo);
|
|
383
448
|
imports.push(
|
|
384
449
|
...buildResolverImports(importsByFile, outputDir, importExtension),
|
|
385
450
|
);
|