@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.
Files changed (96) hide show
  1. package/README.md +15 -3
  2. package/cli/codegen/barrel.d.ts +4 -1
  3. package/cli/codegen/barrel.js +18 -12
  4. package/cli/codegen/client.js +33 -0
  5. package/cli/codegen/custom-mutations.d.ts +11 -1
  6. package/cli/codegen/custom-mutations.js +49 -15
  7. package/cli/codegen/custom-queries.d.ts +8 -0
  8. package/cli/codegen/custom-queries.js +82 -47
  9. package/cli/codegen/gql-ast.js +9 -5
  10. package/cli/codegen/index.js +39 -8
  11. package/cli/codegen/mutations.d.ts +14 -4
  12. package/cli/codegen/mutations.js +114 -28
  13. package/cli/codegen/orm/barrel.js +4 -2
  14. package/cli/codegen/orm/index.js +17 -0
  15. package/cli/codegen/orm/input-types-generator.js +83 -29
  16. package/cli/codegen/orm/model-generator.js +6 -4
  17. package/cli/codegen/queries.d.ts +7 -3
  18. package/cli/codegen/queries.js +185 -158
  19. package/cli/codegen/scalars.d.ts +6 -4
  20. package/cli/codegen/scalars.js +17 -9
  21. package/cli/codegen/schema-types-generator.d.ts +26 -0
  22. package/cli/codegen/schema-types-generator.js +365 -0
  23. package/cli/codegen/ts-ast.d.ts +3 -1
  24. package/cli/codegen/ts-ast.js +2 -2
  25. package/cli/codegen/type-resolver.d.ts +52 -6
  26. package/cli/codegen/type-resolver.js +97 -19
  27. package/cli/codegen/types.d.ts +7 -4
  28. package/cli/codegen/types.js +94 -41
  29. package/cli/codegen/utils.d.ts +20 -2
  30. package/cli/codegen/utils.js +32 -7
  31. package/cli/commands/generate-orm.js +5 -5
  32. package/cli/commands/generate.d.ts +4 -1
  33. package/cli/commands/generate.js +27 -8
  34. package/cli/introspect/transform-schema.d.ts +33 -21
  35. package/cli/introspect/transform-schema.js +31 -21
  36. package/esm/cli/codegen/barrel.d.ts +4 -1
  37. package/esm/cli/codegen/barrel.js +18 -12
  38. package/esm/cli/codegen/client.js +33 -0
  39. package/esm/cli/codegen/custom-mutations.d.ts +11 -1
  40. package/esm/cli/codegen/custom-mutations.js +50 -16
  41. package/esm/cli/codegen/custom-queries.d.ts +8 -0
  42. package/esm/cli/codegen/custom-queries.js +83 -48
  43. package/esm/cli/codegen/gql-ast.js +10 -6
  44. package/esm/cli/codegen/index.js +39 -8
  45. package/esm/cli/codegen/mutations.d.ts +14 -4
  46. package/esm/cli/codegen/mutations.js +115 -29
  47. package/esm/cli/codegen/orm/barrel.js +4 -2
  48. package/esm/cli/codegen/orm/index.js +17 -0
  49. package/esm/cli/codegen/orm/input-types-generator.js +83 -29
  50. package/esm/cli/codegen/orm/model-generator.js +7 -5
  51. package/esm/cli/codegen/queries.d.ts +7 -3
  52. package/esm/cli/codegen/queries.js +186 -159
  53. package/esm/cli/codegen/scalars.d.ts +6 -4
  54. package/esm/cli/codegen/scalars.js +16 -8
  55. package/esm/cli/codegen/schema-types-generator.d.ts +26 -0
  56. package/esm/cli/codegen/schema-types-generator.js +362 -0
  57. package/esm/cli/codegen/ts-ast.d.ts +3 -1
  58. package/esm/cli/codegen/ts-ast.js +2 -2
  59. package/esm/cli/codegen/type-resolver.d.ts +52 -6
  60. package/esm/cli/codegen/type-resolver.js +97 -20
  61. package/esm/cli/codegen/types.d.ts +7 -4
  62. package/esm/cli/codegen/types.js +95 -41
  63. package/esm/cli/codegen/utils.d.ts +20 -2
  64. package/esm/cli/codegen/utils.js +31 -7
  65. package/esm/cli/commands/generate-orm.js +5 -5
  66. package/esm/cli/commands/generate.d.ts +4 -1
  67. package/esm/cli/commands/generate.js +27 -8
  68. package/esm/cli/introspect/transform-schema.d.ts +33 -21
  69. package/esm/cli/introspect/transform-schema.js +31 -21
  70. package/esm/types/config.d.ts +16 -1
  71. package/esm/types/config.js +6 -0
  72. package/esm/types/schema.d.ts +2 -0
  73. package/package.json +8 -6
  74. package/types/config.d.ts +16 -1
  75. package/types/config.js +6 -0
  76. package/types/schema.d.ts +2 -0
  77. package/__tests__/codegen/input-types-generator.test.d.ts +0 -1
  78. package/__tests__/codegen/input-types-generator.test.js +0 -635
  79. package/cli/codegen/filters.d.ts +0 -27
  80. package/cli/codegen/filters.js +0 -357
  81. package/cli/codegen/orm/input-types-generator.test.d.ts +0 -1
  82. package/cli/codegen/orm/input-types-generator.test.js +0 -75
  83. package/cli/codegen/orm/select-types.test.d.ts +0 -11
  84. package/cli/codegen/orm/select-types.test.js +0 -22
  85. package/cli/introspect/transform-schema.test.d.ts +0 -1
  86. package/cli/introspect/transform-schema.test.js +0 -67
  87. package/esm/__tests__/codegen/input-types-generator.test.d.ts +0 -1
  88. package/esm/__tests__/codegen/input-types-generator.test.js +0 -633
  89. package/esm/cli/codegen/filters.d.ts +0 -27
  90. package/esm/cli/codegen/filters.js +0 -351
  91. package/esm/cli/codegen/orm/input-types-generator.test.d.ts +0 -1
  92. package/esm/cli/codegen/orm/input-types-generator.test.js +0 -73
  93. package/esm/cli/codegen/orm/select-types.test.d.ts +0 -11
  94. package/esm/cli/codegen/orm/select-types.test.js +0 -21
  95. package/esm/cli/introspect/transform-schema.test.d.ts +0 -1
  96. 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
+ }
@@ -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
- }>): InterfaceDeclarationStructure;
69
+ }>, options?: {
70
+ isExported?: boolean;
71
+ }): InterfaceDeclarationStructure;
70
72
  /**
71
73
  * Create type alias declaration structure
72
74
  */
@@ -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
- return typeRef.name ?? 'string';
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
- return typeRef.name ?? 'unknown';
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