@constructive-io/graphql-codegen 2.20.1 → 2.22.0
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 +15 -3
- package/cli/codegen/barrel.d.ts +4 -1
- package/cli/codegen/barrel.js +18 -12
- package/cli/codegen/client.js +33 -0
- package/cli/codegen/custom-mutations.d.ts +11 -1
- package/cli/codegen/custom-mutations.js +49 -15
- package/cli/codegen/custom-queries.d.ts +8 -0
- package/cli/codegen/custom-queries.js +82 -47
- package/cli/codegen/gql-ast.js +9 -5
- package/cli/codegen/index.js +39 -8
- package/cli/codegen/mutations.d.ts +14 -4
- package/cli/codegen/mutations.js +114 -28
- package/cli/codegen/orm/barrel.js +4 -2
- package/cli/codegen/orm/index.js +17 -0
- package/cli/codegen/orm/input-types-generator.js +83 -29
- package/cli/codegen/orm/model-generator.js +6 -4
- package/cli/codegen/queries.d.ts +7 -3
- package/cli/codegen/queries.js +185 -158
- package/cli/codegen/scalars.d.ts +6 -4
- package/cli/codegen/scalars.js +17 -9
- package/cli/codegen/schema-types-generator.d.ts +26 -0
- package/cli/codegen/schema-types-generator.js +365 -0
- package/cli/codegen/ts-ast.d.ts +3 -1
- package/cli/codegen/ts-ast.js +2 -2
- package/cli/codegen/type-resolver.d.ts +52 -6
- package/cli/codegen/type-resolver.js +97 -19
- package/cli/codegen/types.d.ts +7 -4
- package/cli/codegen/types.js +94 -41
- package/cli/codegen/utils.d.ts +20 -2
- package/cli/codegen/utils.js +32 -7
- package/cli/commands/generate-orm.js +5 -5
- package/cli/commands/generate.d.ts +4 -1
- package/cli/commands/generate.js +27 -8
- package/cli/introspect/transform-schema.d.ts +33 -21
- package/cli/introspect/transform-schema.js +31 -21
- package/esm/cli/codegen/barrel.d.ts +4 -1
- package/esm/cli/codegen/barrel.js +18 -12
- package/esm/cli/codegen/client.js +33 -0
- package/esm/cli/codegen/custom-mutations.d.ts +11 -1
- package/esm/cli/codegen/custom-mutations.js +50 -16
- package/esm/cli/codegen/custom-queries.d.ts +8 -0
- package/esm/cli/codegen/custom-queries.js +83 -48
- package/esm/cli/codegen/gql-ast.js +10 -6
- package/esm/cli/codegen/index.js +39 -8
- package/esm/cli/codegen/mutations.d.ts +14 -4
- package/esm/cli/codegen/mutations.js +115 -29
- package/esm/cli/codegen/orm/barrel.js +4 -2
- package/esm/cli/codegen/orm/index.js +17 -0
- package/esm/cli/codegen/orm/input-types-generator.js +83 -29
- package/esm/cli/codegen/orm/model-generator.js +7 -5
- package/esm/cli/codegen/queries.d.ts +7 -3
- package/esm/cli/codegen/queries.js +186 -159
- package/esm/cli/codegen/scalars.d.ts +6 -4
- package/esm/cli/codegen/scalars.js +16 -8
- package/esm/cli/codegen/schema-types-generator.d.ts +26 -0
- package/esm/cli/codegen/schema-types-generator.js +362 -0
- package/esm/cli/codegen/ts-ast.d.ts +3 -1
- package/esm/cli/codegen/ts-ast.js +2 -2
- package/esm/cli/codegen/type-resolver.d.ts +52 -6
- package/esm/cli/codegen/type-resolver.js +97 -20
- package/esm/cli/codegen/types.d.ts +7 -4
- package/esm/cli/codegen/types.js +95 -41
- package/esm/cli/codegen/utils.d.ts +20 -2
- package/esm/cli/codegen/utils.js +31 -7
- package/esm/cli/commands/generate-orm.js +5 -5
- package/esm/cli/commands/generate.d.ts +4 -1
- package/esm/cli/commands/generate.js +27 -8
- package/esm/cli/introspect/transform-schema.d.ts +33 -21
- package/esm/cli/introspect/transform-schema.js +31 -21
- package/esm/types/config.d.ts +16 -1
- package/esm/types/config.js +6 -0
- package/esm/types/schema.d.ts +2 -0
- package/package.json +8 -6
- package/types/config.d.ts +16 -1
- package/types/config.js +6 -0
- package/types/schema.d.ts +2 -0
- package/__tests__/codegen/input-types-generator.test.d.ts +0 -1
- package/__tests__/codegen/input-types-generator.test.js +0 -635
- package/cli/codegen/filters.d.ts +0 -27
- package/cli/codegen/filters.js +0 -357
- package/cli/codegen/orm/input-types-generator.test.d.ts +0 -1
- package/cli/codegen/orm/input-types-generator.test.js +0 -75
- package/cli/codegen/orm/select-types.test.d.ts +0 -11
- package/cli/codegen/orm/select-types.test.js +0 -22
- package/cli/introspect/transform-schema.test.d.ts +0 -1
- package/cli/introspect/transform-schema.test.js +0 -67
- package/esm/__tests__/codegen/input-types-generator.test.d.ts +0 -1
- package/esm/__tests__/codegen/input-types-generator.test.js +0 -633
- package/esm/cli/codegen/filters.d.ts +0 -27
- package/esm/cli/codegen/filters.js +0 -351
- package/esm/cli/codegen/orm/input-types-generator.test.d.ts +0 -1
- package/esm/cli/codegen/orm/input-types-generator.test.js +0 -73
- package/esm/cli/codegen/orm/select-types.test.d.ts +0 -11
- package/esm/cli/codegen/orm/select-types.test.js +0 -21
- package/esm/cli/introspect/transform-schema.test.d.ts +0 -1
- package/esm/cli/introspect/transform-schema.test.js +0 -65
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateSchemaTypesFile = generateSchemaTypesFile;
|
|
4
|
+
const ts_ast_1 = require("./ts-ast");
|
|
5
|
+
const type_resolver_1 = require("./type-resolver");
|
|
6
|
+
const scalars_1 = require("./scalars");
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Constants
|
|
9
|
+
// ============================================================================
|
|
10
|
+
/**
|
|
11
|
+
* Types that should not be generated (scalars, built-ins, types generated elsewhere)
|
|
12
|
+
*/
|
|
13
|
+
const SKIP_TYPES = new Set([
|
|
14
|
+
...scalars_1.SCALAR_NAMES,
|
|
15
|
+
// GraphQL built-ins
|
|
16
|
+
'Query',
|
|
17
|
+
'Mutation',
|
|
18
|
+
'Subscription',
|
|
19
|
+
'__Schema',
|
|
20
|
+
'__Type',
|
|
21
|
+
'__Field',
|
|
22
|
+
'__InputValue',
|
|
23
|
+
'__EnumValue',
|
|
24
|
+
'__Directive',
|
|
25
|
+
// Note: PageInfo and Cursor are NOT skipped - they're needed by Connection types
|
|
26
|
+
// Base filter types (generated in types.ts via filters.ts)
|
|
27
|
+
...scalars_1.BASE_FILTER_TYPE_NAMES,
|
|
28
|
+
]);
|
|
29
|
+
/**
|
|
30
|
+
* Type name patterns to skip (regex patterns)
|
|
31
|
+
*
|
|
32
|
+
* Note: We intentionally DO NOT skip Connection, Edge, Filter, or Patch types
|
|
33
|
+
* because they may be referenced by custom operations or payload types.
|
|
34
|
+
* Only skip Condition and OrderBy which are typically not needed.
|
|
35
|
+
*/
|
|
36
|
+
const SKIP_TYPE_PATTERNS = [
|
|
37
|
+
/Condition$/, // e.g., UserCondition (filter conditions are separate)
|
|
38
|
+
/OrderBy$/, // e.g., UsersOrderBy (ordering is handled separately)
|
|
39
|
+
];
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Type Conversion Utilities
|
|
42
|
+
// ============================================================================
|
|
43
|
+
/**
|
|
44
|
+
* Convert a CleanTypeRef to TypeScript type string
|
|
45
|
+
*/
|
|
46
|
+
function typeRefToTs(typeRef) {
|
|
47
|
+
if (typeRef.kind === 'NON_NULL') {
|
|
48
|
+
if (typeRef.ofType) {
|
|
49
|
+
return typeRefToTs(typeRef.ofType);
|
|
50
|
+
}
|
|
51
|
+
return typeRef.name ?? 'unknown';
|
|
52
|
+
}
|
|
53
|
+
if (typeRef.kind === 'LIST') {
|
|
54
|
+
if (typeRef.ofType) {
|
|
55
|
+
return `${typeRefToTs(typeRef.ofType)}[]`;
|
|
56
|
+
}
|
|
57
|
+
return 'unknown[]';
|
|
58
|
+
}
|
|
59
|
+
// Scalar or named type
|
|
60
|
+
const name = typeRef.name ?? 'unknown';
|
|
61
|
+
return (0, scalars_1.scalarToTsType)(name, { unknownScalar: 'name' });
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if a type is required (NON_NULL)
|
|
65
|
+
*/
|
|
66
|
+
function isRequired(typeRef) {
|
|
67
|
+
return typeRef.kind === 'NON_NULL';
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if a type should be skipped
|
|
71
|
+
*/
|
|
72
|
+
function shouldSkipType(typeName, tableTypeNames) {
|
|
73
|
+
// Skip scalars and built-ins
|
|
74
|
+
if (SKIP_TYPES.has(typeName))
|
|
75
|
+
return true;
|
|
76
|
+
// Skip table entity types (already in types.ts)
|
|
77
|
+
if (tableTypeNames.has(typeName))
|
|
78
|
+
return true;
|
|
79
|
+
// Skip types matching patterns
|
|
80
|
+
for (const pattern of SKIP_TYPE_PATTERNS) {
|
|
81
|
+
if (pattern.test(typeName))
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
// Skip table-specific types that would conflict with inline types in table-based hooks.
|
|
85
|
+
// Note: Patch and CreateInput are now NOT exported from hooks (isExported: false),
|
|
86
|
+
// so we only skip Filter types here.
|
|
87
|
+
// The Filter and OrderBy types are generated inline (non-exported) by table query hooks,
|
|
88
|
+
// but schema-types should still generate them for custom operations that need them.
|
|
89
|
+
// Actually, we don't skip any table-based types now since hooks don't export them.
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// ENUM Types Generator
|
|
94
|
+
// ============================================================================
|
|
95
|
+
/**
|
|
96
|
+
* Add ENUM types to source file
|
|
97
|
+
*/
|
|
98
|
+
function addEnumTypes(sourceFile, typeRegistry, tableTypeNames) {
|
|
99
|
+
const generatedTypes = new Set();
|
|
100
|
+
(0, ts_ast_1.addSectionComment)(sourceFile, 'Enum Types');
|
|
101
|
+
for (const [typeName, typeInfo] of typeRegistry) {
|
|
102
|
+
if (typeInfo.kind !== 'ENUM')
|
|
103
|
+
continue;
|
|
104
|
+
if (shouldSkipType(typeName, tableTypeNames))
|
|
105
|
+
continue;
|
|
106
|
+
if (!typeInfo.enumValues || typeInfo.enumValues.length === 0)
|
|
107
|
+
continue;
|
|
108
|
+
const values = typeInfo.enumValues.map((v) => `'${v}'`).join(' | ');
|
|
109
|
+
sourceFile.addTypeAlias((0, ts_ast_1.createTypeAlias)(typeName, values));
|
|
110
|
+
generatedTypes.add(typeName);
|
|
111
|
+
}
|
|
112
|
+
return generatedTypes;
|
|
113
|
+
}
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// INPUT_OBJECT Types Generator
|
|
116
|
+
// ============================================================================
|
|
117
|
+
/**
|
|
118
|
+
* Add INPUT_OBJECT types to source file
|
|
119
|
+
* Uses iteration to handle nested input types
|
|
120
|
+
*/
|
|
121
|
+
function addInputObjectTypes(sourceFile, typeRegistry, tableTypeNames, alreadyGenerated) {
|
|
122
|
+
const generatedTypes = new Set(alreadyGenerated);
|
|
123
|
+
const typesToGenerate = new Set();
|
|
124
|
+
// Collect all INPUT_OBJECT types
|
|
125
|
+
for (const [typeName, typeInfo] of typeRegistry) {
|
|
126
|
+
if (typeInfo.kind !== 'INPUT_OBJECT')
|
|
127
|
+
continue;
|
|
128
|
+
if (shouldSkipType(typeName, tableTypeNames))
|
|
129
|
+
continue;
|
|
130
|
+
if (generatedTypes.has(typeName))
|
|
131
|
+
continue;
|
|
132
|
+
typesToGenerate.add(typeName);
|
|
133
|
+
}
|
|
134
|
+
if (typesToGenerate.size === 0)
|
|
135
|
+
return generatedTypes;
|
|
136
|
+
(0, ts_ast_1.addSectionComment)(sourceFile, 'Input Object Types');
|
|
137
|
+
// Process all types - no artificial limit
|
|
138
|
+
while (typesToGenerate.size > 0) {
|
|
139
|
+
const typeNameResult = typesToGenerate.values().next();
|
|
140
|
+
if (typeNameResult.done)
|
|
141
|
+
break;
|
|
142
|
+
const typeName = typeNameResult.value;
|
|
143
|
+
typesToGenerate.delete(typeName);
|
|
144
|
+
if (generatedTypes.has(typeName))
|
|
145
|
+
continue;
|
|
146
|
+
const typeInfo = typeRegistry.get(typeName);
|
|
147
|
+
if (!typeInfo || typeInfo.kind !== 'INPUT_OBJECT')
|
|
148
|
+
continue;
|
|
149
|
+
generatedTypes.add(typeName);
|
|
150
|
+
if (typeInfo.inputFields && typeInfo.inputFields.length > 0) {
|
|
151
|
+
const properties = [];
|
|
152
|
+
for (const field of typeInfo.inputFields) {
|
|
153
|
+
const optional = !isRequired(field.type);
|
|
154
|
+
const tsType = typeRefToTs(field.type);
|
|
155
|
+
properties.push({
|
|
156
|
+
name: field.name,
|
|
157
|
+
type: tsType,
|
|
158
|
+
optional,
|
|
159
|
+
docs: field.description ? [field.description] : undefined,
|
|
160
|
+
});
|
|
161
|
+
// Follow nested Input types
|
|
162
|
+
const baseType = (0, type_resolver_1.getTypeBaseName)(field.type);
|
|
163
|
+
if (baseType &&
|
|
164
|
+
!generatedTypes.has(baseType) &&
|
|
165
|
+
!shouldSkipType(baseType, tableTypeNames)) {
|
|
166
|
+
const nestedType = typeRegistry.get(baseType);
|
|
167
|
+
if (nestedType?.kind === 'INPUT_OBJECT') {
|
|
168
|
+
typesToGenerate.add(baseType);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
sourceFile.addInterface((0, ts_ast_1.createInterface)(typeName, properties));
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Empty input object
|
|
176
|
+
sourceFile.addInterface((0, ts_ast_1.createInterface)(typeName, []));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return generatedTypes;
|
|
180
|
+
}
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// UNION Types Generator
|
|
183
|
+
// ============================================================================
|
|
184
|
+
/**
|
|
185
|
+
* Add UNION types to source file
|
|
186
|
+
*/
|
|
187
|
+
function addUnionTypes(sourceFile, typeRegistry, tableTypeNames, alreadyGenerated) {
|
|
188
|
+
const generatedTypes = new Set(alreadyGenerated);
|
|
189
|
+
const unionTypesToGenerate = new Set();
|
|
190
|
+
// Collect all UNION types
|
|
191
|
+
for (const [typeName, typeInfo] of typeRegistry) {
|
|
192
|
+
if (typeInfo.kind !== 'UNION')
|
|
193
|
+
continue;
|
|
194
|
+
if (shouldSkipType(typeName, tableTypeNames))
|
|
195
|
+
continue;
|
|
196
|
+
if (generatedTypes.has(typeName))
|
|
197
|
+
continue;
|
|
198
|
+
unionTypesToGenerate.add(typeName);
|
|
199
|
+
}
|
|
200
|
+
if (unionTypesToGenerate.size === 0)
|
|
201
|
+
return generatedTypes;
|
|
202
|
+
(0, ts_ast_1.addSectionComment)(sourceFile, 'Union Types');
|
|
203
|
+
for (const typeName of unionTypesToGenerate) {
|
|
204
|
+
const typeInfo = typeRegistry.get(typeName);
|
|
205
|
+
if (!typeInfo || typeInfo.kind !== 'UNION')
|
|
206
|
+
continue;
|
|
207
|
+
if (!typeInfo.possibleTypes || typeInfo.possibleTypes.length === 0)
|
|
208
|
+
continue;
|
|
209
|
+
// Generate union type as TypeScript union
|
|
210
|
+
const unionMembers = typeInfo.possibleTypes.join(' | ');
|
|
211
|
+
sourceFile.addTypeAlias((0, ts_ast_1.createTypeAlias)(typeName, unionMembers));
|
|
212
|
+
generatedTypes.add(typeName);
|
|
213
|
+
}
|
|
214
|
+
return generatedTypes;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Collect return types from Query and Mutation root types
|
|
218
|
+
* This dynamically discovers what OBJECT types need to be generated
|
|
219
|
+
* based on actual schema structure, not pattern matching
|
|
220
|
+
*/
|
|
221
|
+
function collectReturnTypesFromRootTypes(typeRegistry, tableTypeNames) {
|
|
222
|
+
const returnTypes = new Set();
|
|
223
|
+
// Get Query and Mutation root types
|
|
224
|
+
const queryType = typeRegistry.get('Query');
|
|
225
|
+
const mutationType = typeRegistry.get('Mutation');
|
|
226
|
+
const processFields = (fields) => {
|
|
227
|
+
if (!fields)
|
|
228
|
+
return;
|
|
229
|
+
for (const field of fields) {
|
|
230
|
+
const baseType = (0, type_resolver_1.getTypeBaseName)(field.type);
|
|
231
|
+
if (baseType && !shouldSkipType(baseType, tableTypeNames)) {
|
|
232
|
+
const typeInfo = typeRegistry.get(baseType);
|
|
233
|
+
if (typeInfo?.kind === 'OBJECT') {
|
|
234
|
+
returnTypes.add(baseType);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
if (queryType?.fields)
|
|
240
|
+
processFields(queryType.fields);
|
|
241
|
+
if (mutationType?.fields)
|
|
242
|
+
processFields(mutationType.fields);
|
|
243
|
+
return returnTypes;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Add Payload OBJECT types to source file
|
|
247
|
+
* These are return types from mutations (e.g., LoginPayload, BootstrapUserPayload)
|
|
248
|
+
*
|
|
249
|
+
* Also tracks which table entity types are referenced so they can be imported.
|
|
250
|
+
*
|
|
251
|
+
* Uses dynamic type discovery from Query/Mutation return types instead of pattern matching.
|
|
252
|
+
*/
|
|
253
|
+
function addPayloadObjectTypes(sourceFile, typeRegistry, tableTypeNames, alreadyGenerated) {
|
|
254
|
+
const generatedTypes = new Set(alreadyGenerated);
|
|
255
|
+
const referencedTableTypes = new Set();
|
|
256
|
+
// Dynamically collect return types from Query and Mutation
|
|
257
|
+
const typesToGenerate = collectReturnTypesFromRootTypes(typeRegistry, tableTypeNames);
|
|
258
|
+
// Filter out already generated types
|
|
259
|
+
for (const typeName of Array.from(typesToGenerate)) {
|
|
260
|
+
if (generatedTypes.has(typeName)) {
|
|
261
|
+
typesToGenerate.delete(typeName);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (typesToGenerate.size === 0) {
|
|
265
|
+
return { generatedTypes, referencedTableTypes };
|
|
266
|
+
}
|
|
267
|
+
(0, ts_ast_1.addSectionComment)(sourceFile, 'Payload/Return Object Types');
|
|
268
|
+
// Process all types - no artificial limit
|
|
269
|
+
while (typesToGenerate.size > 0) {
|
|
270
|
+
const typeNameResult = typesToGenerate.values().next();
|
|
271
|
+
if (typeNameResult.done)
|
|
272
|
+
break;
|
|
273
|
+
const typeName = typeNameResult.value;
|
|
274
|
+
typesToGenerate.delete(typeName);
|
|
275
|
+
if (generatedTypes.has(typeName))
|
|
276
|
+
continue;
|
|
277
|
+
const typeInfo = typeRegistry.get(typeName);
|
|
278
|
+
if (!typeInfo || typeInfo.kind !== 'OBJECT')
|
|
279
|
+
continue;
|
|
280
|
+
generatedTypes.add(typeName);
|
|
281
|
+
if (typeInfo.fields && typeInfo.fields.length > 0) {
|
|
282
|
+
const properties = [];
|
|
283
|
+
for (const field of typeInfo.fields) {
|
|
284
|
+
const baseType = (0, type_resolver_1.getTypeBaseName)(field.type);
|
|
285
|
+
// Skip Query and Mutation fields
|
|
286
|
+
if (baseType === 'Query' || baseType === 'Mutation')
|
|
287
|
+
continue;
|
|
288
|
+
const tsType = typeRefToTs(field.type);
|
|
289
|
+
const isNullable = !isRequired(field.type);
|
|
290
|
+
properties.push({
|
|
291
|
+
name: field.name,
|
|
292
|
+
type: isNullable ? `${tsType} | null` : tsType,
|
|
293
|
+
optional: isNullable,
|
|
294
|
+
docs: field.description ? [field.description] : undefined,
|
|
295
|
+
});
|
|
296
|
+
// Track table entity types that are referenced
|
|
297
|
+
if (baseType && tableTypeNames.has(baseType)) {
|
|
298
|
+
referencedTableTypes.add(baseType);
|
|
299
|
+
}
|
|
300
|
+
// Follow nested OBJECT types that aren't table types
|
|
301
|
+
if (baseType &&
|
|
302
|
+
!generatedTypes.has(baseType) &&
|
|
303
|
+
!shouldSkipType(baseType, tableTypeNames)) {
|
|
304
|
+
const nestedType = typeRegistry.get(baseType);
|
|
305
|
+
if (nestedType?.kind === 'OBJECT') {
|
|
306
|
+
typesToGenerate.add(baseType);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
sourceFile.addInterface((0, ts_ast_1.createInterface)(typeName, properties));
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
// Empty payload object
|
|
314
|
+
sourceFile.addInterface((0, ts_ast_1.createInterface)(typeName, []));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return { generatedTypes, referencedTableTypes };
|
|
318
|
+
}
|
|
319
|
+
// ============================================================================
|
|
320
|
+
// Main Generator
|
|
321
|
+
// ============================================================================
|
|
322
|
+
/**
|
|
323
|
+
* Generate comprehensive schema-types.ts file using ts-morph AST
|
|
324
|
+
*
|
|
325
|
+
* This generates all Input/Payload/Enum types from the TypeRegistry
|
|
326
|
+
* that are needed by custom mutation/query hooks.
|
|
327
|
+
*/
|
|
328
|
+
function generateSchemaTypesFile(options) {
|
|
329
|
+
const { typeRegistry, tableTypeNames } = options;
|
|
330
|
+
const project = (0, ts_ast_1.createProject)();
|
|
331
|
+
const sourceFile = (0, ts_ast_1.createSourceFile)(project, 'schema-types.ts');
|
|
332
|
+
// Add file header
|
|
333
|
+
sourceFile.insertText(0, (0, ts_ast_1.createFileHeader)('GraphQL schema types for custom operations') + '\n');
|
|
334
|
+
// Track all generated types
|
|
335
|
+
let generatedTypes = new Set();
|
|
336
|
+
// 1. Generate ENUM types
|
|
337
|
+
const enumTypes = addEnumTypes(sourceFile, typeRegistry, tableTypeNames);
|
|
338
|
+
generatedTypes = new Set([...generatedTypes, ...enumTypes]);
|
|
339
|
+
// 2. Generate UNION types
|
|
340
|
+
const unionTypes = addUnionTypes(sourceFile, typeRegistry, tableTypeNames, generatedTypes);
|
|
341
|
+
generatedTypes = new Set([...generatedTypes, ...unionTypes]);
|
|
342
|
+
// 3. Generate INPUT_OBJECT types
|
|
343
|
+
const inputTypes = addInputObjectTypes(sourceFile, typeRegistry, tableTypeNames, generatedTypes);
|
|
344
|
+
generatedTypes = new Set([...generatedTypes, ...inputTypes]);
|
|
345
|
+
// 4. Generate Payload OBJECT types
|
|
346
|
+
const payloadResult = addPayloadObjectTypes(sourceFile, typeRegistry, tableTypeNames, generatedTypes);
|
|
347
|
+
// 5. Add imports from types.ts (table entity types + base filter types)
|
|
348
|
+
const referencedTableTypes = Array.from(payloadResult.referencedTableTypes).sort();
|
|
349
|
+
// Always import base filter types since generated Filter interfaces reference them
|
|
350
|
+
const baseFilterImports = Array.from(scalars_1.BASE_FILTER_TYPE_NAMES).sort();
|
|
351
|
+
const allTypesImports = [...referencedTableTypes, ...baseFilterImports];
|
|
352
|
+
if (allTypesImports.length > 0) {
|
|
353
|
+
// Insert import after the file header comment
|
|
354
|
+
const importStatement = `import type { ${allTypesImports.join(', ')} } from './types';\n\n`;
|
|
355
|
+
// Find position after header (after first */ + newlines)
|
|
356
|
+
const headerEndIndex = sourceFile.getFullText().indexOf('*/') + 3;
|
|
357
|
+
sourceFile.insertText(headerEndIndex, '\n' + importStatement);
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
fileName: 'schema-types.ts',
|
|
361
|
+
content: (0, ts_ast_1.getMinimalFormattedOutput)(sourceFile),
|
|
362
|
+
generatedEnums: Array.from(enumTypes).sort(),
|
|
363
|
+
referencedTableTypes,
|
|
364
|
+
};
|
|
365
|
+
}
|
package/cli/codegen/ts-ast.d.ts
CHANGED
|
@@ -66,7 +66,9 @@ export declare function createInterface(name: string, properties: InterfacePrope
|
|
|
66
66
|
export declare function createFilterInterface(name: string, fieldFilters: Array<{
|
|
67
67
|
fieldName: string;
|
|
68
68
|
filterType: string;
|
|
69
|
-
}
|
|
69
|
+
}>, options?: {
|
|
70
|
+
isExported?: boolean;
|
|
71
|
+
}): InterfaceDeclarationStructure;
|
|
70
72
|
/**
|
|
71
73
|
* Create type alias declaration structure
|
|
72
74
|
*/
|
package/cli/codegen/ts-ast.js
CHANGED
|
@@ -163,7 +163,7 @@ function createInterface(name, properties, options) {
|
|
|
163
163
|
/**
|
|
164
164
|
* Create filter interface with standard PostGraphile operators
|
|
165
165
|
*/
|
|
166
|
-
function createFilterInterface(name, fieldFilters) {
|
|
166
|
+
function createFilterInterface(name, fieldFilters, options) {
|
|
167
167
|
const properties = [
|
|
168
168
|
...fieldFilters.map((f) => ({
|
|
169
169
|
name: f.fieldName,
|
|
@@ -174,7 +174,7 @@ function createFilterInterface(name, fieldFilters) {
|
|
|
174
174
|
{ name: 'or', type: `${name}[]`, optional: true, docs: ['Logical OR'] },
|
|
175
175
|
{ name: 'not', type: name, optional: true, docs: ['Logical NOT'] },
|
|
176
176
|
];
|
|
177
|
-
return createInterface(name, properties);
|
|
177
|
+
return createInterface(name, properties, { isExported: options?.isExported ?? true });
|
|
178
178
|
}
|
|
179
179
|
// ============================================================================
|
|
180
180
|
// Type alias builders
|
|
@@ -6,6 +6,34 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { CleanTypeRef, CleanArgument, CleanObjectField } from '../../types/schema';
|
|
8
8
|
import type { InterfaceProperty } from './ts-ast';
|
|
9
|
+
/**
|
|
10
|
+
* Interface for tracking referenced types during code generation
|
|
11
|
+
*/
|
|
12
|
+
export interface TypeTracker {
|
|
13
|
+
/** Set of type names that have been referenced */
|
|
14
|
+
referencedTypes: Set<string>;
|
|
15
|
+
/** Track a type reference */
|
|
16
|
+
track(typeName: string): void;
|
|
17
|
+
/** Get importable types from schema-types.ts (Input/Payload/Enum types) */
|
|
18
|
+
getImportableTypes(): string[];
|
|
19
|
+
/** Get importable types from types.ts (table entity types) */
|
|
20
|
+
getTableTypes(): string[];
|
|
21
|
+
/** Reset the tracker */
|
|
22
|
+
reset(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Options for creating a TypeTracker
|
|
26
|
+
*/
|
|
27
|
+
export interface TypeTrackerOptions {
|
|
28
|
+
/** Table entity type names that should be imported from types.ts */
|
|
29
|
+
tableTypeNames?: Set<string>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create a new TypeTracker instance
|
|
33
|
+
*
|
|
34
|
+
* @param options - Optional configuration for the tracker
|
|
35
|
+
*/
|
|
36
|
+
export declare function createTypeTracker(options?: TypeTrackerOptions): TypeTracker;
|
|
9
37
|
/**
|
|
10
38
|
* Convert a GraphQL scalar type to TypeScript type
|
|
11
39
|
*/
|
|
@@ -13,13 +41,19 @@ export declare function scalarToTsType(scalarName: string): string;
|
|
|
13
41
|
/**
|
|
14
42
|
* Convert a CleanTypeRef to a TypeScript type string
|
|
15
43
|
* Handles nested LIST and NON_NULL wrappers
|
|
44
|
+
*
|
|
45
|
+
* @param typeRef - The GraphQL type reference
|
|
46
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
16
47
|
*/
|
|
17
|
-
export declare function typeRefToTsType(typeRef: CleanTypeRef): string;
|
|
48
|
+
export declare function typeRefToTsType(typeRef: CleanTypeRef, tracker?: TypeTracker): string;
|
|
18
49
|
/**
|
|
19
50
|
* Convert a CleanTypeRef to a nullable TypeScript type string
|
|
20
51
|
* (for optional fields that can be null)
|
|
52
|
+
*
|
|
53
|
+
* @param typeRef - The GraphQL type reference
|
|
54
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
21
55
|
*/
|
|
22
|
-
export declare function typeRefToNullableTsType(typeRef: CleanTypeRef): string;
|
|
56
|
+
export declare function typeRefToNullableTsType(typeRef: CleanTypeRef, tracker?: TypeTracker): string;
|
|
23
57
|
/**
|
|
24
58
|
* Check if a type reference is required (wrapped in NON_NULL)
|
|
25
59
|
*/
|
|
@@ -38,20 +72,32 @@ export declare function getTypeBaseName(typeRef: CleanTypeRef): string | null;
|
|
|
38
72
|
export declare function getBaseTypeKind(typeRef: CleanTypeRef): CleanTypeRef['kind'];
|
|
39
73
|
/**
|
|
40
74
|
* Convert CleanArgument to InterfaceProperty for ts-morph
|
|
75
|
+
*
|
|
76
|
+
* @param arg - The GraphQL argument
|
|
77
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
41
78
|
*/
|
|
42
|
-
export declare function argumentToInterfaceProperty(arg: CleanArgument): InterfaceProperty;
|
|
79
|
+
export declare function argumentToInterfaceProperty(arg: CleanArgument, tracker?: TypeTracker): InterfaceProperty;
|
|
43
80
|
/**
|
|
44
81
|
* Convert CleanObjectField to InterfaceProperty for ts-morph
|
|
82
|
+
*
|
|
83
|
+
* @param field - The GraphQL object field
|
|
84
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
45
85
|
*/
|
|
46
|
-
export declare function fieldToInterfaceProperty(field: CleanObjectField): InterfaceProperty;
|
|
86
|
+
export declare function fieldToInterfaceProperty(field: CleanObjectField, tracker?: TypeTracker): InterfaceProperty;
|
|
47
87
|
/**
|
|
48
88
|
* Convert an array of CleanArguments to InterfaceProperty array
|
|
89
|
+
*
|
|
90
|
+
* @param args - The GraphQL arguments
|
|
91
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
49
92
|
*/
|
|
50
|
-
export declare function argumentsToInterfaceProperties(args: CleanArgument[]): InterfaceProperty[];
|
|
93
|
+
export declare function argumentsToInterfaceProperties(args: CleanArgument[], tracker?: TypeTracker): InterfaceProperty[];
|
|
51
94
|
/**
|
|
52
95
|
* Convert an array of CleanObjectFields to InterfaceProperty array
|
|
96
|
+
*
|
|
97
|
+
* @param fields - The GraphQL object fields
|
|
98
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
53
99
|
*/
|
|
54
|
-
export declare function fieldsToInterfaceProperties(fields: CleanObjectField[]): InterfaceProperty[];
|
|
100
|
+
export declare function fieldsToInterfaceProperties(fields: CleanObjectField[], tracker?: TypeTracker): InterfaceProperty[];
|
|
55
101
|
/**
|
|
56
102
|
* Check if a field should be skipped in selections
|
|
57
103
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTypeTracker = createTypeTracker;
|
|
3
4
|
exports.scalarToTsType = scalarToTsType;
|
|
4
5
|
exports.typeRefToTsType = typeRefToTsType;
|
|
5
6
|
exports.typeRefToNullableTsType = typeRefToNullableTsType;
|
|
@@ -22,6 +23,59 @@ exports.getQueryKeyName = getQueryKeyName;
|
|
|
22
23
|
exports.getDocumentConstName = getDocumentConstName;
|
|
23
24
|
const scalars_1 = require("./scalars");
|
|
24
25
|
// ============================================================================
|
|
26
|
+
// Type Tracker for Collecting Referenced Types
|
|
27
|
+
// ============================================================================
|
|
28
|
+
/**
|
|
29
|
+
* Types that should not be tracked (scalars, built-ins, internal types)
|
|
30
|
+
*/
|
|
31
|
+
const SKIP_TYPE_TRACKING = new Set([
|
|
32
|
+
...scalars_1.SCALAR_NAMES,
|
|
33
|
+
// GraphQL built-ins
|
|
34
|
+
'Query',
|
|
35
|
+
'Mutation',
|
|
36
|
+
'Subscription',
|
|
37
|
+
'__Schema',
|
|
38
|
+
'__Type',
|
|
39
|
+
'__Field',
|
|
40
|
+
'__InputValue',
|
|
41
|
+
'__EnumValue',
|
|
42
|
+
'__Directive',
|
|
43
|
+
// Connection types (handled separately)
|
|
44
|
+
'PageInfo',
|
|
45
|
+
]);
|
|
46
|
+
/**
|
|
47
|
+
* Create a new TypeTracker instance
|
|
48
|
+
*
|
|
49
|
+
* @param options - Optional configuration for the tracker
|
|
50
|
+
*/
|
|
51
|
+
function createTypeTracker(options) {
|
|
52
|
+
const referencedTypes = new Set();
|
|
53
|
+
const tableTypeNames = options?.tableTypeNames ?? new Set();
|
|
54
|
+
return {
|
|
55
|
+
referencedTypes,
|
|
56
|
+
track(typeName) {
|
|
57
|
+
if (typeName && !SKIP_TYPE_TRACKING.has(typeName)) {
|
|
58
|
+
referencedTypes.add(typeName);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
getImportableTypes() {
|
|
62
|
+
// Return schema types (not table entity types)
|
|
63
|
+
return Array.from(referencedTypes)
|
|
64
|
+
.filter((name) => !tableTypeNames.has(name))
|
|
65
|
+
.sort();
|
|
66
|
+
},
|
|
67
|
+
getTableTypes() {
|
|
68
|
+
// Return table entity types only
|
|
69
|
+
return Array.from(referencedTypes)
|
|
70
|
+
.filter((name) => tableTypeNames.has(name))
|
|
71
|
+
.sort();
|
|
72
|
+
},
|
|
73
|
+
reset() {
|
|
74
|
+
referencedTypes.clear();
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// ============================================================================
|
|
25
79
|
// GraphQL to TypeScript Type Mapping
|
|
26
80
|
// ============================================================================
|
|
27
81
|
/**
|
|
@@ -36,32 +90,41 @@ function scalarToTsType(scalarName) {
|
|
|
36
90
|
/**
|
|
37
91
|
* Convert a CleanTypeRef to a TypeScript type string
|
|
38
92
|
* Handles nested LIST and NON_NULL wrappers
|
|
93
|
+
*
|
|
94
|
+
* @param typeRef - The GraphQL type reference
|
|
95
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
39
96
|
*/
|
|
40
|
-
function typeRefToTsType(typeRef) {
|
|
97
|
+
function typeRefToTsType(typeRef, tracker) {
|
|
41
98
|
switch (typeRef.kind) {
|
|
42
99
|
case 'NON_NULL':
|
|
43
100
|
// Non-null wrapper - unwrap and return the inner type
|
|
44
101
|
if (typeRef.ofType) {
|
|
45
|
-
return typeRefToTsType(typeRef.ofType);
|
|
102
|
+
return typeRefToTsType(typeRef.ofType, tracker);
|
|
46
103
|
}
|
|
47
104
|
return 'unknown';
|
|
48
105
|
case 'LIST':
|
|
49
106
|
// List wrapper - wrap inner type in array
|
|
50
107
|
if (typeRef.ofType) {
|
|
51
|
-
const innerType = typeRefToTsType(typeRef.ofType);
|
|
108
|
+
const innerType = typeRefToTsType(typeRef.ofType, tracker);
|
|
52
109
|
return `${innerType}[]`;
|
|
53
110
|
}
|
|
54
111
|
return 'unknown[]';
|
|
55
112
|
case 'SCALAR':
|
|
56
113
|
// Scalar type - map to TS type
|
|
57
114
|
return scalarToTsType(typeRef.name ?? 'unknown');
|
|
58
|
-
case 'ENUM':
|
|
59
|
-
// Enum type - use the GraphQL enum name
|
|
60
|
-
|
|
115
|
+
case 'ENUM': {
|
|
116
|
+
// Enum type - use the GraphQL enum name and track it
|
|
117
|
+
const typeName = typeRef.name ?? 'string';
|
|
118
|
+
tracker?.track(typeName);
|
|
119
|
+
return typeName;
|
|
120
|
+
}
|
|
61
121
|
case 'OBJECT':
|
|
62
|
-
case 'INPUT_OBJECT':
|
|
63
|
-
// Object types - use the GraphQL type name
|
|
64
|
-
|
|
122
|
+
case 'INPUT_OBJECT': {
|
|
123
|
+
// Object types - use the GraphQL type name and track it
|
|
124
|
+
const typeName = typeRef.name ?? 'unknown';
|
|
125
|
+
tracker?.track(typeName);
|
|
126
|
+
return typeName;
|
|
127
|
+
}
|
|
65
128
|
default:
|
|
66
129
|
return 'unknown';
|
|
67
130
|
}
|
|
@@ -69,9 +132,12 @@ function typeRefToTsType(typeRef) {
|
|
|
69
132
|
/**
|
|
70
133
|
* Convert a CleanTypeRef to a nullable TypeScript type string
|
|
71
134
|
* (for optional fields that can be null)
|
|
135
|
+
*
|
|
136
|
+
* @param typeRef - The GraphQL type reference
|
|
137
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
72
138
|
*/
|
|
73
|
-
function typeRefToNullableTsType(typeRef) {
|
|
74
|
-
const baseType = typeRefToTsType(typeRef);
|
|
139
|
+
function typeRefToNullableTsType(typeRef, tracker) {
|
|
140
|
+
const baseType = typeRefToTsType(typeRef, tracker);
|
|
75
141
|
// If the outer type is NON_NULL, it's required
|
|
76
142
|
if (typeRef.kind === 'NON_NULL') {
|
|
77
143
|
return baseType;
|
|
@@ -122,37 +188,49 @@ function getBaseTypeKind(typeRef) {
|
|
|
122
188
|
// ============================================================================
|
|
123
189
|
/**
|
|
124
190
|
* Convert CleanArgument to InterfaceProperty for ts-morph
|
|
191
|
+
*
|
|
192
|
+
* @param arg - The GraphQL argument
|
|
193
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
125
194
|
*/
|
|
126
|
-
function argumentToInterfaceProperty(arg) {
|
|
195
|
+
function argumentToInterfaceProperty(arg, tracker) {
|
|
127
196
|
return {
|
|
128
197
|
name: arg.name,
|
|
129
|
-
type: typeRefToTsType(arg.type),
|
|
198
|
+
type: typeRefToTsType(arg.type, tracker),
|
|
130
199
|
optional: !isTypeRequired(arg.type),
|
|
131
200
|
docs: arg.description ? [arg.description] : undefined,
|
|
132
201
|
};
|
|
133
202
|
}
|
|
134
203
|
/**
|
|
135
204
|
* Convert CleanObjectField to InterfaceProperty for ts-morph
|
|
205
|
+
*
|
|
206
|
+
* @param field - The GraphQL object field
|
|
207
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
136
208
|
*/
|
|
137
|
-
function fieldToInterfaceProperty(field) {
|
|
209
|
+
function fieldToInterfaceProperty(field, tracker) {
|
|
138
210
|
return {
|
|
139
211
|
name: field.name,
|
|
140
|
-
type: typeRefToNullableTsType(field.type),
|
|
212
|
+
type: typeRefToNullableTsType(field.type, tracker),
|
|
141
213
|
optional: false, // Fields are always present, just potentially null
|
|
142
214
|
docs: field.description ? [field.description] : undefined,
|
|
143
215
|
};
|
|
144
216
|
}
|
|
145
217
|
/**
|
|
146
218
|
* Convert an array of CleanArguments to InterfaceProperty array
|
|
219
|
+
*
|
|
220
|
+
* @param args - The GraphQL arguments
|
|
221
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
147
222
|
*/
|
|
148
|
-
function argumentsToInterfaceProperties(args) {
|
|
149
|
-
return args.map(argumentToInterfaceProperty);
|
|
223
|
+
function argumentsToInterfaceProperties(args, tracker) {
|
|
224
|
+
return args.map((arg) => argumentToInterfaceProperty(arg, tracker));
|
|
150
225
|
}
|
|
151
226
|
/**
|
|
152
227
|
* Convert an array of CleanObjectFields to InterfaceProperty array
|
|
228
|
+
*
|
|
229
|
+
* @param fields - The GraphQL object fields
|
|
230
|
+
* @param tracker - Optional TypeTracker to collect referenced types
|
|
153
231
|
*/
|
|
154
|
-
function fieldsToInterfaceProperties(fields) {
|
|
155
|
-
return fields.map(fieldToInterfaceProperty);
|
|
232
|
+
function fieldsToInterfaceProperties(fields, tracker) {
|
|
233
|
+
return fields.map((field) => fieldToInterfaceProperty(field, tracker));
|
|
156
234
|
}
|
|
157
235
|
// ============================================================================
|
|
158
236
|
// Type Filtering
|