@constructive-io/graphql-codegen 2.23.3 → 2.24.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 (86) hide show
  1. package/README.md +147 -2
  2. package/cli/codegen/babel-ast.d.ts +46 -0
  3. package/cli/codegen/babel-ast.js +145 -0
  4. package/cli/codegen/barrel.d.ts +7 -2
  5. package/cli/codegen/barrel.js +159 -97
  6. package/cli/codegen/client.js +61 -0
  7. package/cli/codegen/custom-mutations.d.ts +2 -12
  8. package/cli/codegen/custom-mutations.js +116 -124
  9. package/cli/codegen/custom-queries.d.ts +2 -10
  10. package/cli/codegen/custom-queries.js +246 -335
  11. package/cli/codegen/index.d.ts +3 -0
  12. package/cli/codegen/index.js +72 -3
  13. package/cli/codegen/invalidation.d.ts +20 -0
  14. package/cli/codegen/invalidation.js +327 -0
  15. package/cli/codegen/mutation-keys.d.ts +24 -0
  16. package/cli/codegen/mutation-keys.js +247 -0
  17. package/cli/codegen/mutations.d.ts +3 -19
  18. package/cli/codegen/mutations.js +372 -383
  19. package/cli/codegen/orm/barrel.d.ts +1 -1
  20. package/cli/codegen/orm/barrel.js +42 -10
  21. package/cli/codegen/orm/client-generator.d.ts +1 -19
  22. package/cli/codegen/orm/client-generator.js +108 -77
  23. package/cli/codegen/orm/custom-ops-generator.d.ts +1 -12
  24. package/cli/codegen/orm/custom-ops-generator.js +192 -235
  25. package/cli/codegen/orm/input-types-generator.d.ts +13 -1
  26. package/cli/codegen/orm/input-types-generator.js +403 -147
  27. package/cli/codegen/orm/model-generator.d.ts +1 -19
  28. package/cli/codegen/orm/model-generator.js +229 -234
  29. package/cli/codegen/queries.d.ts +3 -11
  30. package/cli/codegen/queries.js +582 -389
  31. package/cli/codegen/query-keys.d.ts +15 -0
  32. package/cli/codegen/query-keys.js +477 -0
  33. package/cli/codegen/scalars.js +1 -0
  34. package/cli/codegen/schema-types-generator.d.ts +15 -10
  35. package/cli/codegen/schema-types-generator.js +87 -175
  36. package/cli/codegen/type-resolver.d.ts +1 -30
  37. package/cli/codegen/type-resolver.js +0 -53
  38. package/cli/codegen/types.d.ts +1 -1
  39. package/cli/codegen/types.js +76 -21
  40. package/esm/cli/codegen/babel-ast.d.ts +46 -0
  41. package/esm/cli/codegen/babel-ast.js +97 -0
  42. package/esm/cli/codegen/barrel.d.ts +7 -2
  43. package/esm/cli/codegen/barrel.js +126 -97
  44. package/esm/cli/codegen/client.js +61 -0
  45. package/esm/cli/codegen/custom-mutations.d.ts +2 -12
  46. package/esm/cli/codegen/custom-mutations.js +83 -124
  47. package/esm/cli/codegen/custom-queries.d.ts +2 -10
  48. package/esm/cli/codegen/custom-queries.js +214 -336
  49. package/esm/cli/codegen/index.d.ts +3 -0
  50. package/esm/cli/codegen/index.js +68 -2
  51. package/esm/cli/codegen/invalidation.d.ts +20 -0
  52. package/esm/cli/codegen/invalidation.js +291 -0
  53. package/esm/cli/codegen/mutation-keys.d.ts +24 -0
  54. package/esm/cli/codegen/mutation-keys.js +211 -0
  55. package/esm/cli/codegen/mutations.d.ts +3 -19
  56. package/esm/cli/codegen/mutations.js +340 -384
  57. package/esm/cli/codegen/orm/barrel.d.ts +1 -1
  58. package/esm/cli/codegen/orm/barrel.js +10 -11
  59. package/esm/cli/codegen/orm/client-generator.d.ts +1 -19
  60. package/esm/cli/codegen/orm/client-generator.js +76 -78
  61. package/esm/cli/codegen/orm/custom-ops-generator.d.ts +1 -12
  62. package/esm/cli/codegen/orm/custom-ops-generator.js +160 -236
  63. package/esm/cli/codegen/orm/input-types-generator.d.ts +13 -1
  64. package/esm/cli/codegen/orm/input-types-generator.js +371 -148
  65. package/esm/cli/codegen/orm/model-generator.d.ts +1 -19
  66. package/esm/cli/codegen/orm/model-generator.js +197 -235
  67. package/esm/cli/codegen/queries.d.ts +3 -11
  68. package/esm/cli/codegen/queries.js +550 -390
  69. package/esm/cli/codegen/query-keys.d.ts +15 -0
  70. package/esm/cli/codegen/query-keys.js +441 -0
  71. package/esm/cli/codegen/scalars.js +1 -0
  72. package/esm/cli/codegen/schema-types-generator.d.ts +15 -10
  73. package/esm/cli/codegen/schema-types-generator.js +54 -175
  74. package/esm/cli/codegen/type-resolver.d.ts +1 -30
  75. package/esm/cli/codegen/type-resolver.js +0 -49
  76. package/esm/cli/codegen/types.d.ts +1 -1
  77. package/esm/cli/codegen/types.js +44 -22
  78. package/esm/types/config.d.ts +75 -0
  79. package/esm/types/config.js +18 -0
  80. package/package.json +6 -4
  81. package/types/config.d.ts +75 -0
  82. package/types/config.js +19 -1
  83. package/cli/codegen/ts-ast.d.ts +0 -124
  84. package/cli/codegen/ts-ast.js +0 -280
  85. package/esm/cli/codegen/ts-ast.d.ts +0 -124
  86. package/esm/cli/codegen/ts-ast.js +0 -260
@@ -1,15 +1,10 @@
1
- import { createProject, createSourceFile, getMinimalFormattedOutput, createFileHeader, createInterface, createTypeAlias, addSectionComment, } from './ts-ast';
1
+ import * as t from '@babel/types';
2
+ import { generateCode } from './babel-ast';
2
3
  import { getTypeBaseName } from './type-resolver';
3
4
  import { scalarToTsType, SCALAR_NAMES, BASE_FILTER_TYPE_NAMES, } from './scalars';
4
- // ============================================================================
5
- // Constants
6
- // ============================================================================
7
- /**
8
- * Types that should not be generated (scalars, built-ins, types generated elsewhere)
9
- */
5
+ import { getGeneratedFileHeader } from './utils';
10
6
  const SKIP_TYPES = new Set([
11
7
  ...SCALAR_NAMES,
12
- // GraphQL built-ins
13
8
  'Query',
14
9
  'Mutation',
15
10
  'Subscription',
@@ -19,27 +14,9 @@ const SKIP_TYPES = new Set([
19
14
  '__InputValue',
20
15
  '__EnumValue',
21
16
  '__Directive',
22
- // Note: PageInfo and Cursor are NOT skipped - they're needed by Connection types
23
- // Base filter types (generated in types.ts via filters.ts)
24
17
  ...BASE_FILTER_TYPE_NAMES,
25
18
  ]);
26
- /**
27
- * Type name patterns to skip (regex patterns)
28
- *
29
- * Note: We intentionally DO NOT skip Connection, Edge, Filter, Patch, Condition,
30
- * or OrderBy types because they may be referenced by custom operations.
31
- * Previously Condition and OrderBy were skipped but they ARE needed for
32
- * custom queries like `schemata`, `apiSchemata`, etc.
33
- */
34
- const SKIP_TYPE_PATTERNS = [
35
- // Currently no patterns are skipped - all types may be needed by custom operations
36
- ];
37
- // ============================================================================
38
- // Type Conversion Utilities
39
- // ============================================================================
40
- /**
41
- * Convert a CleanTypeRef to TypeScript type string
42
- */
19
+ const SKIP_TYPE_PATTERNS = [];
43
20
  function typeRefToTs(typeRef) {
44
21
  if (typeRef.kind === 'NON_NULL') {
45
22
  if (typeRef.ofType) {
@@ -53,48 +30,26 @@ function typeRefToTs(typeRef) {
53
30
  }
54
31
  return 'unknown[]';
55
32
  }
56
- // Scalar or named type
57
33
  const name = typeRef.name ?? 'unknown';
58
34
  return scalarToTsType(name, { unknownScalar: 'name' });
59
35
  }
60
- /**
61
- * Check if a type is required (NON_NULL)
62
- */
63
36
  function isRequired(typeRef) {
64
37
  return typeRef.kind === 'NON_NULL';
65
38
  }
66
- /**
67
- * Check if a type should be skipped
68
- */
69
39
  function shouldSkipType(typeName, tableTypeNames) {
70
- // Skip scalars and built-ins
71
40
  if (SKIP_TYPES.has(typeName))
72
41
  return true;
73
- // Skip table entity types (already in types.ts)
74
42
  if (tableTypeNames.has(typeName))
75
43
  return true;
76
- // Skip types matching patterns
77
44
  for (const pattern of SKIP_TYPE_PATTERNS) {
78
45
  if (pattern.test(typeName))
79
46
  return true;
80
47
  }
81
- // Skip table-specific types that would conflict with inline types in table-based hooks.
82
- // Note: Patch and CreateInput are now NOT exported from hooks (isExported: false),
83
- // so we only skip Filter types here.
84
- // The Filter and OrderBy types are generated inline (non-exported) by table query hooks,
85
- // but schema-types should still generate them for custom operations that need them.
86
- // Actually, we don't skip any table-based types now since hooks don't export them.
87
48
  return false;
88
49
  }
89
- // ============================================================================
90
- // ENUM Types Generator
91
- // ============================================================================
92
- /**
93
- * Add ENUM types to source file
94
- */
95
- function addEnumTypes(sourceFile, typeRegistry, tableTypeNames) {
50
+ function generateEnumTypes(typeRegistry, tableTypeNames) {
51
+ const statements = [];
96
52
  const generatedTypes = new Set();
97
- addSectionComment(sourceFile, 'Enum Types');
98
53
  for (const [typeName, typeInfo] of typeRegistry) {
99
54
  if (typeInfo.kind !== 'ENUM')
100
55
  continue;
@@ -102,23 +57,17 @@ function addEnumTypes(sourceFile, typeRegistry, tableTypeNames) {
102
57
  continue;
103
58
  if (!typeInfo.enumValues || typeInfo.enumValues.length === 0)
104
59
  continue;
105
- const values = typeInfo.enumValues.map((v) => `'${v}'`).join(' | ');
106
- sourceFile.addTypeAlias(createTypeAlias(typeName, values));
60
+ const unionType = t.tsUnionType(typeInfo.enumValues.map((v) => t.tsLiteralType(t.stringLiteral(v))));
61
+ const typeAlias = t.tsTypeAliasDeclaration(t.identifier(typeName), null, unionType);
62
+ statements.push(t.exportNamedDeclaration(typeAlias));
107
63
  generatedTypes.add(typeName);
108
64
  }
109
- return generatedTypes;
65
+ return { statements, generatedTypes };
110
66
  }
111
- // ============================================================================
112
- // INPUT_OBJECT Types Generator
113
- // ============================================================================
114
- /**
115
- * Add INPUT_OBJECT types to source file
116
- * Uses iteration to handle nested input types
117
- */
118
- function addInputObjectTypes(sourceFile, typeRegistry, tableTypeNames, alreadyGenerated) {
67
+ function generateInputObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated) {
68
+ const statements = [];
119
69
  const generatedTypes = new Set(alreadyGenerated);
120
70
  const typesToGenerate = new Set();
121
- // Collect all INPUT_OBJECT types
122
71
  for (const [typeName, typeInfo] of typeRegistry) {
123
72
  if (typeInfo.kind !== 'INPUT_OBJECT')
124
73
  continue;
@@ -128,10 +77,6 @@ function addInputObjectTypes(sourceFile, typeRegistry, tableTypeNames, alreadyGe
128
77
  continue;
129
78
  typesToGenerate.add(typeName);
130
79
  }
131
- if (typesToGenerate.size === 0)
132
- return generatedTypes;
133
- addSectionComment(sourceFile, 'Input Object Types');
134
- // Process all types - no artificial limit
135
80
  while (typesToGenerate.size > 0) {
136
81
  const typeNameResult = typesToGenerate.values().next();
137
82
  if (typeNameResult.done)
@@ -144,18 +89,14 @@ function addInputObjectTypes(sourceFile, typeRegistry, tableTypeNames, alreadyGe
144
89
  if (!typeInfo || typeInfo.kind !== 'INPUT_OBJECT')
145
90
  continue;
146
91
  generatedTypes.add(typeName);
92
+ const properties = [];
147
93
  if (typeInfo.inputFields && typeInfo.inputFields.length > 0) {
148
- const properties = [];
149
94
  for (const field of typeInfo.inputFields) {
150
95
  const optional = !isRequired(field.type);
151
96
  const tsType = typeRefToTs(field.type);
152
- properties.push({
153
- name: field.name,
154
- type: tsType,
155
- optional,
156
- docs: field.description ? [field.description] : undefined,
157
- });
158
- // Follow nested Input types
97
+ const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(tsType))));
98
+ prop.optional = optional;
99
+ properties.push(prop);
159
100
  const baseType = getTypeBaseName(field.type);
160
101
  if (baseType &&
161
102
  !generatedTypes.has(baseType) &&
@@ -166,25 +107,15 @@ function addInputObjectTypes(sourceFile, typeRegistry, tableTypeNames, alreadyGe
166
107
  }
167
108
  }
168
109
  }
169
- sourceFile.addInterface(createInterface(typeName, properties));
170
- }
171
- else {
172
- // Empty input object
173
- sourceFile.addInterface(createInterface(typeName, []));
174
110
  }
111
+ const interfaceDecl = t.tsInterfaceDeclaration(t.identifier(typeName), null, null, t.tsInterfaceBody(properties));
112
+ statements.push(t.exportNamedDeclaration(interfaceDecl));
175
113
  }
176
- return generatedTypes;
114
+ return { statements, generatedTypes };
177
115
  }
178
- // ============================================================================
179
- // UNION Types Generator
180
- // ============================================================================
181
- /**
182
- * Add UNION types to source file
183
- */
184
- function addUnionTypes(sourceFile, typeRegistry, tableTypeNames, alreadyGenerated) {
116
+ function generateUnionTypes(typeRegistry, tableTypeNames, alreadyGenerated) {
117
+ const statements = [];
185
118
  const generatedTypes = new Set(alreadyGenerated);
186
- const unionTypesToGenerate = new Set();
187
- // Collect all UNION types
188
119
  for (const [typeName, typeInfo] of typeRegistry) {
189
120
  if (typeInfo.kind !== 'UNION')
190
121
  continue;
@@ -192,32 +123,17 @@ function addUnionTypes(sourceFile, typeRegistry, tableTypeNames, alreadyGenerate
192
123
  continue;
193
124
  if (generatedTypes.has(typeName))
194
125
  continue;
195
- unionTypesToGenerate.add(typeName);
196
- }
197
- if (unionTypesToGenerate.size === 0)
198
- return generatedTypes;
199
- addSectionComment(sourceFile, 'Union Types');
200
- for (const typeName of unionTypesToGenerate) {
201
- const typeInfo = typeRegistry.get(typeName);
202
- if (!typeInfo || typeInfo.kind !== 'UNION')
203
- continue;
204
126
  if (!typeInfo.possibleTypes || typeInfo.possibleTypes.length === 0)
205
127
  continue;
206
- // Generate union type as TypeScript union
207
- const unionMembers = typeInfo.possibleTypes.join(' | ');
208
- sourceFile.addTypeAlias(createTypeAlias(typeName, unionMembers));
128
+ const unionType = t.tsUnionType(typeInfo.possibleTypes.map((pt) => t.tsTypeReference(t.identifier(pt))));
129
+ const typeAlias = t.tsTypeAliasDeclaration(t.identifier(typeName), null, unionType);
130
+ statements.push(t.exportNamedDeclaration(typeAlias));
209
131
  generatedTypes.add(typeName);
210
132
  }
211
- return generatedTypes;
133
+ return { statements, generatedTypes };
212
134
  }
213
- /**
214
- * Collect return types from Query and Mutation root types
215
- * This dynamically discovers what OBJECT types need to be generated
216
- * based on actual schema structure, not pattern matching
217
- */
218
135
  function collectReturnTypesFromRootTypes(typeRegistry, tableTypeNames) {
219
136
  const returnTypes = new Set();
220
- // Get Query and Mutation root types
221
137
  const queryType = typeRegistry.get('Query');
222
138
  const mutationType = typeRegistry.get('Mutation');
223
139
  const processFields = (fields) => {
@@ -239,30 +155,16 @@ function collectReturnTypesFromRootTypes(typeRegistry, tableTypeNames) {
239
155
  processFields(mutationType.fields);
240
156
  return returnTypes;
241
157
  }
242
- /**
243
- * Add Payload OBJECT types to source file
244
- * These are return types from mutations (e.g., LoginPayload, BootstrapUserPayload)
245
- *
246
- * Also tracks which table entity types are referenced so they can be imported.
247
- *
248
- * Uses dynamic type discovery from Query/Mutation return types instead of pattern matching.
249
- */
250
- function addPayloadObjectTypes(sourceFile, typeRegistry, tableTypeNames, alreadyGenerated) {
158
+ function generatePayloadObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated) {
159
+ const statements = [];
251
160
  const generatedTypes = new Set(alreadyGenerated);
252
161
  const referencedTableTypes = new Set();
253
- // Dynamically collect return types from Query and Mutation
254
162
  const typesToGenerate = collectReturnTypesFromRootTypes(typeRegistry, tableTypeNames);
255
- // Filter out already generated types
256
163
  for (const typeName of Array.from(typesToGenerate)) {
257
164
  if (generatedTypes.has(typeName)) {
258
165
  typesToGenerate.delete(typeName);
259
166
  }
260
167
  }
261
- if (typesToGenerate.size === 0) {
262
- return { generatedTypes, referencedTableTypes };
263
- }
264
- addSectionComment(sourceFile, 'Payload/Return Object Types');
265
- // Process all types - no artificial limit
266
168
  while (typesToGenerate.size > 0) {
267
169
  const typeNameResult = typesToGenerate.values().next();
268
170
  if (typeNameResult.done)
@@ -275,26 +177,21 @@ function addPayloadObjectTypes(sourceFile, typeRegistry, tableTypeNames, already
275
177
  if (!typeInfo || typeInfo.kind !== 'OBJECT')
276
178
  continue;
277
179
  generatedTypes.add(typeName);
180
+ const properties = [];
278
181
  if (typeInfo.fields && typeInfo.fields.length > 0) {
279
- const properties = [];
280
182
  for (const field of typeInfo.fields) {
281
183
  const baseType = getTypeBaseName(field.type);
282
- // Skip Query and Mutation fields
283
184
  if (baseType === 'Query' || baseType === 'Mutation')
284
185
  continue;
285
186
  const tsType = typeRefToTs(field.type);
286
187
  const isNullable = !isRequired(field.type);
287
- properties.push({
288
- name: field.name,
289
- type: isNullable ? `${tsType} | null` : tsType,
290
- optional: isNullable,
291
- docs: field.description ? [field.description] : undefined,
292
- });
293
- // Track table entity types that are referenced
188
+ const finalType = isNullable ? `${tsType} | null` : tsType;
189
+ const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(finalType))));
190
+ prop.optional = isNullable;
191
+ properties.push(prop);
294
192
  if (baseType && tableTypeNames.has(baseType)) {
295
193
  referencedTableTypes.add(baseType);
296
194
  }
297
- // Follow nested OBJECT types that aren't table types
298
195
  if (baseType &&
299
196
  !generatedTypes.has(baseType) &&
300
197
  !shouldSkipType(baseType, tableTypeNames)) {
@@ -304,59 +201,41 @@ function addPayloadObjectTypes(sourceFile, typeRegistry, tableTypeNames, already
304
201
  }
305
202
  }
306
203
  }
307
- sourceFile.addInterface(createInterface(typeName, properties));
308
- }
309
- else {
310
- // Empty payload object
311
- sourceFile.addInterface(createInterface(typeName, []));
312
204
  }
205
+ const interfaceDecl = t.tsInterfaceDeclaration(t.identifier(typeName), null, null, t.tsInterfaceBody(properties));
206
+ statements.push(t.exportNamedDeclaration(interfaceDecl));
313
207
  }
314
- return { generatedTypes, referencedTableTypes };
208
+ return { statements, generatedTypes, referencedTableTypes };
315
209
  }
316
- // ============================================================================
317
- // Main Generator
318
- // ============================================================================
319
- /**
320
- * Generate comprehensive schema-types.ts file using ts-morph AST
321
- *
322
- * This generates all Input/Payload/Enum types from the TypeRegistry
323
- * that are needed by custom mutation/query hooks.
324
- */
325
210
  export function generateSchemaTypesFile(options) {
326
211
  const { typeRegistry, tableTypeNames } = options;
327
- const project = createProject();
328
- const sourceFile = createSourceFile(project, 'schema-types.ts');
329
- // Add file header
330
- sourceFile.insertText(0, createFileHeader('GraphQL schema types for custom operations') + '\n');
331
- // Track all generated types
212
+ const allStatements = [];
332
213
  let generatedTypes = new Set();
333
- // 1. Generate ENUM types
334
- const enumTypes = addEnumTypes(sourceFile, typeRegistry, tableTypeNames);
335
- generatedTypes = new Set([...generatedTypes, ...enumTypes]);
336
- // 2. Generate UNION types
337
- const unionTypes = addUnionTypes(sourceFile, typeRegistry, tableTypeNames, generatedTypes);
338
- generatedTypes = new Set([...generatedTypes, ...unionTypes]);
339
- // 3. Generate INPUT_OBJECT types
340
- const inputTypes = addInputObjectTypes(sourceFile, typeRegistry, tableTypeNames, generatedTypes);
341
- generatedTypes = new Set([...generatedTypes, ...inputTypes]);
342
- // 4. Generate Payload OBJECT types
343
- const payloadResult = addPayloadObjectTypes(sourceFile, typeRegistry, tableTypeNames, generatedTypes);
344
- // 5. Add imports from types.ts (table entity types + base filter types)
214
+ const enumResult = generateEnumTypes(typeRegistry, tableTypeNames);
215
+ generatedTypes = new Set([...generatedTypes, ...enumResult.generatedTypes]);
216
+ const unionResult = generateUnionTypes(typeRegistry, tableTypeNames, generatedTypes);
217
+ generatedTypes = new Set([...generatedTypes, ...unionResult.generatedTypes]);
218
+ const inputResult = generateInputObjectTypes(typeRegistry, tableTypeNames, generatedTypes);
219
+ generatedTypes = new Set([...generatedTypes, ...inputResult.generatedTypes]);
220
+ const payloadResult = generatePayloadObjectTypes(typeRegistry, tableTypeNames, generatedTypes);
345
221
  const referencedTableTypes = Array.from(payloadResult.referencedTableTypes).sort();
346
- // Always import base filter types since generated Filter interfaces reference them
347
222
  const baseFilterImports = Array.from(BASE_FILTER_TYPE_NAMES).sort();
348
223
  const allTypesImports = [...referencedTableTypes, ...baseFilterImports];
349
224
  if (allTypesImports.length > 0) {
350
- // Insert import after the file header comment
351
- const importStatement = `import type { ${allTypesImports.join(', ')} } from './types';\n\n`;
352
- // Find position after header (after first */ + newlines)
353
- const headerEndIndex = sourceFile.getFullText().indexOf('*/') + 3;
354
- sourceFile.insertText(headerEndIndex, '\n' + importStatement);
225
+ const typesImport = t.importDeclaration(allTypesImports.map((ti) => t.importSpecifier(t.identifier(ti), t.identifier(ti))), t.stringLiteral('./types'));
226
+ typesImport.importKind = 'type';
227
+ allStatements.push(typesImport);
355
228
  }
229
+ allStatements.push(...enumResult.statements);
230
+ allStatements.push(...unionResult.statements);
231
+ allStatements.push(...inputResult.statements);
232
+ allStatements.push(...payloadResult.statements);
233
+ const code = generateCode(allStatements);
234
+ const content = getGeneratedFileHeader('GraphQL schema types for custom operations') + '\n\n' + code;
356
235
  return {
357
236
  fileName: 'schema-types.ts',
358
- content: getMinimalFormattedOutput(sourceFile),
359
- generatedEnums: Array.from(enumTypes).sort(),
237
+ content,
238
+ generatedEnums: Array.from(enumResult.generatedTypes).sort(),
360
239
  referencedTableTypes,
361
240
  };
362
241
  }
@@ -4,8 +4,7 @@
4
4
  * Utilities for converting CleanTypeRef and other GraphQL types
5
5
  * into TypeScript type strings and interface definitions.
6
6
  */
7
- import type { CleanTypeRef, CleanArgument, CleanObjectField } from '../../types/schema';
8
- import type { InterfaceProperty } from './ts-ast';
7
+ import type { CleanTypeRef, CleanObjectField } from '../../types/schema';
9
8
  /**
10
9
  * Interface for tracking referenced types during code generation
11
10
  */
@@ -70,34 +69,6 @@ export declare function getTypeBaseName(typeRef: CleanTypeRef): string | null;
70
69
  * Get the base type kind (unwrapping LIST and NON_NULL)
71
70
  */
72
71
  export declare function getBaseTypeKind(typeRef: CleanTypeRef): CleanTypeRef['kind'];
73
- /**
74
- * Convert CleanArgument to InterfaceProperty for ts-morph
75
- *
76
- * @param arg - The GraphQL argument
77
- * @param tracker - Optional TypeTracker to collect referenced types
78
- */
79
- export declare function argumentToInterfaceProperty(arg: CleanArgument, tracker?: TypeTracker): InterfaceProperty;
80
- /**
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
85
- */
86
- export declare function fieldToInterfaceProperty(field: CleanObjectField, tracker?: TypeTracker): InterfaceProperty;
87
- /**
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
92
- */
93
- export declare function argumentsToInterfaceProperties(args: CleanArgument[], tracker?: TypeTracker): InterfaceProperty[];
94
- /**
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
99
- */
100
- export declare function fieldsToInterfaceProperties(fields: CleanObjectField[], tracker?: TypeTracker): InterfaceProperty[];
101
72
  /**
102
73
  * Check if a field should be skipped in selections
103
74
  */
@@ -161,55 +161,6 @@ export function getBaseTypeKind(typeRef) {
161
161
  return typeRef.kind;
162
162
  }
163
163
  // ============================================================================
164
- // Interface Property Generation
165
- // ============================================================================
166
- /**
167
- * Convert CleanArgument to InterfaceProperty for ts-morph
168
- *
169
- * @param arg - The GraphQL argument
170
- * @param tracker - Optional TypeTracker to collect referenced types
171
- */
172
- export function argumentToInterfaceProperty(arg, tracker) {
173
- return {
174
- name: arg.name,
175
- type: typeRefToTsType(arg.type, tracker),
176
- optional: !isTypeRequired(arg.type),
177
- docs: arg.description ? [arg.description] : undefined,
178
- };
179
- }
180
- /**
181
- * Convert CleanObjectField to InterfaceProperty for ts-morph
182
- *
183
- * @param field - The GraphQL object field
184
- * @param tracker - Optional TypeTracker to collect referenced types
185
- */
186
- export function fieldToInterfaceProperty(field, tracker) {
187
- return {
188
- name: field.name,
189
- type: typeRefToNullableTsType(field.type, tracker),
190
- optional: false, // Fields are always present, just potentially null
191
- docs: field.description ? [field.description] : undefined,
192
- };
193
- }
194
- /**
195
- * Convert an array of CleanArguments to InterfaceProperty array
196
- *
197
- * @param args - The GraphQL arguments
198
- * @param tracker - Optional TypeTracker to collect referenced types
199
- */
200
- export function argumentsToInterfaceProperties(args, tracker) {
201
- return args.map((arg) => argumentToInterfaceProperty(arg, tracker));
202
- }
203
- /**
204
- * Convert an array of CleanObjectFields to InterfaceProperty array
205
- *
206
- * @param fields - The GraphQL object fields
207
- * @param tracker - Optional TypeTracker to collect referenced types
208
- */
209
- export function fieldsToInterfaceProperties(fields, tracker) {
210
- return fields.map((field) => fieldToInterfaceProperty(field, tracker));
211
- }
212
- // ============================================================================
213
164
  // Type Filtering
214
165
  // ============================================================================
215
166
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Types generator - generates types.ts with entity interfaces using AST
2
+ * Types generator - generates types.ts with entity interfaces using Babel AST
3
3
  */
4
4
  import type { CleanTable } from '../../types/schema';
5
5
  /**
@@ -1,5 +1,6 @@
1
- import { createProject, createSourceFile, getFormattedOutput, createFileHeader, createImport, createInterface, } from './ts-ast';
2
- import { getScalarFields, fieldTypeToTs } from './utils';
1
+ import * as t from '@babel/types';
2
+ import { generateCode } from './babel-ast';
3
+ import { getScalarFields, fieldTypeToTs, getGeneratedFileHeader } from './utils';
3
4
  /** All filter type configurations - scalar and list filters */
4
5
  const FILTER_CONFIGS = [
5
6
  // Scalar filters
@@ -64,16 +65,42 @@ function buildFilterProperties(tsType, operators) {
64
65
  }
65
66
  return props;
66
67
  }
68
+ function parseTypeAnnotation(typeStr) {
69
+ if (typeStr === 'string')
70
+ return t.tsStringKeyword();
71
+ if (typeStr === 'number')
72
+ return t.tsNumberKeyword();
73
+ if (typeStr === 'boolean')
74
+ return t.tsBooleanKeyword();
75
+ if (typeStr === 'unknown')
76
+ return t.tsUnknownKeyword();
77
+ if (typeStr.includes(' | ')) {
78
+ const parts = typeStr.split(' | ').map((p) => parseTypeAnnotation(p.trim()));
79
+ return t.tsUnionType(parts);
80
+ }
81
+ if (typeStr.endsWith('[]')) {
82
+ return t.tsArrayType(parseTypeAnnotation(typeStr.slice(0, -2)));
83
+ }
84
+ if (typeStr === 'null')
85
+ return t.tsNullKeyword();
86
+ return t.tsTypeReference(t.identifier(typeStr));
87
+ }
88
+ function createInterfaceDeclaration(name, properties) {
89
+ const props = properties.map((prop) => {
90
+ const propSig = t.tsPropertySignature(t.identifier(prop.name), t.tsTypeAnnotation(parseTypeAnnotation(prop.type)));
91
+ propSig.optional = prop.optional ?? false;
92
+ return propSig;
93
+ });
94
+ const interfaceDecl = t.tsInterfaceDeclaration(t.identifier(name), null, null, t.tsInterfaceBody(props));
95
+ return t.exportNamedDeclaration(interfaceDecl);
96
+ }
67
97
  /**
68
98
  * Generate types.ts content with all entity interfaces and base filter types
69
99
  */
70
100
  export function generateTypesFile(tables, options = {}) {
71
101
  const { enumsFromSchemaTypes = [] } = options;
72
102
  const enumSet = new Set(enumsFromSchemaTypes);
73
- const project = createProject();
74
- const sourceFile = createSourceFile(project, 'types.ts');
75
- // Add file header
76
- sourceFile.insertText(0, createFileHeader('Entity types and filter types') + '\n\n');
103
+ const statements = [];
77
104
  // Collect which enums are actually used by entity fields
78
105
  const usedEnums = new Set();
79
106
  for (const table of tables) {
@@ -88,16 +115,13 @@ export function generateTypesFile(tables, options = {}) {
88
115
  }
89
116
  // Add import for enum types from schema-types if any are used
90
117
  if (usedEnums.size > 0) {
91
- sourceFile.addImportDeclaration(createImport({
92
- moduleSpecifier: './schema-types',
93
- typeOnlyNamedImports: Array.from(usedEnums).sort(),
94
- }));
95
- sourceFile.addStatements('');
118
+ const specifiers = Array.from(usedEnums)
119
+ .sort()
120
+ .map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
121
+ const importDecl = t.importDeclaration(specifiers, t.stringLiteral('./schema-types'));
122
+ importDecl.importKind = 'type';
123
+ statements.push(importDecl);
96
124
  }
97
- // Add section comment for entity types
98
- sourceFile.addStatements('// ============================================================================');
99
- sourceFile.addStatements('// Entity types');
100
- sourceFile.addStatements('// ============================================================================\n');
101
125
  // Generate entity interfaces
102
126
  for (const table of tables) {
103
127
  const scalarFields = getScalarFields(table);
@@ -105,15 +129,13 @@ export function generateTypesFile(tables, options = {}) {
105
129
  name: field.name,
106
130
  type: `${fieldTypeToTs(field.type)} | null`,
107
131
  }));
108
- sourceFile.addInterface(createInterface(table.name, properties));
132
+ statements.push(createInterfaceDeclaration(table.name, properties));
109
133
  }
110
- // Add section comment for filter types
111
- sourceFile.addStatements('\n// ============================================================================');
112
- sourceFile.addStatements('// Filter types (shared PostGraphile filter interfaces)');
113
- sourceFile.addStatements('// ============================================================================\n');
114
134
  // Generate all filter types
115
135
  for (const { name, tsType, operators } of FILTER_CONFIGS) {
116
- sourceFile.addInterface(createInterface(name, buildFilterProperties(tsType, operators)));
136
+ statements.push(createInterfaceDeclaration(name, buildFilterProperties(tsType, operators)));
117
137
  }
118
- return getFormattedOutput(sourceFile);
138
+ const header = getGeneratedFileHeader('Entity types and filter types');
139
+ const code = generateCode(statements);
140
+ return header + '\n' + code;
119
141
  }