@constructive-io/graphql-codegen 4.5.3 → 4.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/core/codegen/hooks-docs-generator.js +16 -16
  2. package/core/codegen/mutations.js +6 -6
  3. package/core/codegen/orm/custom-ops-generator.d.ts +2 -2
  4. package/core/codegen/orm/custom-ops-generator.js +15 -6
  5. package/core/codegen/orm/docs-generator.js +6 -6
  6. package/core/codegen/orm/index.js +4 -3
  7. package/core/codegen/orm/input-types-generator.d.ts +1 -1
  8. package/core/codegen/orm/input-types-generator.js +41 -17
  9. package/core/codegen/queries.js +12 -10
  10. package/core/codegen/schema-types-generator.d.ts +2 -0
  11. package/core/codegen/schema-types-generator.js +41 -12
  12. package/core/codegen/shared/index.js +2 -0
  13. package/core/codegen/utils.d.ts +14 -0
  14. package/core/codegen/utils.js +87 -0
  15. package/core/introspect/infer-tables.d.ts +5 -0
  16. package/core/introspect/infer-tables.js +54 -4
  17. package/core/pipeline/index.js +2 -1
  18. package/esm/core/codegen/hooks-docs-generator.js +16 -16
  19. package/esm/core/codegen/mutations.js +6 -6
  20. package/esm/core/codegen/orm/custom-ops-generator.d.ts +2 -2
  21. package/esm/core/codegen/orm/custom-ops-generator.js +17 -8
  22. package/esm/core/codegen/orm/docs-generator.js +6 -6
  23. package/esm/core/codegen/orm/index.js +4 -3
  24. package/esm/core/codegen/orm/input-types-generator.d.ts +1 -1
  25. package/esm/core/codegen/orm/input-types-generator.js +43 -19
  26. package/esm/core/codegen/queries.js +12 -10
  27. package/esm/core/codegen/schema-types-generator.d.ts +2 -0
  28. package/esm/core/codegen/schema-types-generator.js +43 -14
  29. package/esm/core/codegen/shared/index.js +2 -0
  30. package/esm/core/codegen/utils.d.ts +14 -0
  31. package/esm/core/codegen/utils.js +86 -0
  32. package/esm/core/introspect/infer-tables.d.ts +5 -0
  33. package/esm/core/introspect/infer-tables.js +55 -5
  34. package/esm/core/pipeline/index.js +2 -1
  35. package/esm/generators/field-selector.js +32 -7
  36. package/esm/generators/mutations.d.ts +1 -2
  37. package/esm/generators/mutations.js +12 -9
  38. package/esm/generators/naming-helpers.d.ts +48 -0
  39. package/esm/generators/naming-helpers.js +154 -0
  40. package/esm/generators/select.d.ts +1 -12
  41. package/esm/generators/select.js +96 -71
  42. package/esm/types/config.d.ts +6 -0
  43. package/esm/types/config.js +1 -0
  44. package/esm/types/query.d.ts +9 -0
  45. package/esm/types/schema.d.ts +8 -0
  46. package/generators/field-selector.js +32 -7
  47. package/generators/mutations.d.ts +1 -2
  48. package/generators/mutations.js +12 -9
  49. package/generators/naming-helpers.d.ts +48 -0
  50. package/generators/naming-helpers.js +169 -0
  51. package/generators/select.d.ts +1 -12
  52. package/generators/select.js +98 -72
  53. package/package.json +6 -6
  54. package/types/config.d.ts +6 -0
  55. package/types/config.js +1 -0
  56. package/types/query.d.ts +9 -0
  57. package/types/schema.d.ts +8 -0
@@ -5,10 +5,10 @@
5
5
  * like login, register, currentUser, etc.
6
6
  */
7
7
  import * as t from '@babel/types';
8
- import { generateCode } from '../babel-ast';
8
+ import { addJSDocComment, generateCode } from '../babel-ast';
9
9
  import { NON_SELECT_TYPES, getSelectTypeName } from '../select-helpers';
10
10
  import { getTypeBaseName, isTypeRequired, scalarToTsType, typeRefToTsType, } from '../type-resolver';
11
- import { getGeneratedFileHeader, ucFirst } from '../utils';
11
+ import { getGeneratedFileHeader, stripSmartComments, ucFirst } from '../utils';
12
12
  /**
13
13
  * Collect all input type names used by operations
14
14
  * Includes Input, Filter, OrderBy, and Condition types
@@ -67,7 +67,7 @@ function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false
67
67
  decl.importKind = typeOnly ? 'type' : 'value';
68
68
  return decl;
69
69
  }
70
- function createVariablesInterface(op) {
70
+ function createVariablesInterface(op, comments = true) {
71
71
  if (op.args.length === 0)
72
72
  return null;
73
73
  const varTypeName = `${ucFirst(op.name)}Variables`;
@@ -75,10 +75,19 @@ function createVariablesInterface(op) {
75
75
  const optional = !isTypeRequired(arg.type);
76
76
  const prop = t.tsPropertySignature(t.identifier(arg.name), t.tsTypeAnnotation(parseTypeAnnotation(typeRefToTsType(arg.type))));
77
77
  prop.optional = optional;
78
+ const argDescription = stripSmartComments(arg.description, comments);
79
+ if (argDescription) {
80
+ addJSDocComment(prop, argDescription.split('\n'));
81
+ }
78
82
  return prop;
79
83
  });
80
84
  const interfaceDecl = t.tsInterfaceDeclaration(t.identifier(varTypeName), null, null, t.tsInterfaceBody(props));
81
- return t.exportNamedDeclaration(interfaceDecl);
85
+ const exportDecl = t.exportNamedDeclaration(interfaceDecl);
86
+ const opDescription = stripSmartComments(op.description, comments);
87
+ if (opDescription) {
88
+ addJSDocComment(exportDecl, [`Variables for ${op.name}`, opDescription]);
89
+ }
90
+ return exportDecl;
82
91
  }
83
92
  function parseTypeAnnotation(typeStr) {
84
93
  if (typeStr === 'string')
@@ -250,7 +259,7 @@ function buildOperationMethod(op, operationType) {
250
259
  /**
251
260
  * Generate the query/index.ts file for custom query operations
252
261
  */
253
- export function generateCustomQueryOpsFile(operations) {
262
+ export function generateCustomQueryOpsFile(operations, comments = true) {
254
263
  const statements = [];
255
264
  // Collect all input type names and payload type names
256
265
  const inputTypeNames = collectInputTypeNamesFromOps(operations);
@@ -278,7 +287,7 @@ export function generateCustomQueryOpsFile(operations) {
278
287
  statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap']));
279
288
  // Generate variable interfaces
280
289
  for (const op of operations) {
281
- const varInterface = createVariablesInterface(op);
290
+ const varInterface = createVariablesInterface(op, comments);
282
291
  if (varInterface)
283
292
  statements.push(varInterface);
284
293
  }
@@ -300,7 +309,7 @@ export function generateCustomQueryOpsFile(operations) {
300
309
  /**
301
310
  * Generate the mutation/index.ts file for custom mutation operations
302
311
  */
303
- export function generateCustomMutationOpsFile(operations) {
312
+ export function generateCustomMutationOpsFile(operations, comments = true) {
304
313
  const statements = [];
305
314
  // Collect all input type names and payload type names
306
315
  const inputTypeNames = collectInputTypeNamesFromOps(operations);
@@ -328,7 +337,7 @@ export function generateCustomMutationOpsFile(operations) {
328
337
  statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap']));
329
338
  // Generate variable interfaces
330
339
  for (const op of operations) {
331
- const varInterface = createVariablesInterface(op);
340
+ const varInterface = createVariablesInterface(op, comments);
332
341
  if (varInterface)
333
342
  statements.push(varInterface);
334
343
  }
@@ -224,7 +224,7 @@ export function getOrmMcpTools(tables, customOperations) {
224
224
  const editableFields = getEditableFields(table);
225
225
  tools.push({
226
226
  name: `orm_${lcFirst(singularName)}_findMany`,
227
- description: `List all ${table.name} records via ORM`,
227
+ description: table.description || `List all ${table.name} records via ORM`,
228
228
  inputSchema: {
229
229
  type: 'object',
230
230
  properties: {
@@ -241,7 +241,7 @@ export function getOrmMcpTools(tables, customOperations) {
241
241
  });
242
242
  tools.push({
243
243
  name: `orm_${lcFirst(singularName)}_findOne`,
244
- description: `Get a single ${table.name} record by ${pk.name}`,
244
+ description: table.description || `Get a single ${table.name} record by ${pk.name}`,
245
245
  inputSchema: {
246
246
  type: 'object',
247
247
  properties: {
@@ -262,7 +262,7 @@ export function getOrmMcpTools(tables, customOperations) {
262
262
  }
263
263
  tools.push({
264
264
  name: `orm_${lcFirst(singularName)}_create`,
265
- description: `Create a new ${table.name} record`,
265
+ description: table.description || `Create a new ${table.name} record`,
266
266
  inputSchema: {
267
267
  type: 'object',
268
268
  properties: createProps,
@@ -283,7 +283,7 @@ export function getOrmMcpTools(tables, customOperations) {
283
283
  }
284
284
  tools.push({
285
285
  name: `orm_${lcFirst(singularName)}_update`,
286
- description: `Update an existing ${table.name} record`,
286
+ description: table.description || `Update an existing ${table.name} record`,
287
287
  inputSchema: {
288
288
  type: 'object',
289
289
  properties: updateProps,
@@ -292,7 +292,7 @@ export function getOrmMcpTools(tables, customOperations) {
292
292
  });
293
293
  tools.push({
294
294
  name: `orm_${lcFirst(singularName)}_delete`,
295
- description: `Delete a ${table.name} record by ${pk.name}`,
295
+ description: table.description || `Delete a ${table.name} record by ${pk.name}`,
296
296
  inputSchema: {
297
297
  type: 'object',
298
298
  properties: {
@@ -350,7 +350,7 @@ export function generateOrmSkills(tables, customOperations) {
350
350
  fileName: `skills/${lcFirst(singularName)}.md`,
351
351
  content: buildSkillFile({
352
352
  name: `orm-${lcFirst(singularName)}`,
353
- description: `ORM operations for ${table.name} records`,
353
+ description: table.description || `ORM operations for ${table.name} records`,
354
354
  language: 'typescript',
355
355
  usage: [
356
356
  `db.${lcFirst(singularName)}.findMany({ select: { id: true } }).execute()`,
@@ -8,6 +8,7 @@ import { generateAllModelFiles } from './model-generator';
8
8
  */
9
9
  export function generateOrm(options) {
10
10
  const { tables, customOperations, sharedTypesPath } = options;
11
+ const commentsEnabled = options.config.codegen?.comments !== false;
11
12
  const files = [];
12
13
  // Use shared types when a sharedTypesPath is provided (unified output mode)
13
14
  const useSharedTypes = !!sharedTypesPath;
@@ -65,7 +66,7 @@ export function generateOrm(options) {
65
66
  }
66
67
  }
67
68
  }
68
- const inputTypesFile = generateInputTypesFile(typeRegistry ?? new Map(), usedInputTypes, tables, usedPayloadTypes);
69
+ const inputTypesFile = generateInputTypesFile(typeRegistry ?? new Map(), usedInputTypes, tables, usedPayloadTypes, commentsEnabled);
69
70
  files.push({
70
71
  path: inputTypesFile.fileName,
71
72
  content: inputTypesFile.content,
@@ -73,11 +74,11 @@ export function generateOrm(options) {
73
74
  }
74
75
  // 5. Generate custom operations (if any)
75
76
  if (hasCustomQueries && customOperations?.queries) {
76
- const queryOpsFile = generateCustomQueryOpsFile(customOperations.queries);
77
+ const queryOpsFile = generateCustomQueryOpsFile(customOperations.queries, commentsEnabled);
77
78
  files.push({ path: queryOpsFile.fileName, content: queryOpsFile.content });
78
79
  }
79
80
  if (hasCustomMutations && customOperations?.mutations) {
80
- const mutationOpsFile = generateCustomMutationOpsFile(customOperations.mutations);
81
+ const mutationOpsFile = generateCustomMutationOpsFile(customOperations.mutations, commentsEnabled);
81
82
  files.push({
82
83
  path: mutationOpsFile.fileName,
83
84
  content: mutationOpsFile.content,
@@ -18,4 +18,4 @@ export declare function collectPayloadTypeNames(operations: Array<{
18
18
  /**
19
19
  * Generate comprehensive input-types.ts file using Babel AST
20
20
  */
21
- export declare function generateInputTypesFile(typeRegistry: TypeRegistry, usedInputTypes: Set<string>, tables?: CleanTable[], usedPayloadTypes?: Set<string>): GeneratedInputTypesFile;
21
+ export declare function generateInputTypesFile(typeRegistry: TypeRegistry, usedInputTypes: Set<string>, tables?: CleanTable[], usedPayloadTypes?: Set<string>, comments?: boolean): GeneratedInputTypesFile;
@@ -12,10 +12,10 @@
12
12
  */
13
13
  import * as t from '@babel/types';
14
14
  import { pluralize } from 'inflekt';
15
- import { addLineComment, generateCode } from '../babel-ast';
15
+ import { addJSDocComment, addLineComment, generateCode } from '../babel-ast';
16
16
  import { SCALAR_NAMES, scalarToFilterType, scalarToTsType } from '../scalars';
17
17
  import { getTypeBaseName } from '../type-resolver';
18
- import { getCreateInputTypeName, getConditionTypeName, getFilterTypeName, getGeneratedFileHeader, getOrderByTypeName, getPrimaryKeyInfo, getTableNames, isRelationField, lcFirst, } from '../utils';
18
+ import { getCreateInputTypeName, getConditionTypeName, getFilterTypeName, getGeneratedFileHeader, getOrderByTypeName, getPrimaryKeyInfo, getTableNames, isRelationField, lcFirst, stripSmartComments, } from '../utils';
19
19
  // ============================================================================
20
20
  // Constants
21
21
  // ============================================================================
@@ -117,19 +117,26 @@ function parseTypeString(typeStr) {
117
117
  /**
118
118
  * Create an interface property signature
119
119
  */
120
- function createPropertySignature(name, typeStr, optional) {
120
+ function createPropertySignature(name, typeStr, optional, description) {
121
121
  const prop = t.tsPropertySignature(t.identifier(name), t.tsTypeAnnotation(parseTypeString(typeStr)));
122
122
  prop.optional = optional;
123
+ if (description) {
124
+ addJSDocComment(prop, description.split('\n'));
125
+ }
123
126
  return prop;
124
127
  }
125
128
  /**
126
129
  * Create an exported interface declaration
127
130
  */
128
- function createExportedInterface(name, properties) {
129
- const props = properties.map((p) => createPropertySignature(p.name, p.type, p.optional));
131
+ function createExportedInterface(name, properties, description) {
132
+ const props = properties.map((p) => createPropertySignature(p.name, p.type, p.optional, p.description));
130
133
  const body = t.tsInterfaceBody(props);
131
134
  const interfaceDecl = t.tsInterfaceDeclaration(t.identifier(name), null, null, body);
132
- return t.exportNamedDeclaration(interfaceDecl);
135
+ const exportDecl = t.exportNamedDeclaration(interfaceDecl);
136
+ if (description) {
137
+ addJSDocComment(exportDecl, description.split('\n'));
138
+ }
139
+ return exportDecl;
133
140
  }
134
141
  /**
135
142
  * Create an exported type alias declaration
@@ -310,7 +317,7 @@ function collectEnumTypesFromTables(tables, typeRegistry) {
310
317
  /**
311
318
  * Generate enum type statements
312
319
  */
313
- function generateEnumTypes(typeRegistry, enumTypeNames) {
320
+ function generateEnumTypes(typeRegistry, enumTypeNames, comments = true) {
314
321
  if (enumTypeNames.size === 0)
315
322
  return [];
316
323
  const statements = [];
@@ -320,7 +327,12 @@ function generateEnumTypes(typeRegistry, enumTypeNames) {
320
327
  continue;
321
328
  const unionType = createStringLiteralUnion(typeInfo.enumValues);
322
329
  const typeAlias = t.tsTypeAliasDeclaration(t.identifier(typeName), null, unionType);
323
- statements.push(t.exportNamedDeclaration(typeAlias));
330
+ const exportDecl = t.exportNamedDeclaration(typeAlias);
331
+ const enumDescription = stripSmartComments(typeInfo.description, comments);
332
+ if (enumDescription) {
333
+ addJSDocComment(exportDecl, enumDescription.split('\n'));
334
+ }
335
+ statements.push(exportDecl);
324
336
  }
325
337
  if (statements.length > 0) {
326
338
  addSectionComment(statements, 'Enum Types');
@@ -380,6 +392,7 @@ function buildEntityProperties(table) {
380
392
  name: field.name,
381
393
  type: isNullable ? `${tsType} | null` : tsType,
382
394
  optional: isNullable,
395
+ description: field.description,
383
396
  });
384
397
  }
385
398
  return properties;
@@ -391,7 +404,7 @@ function generateEntityTypes(tables) {
391
404
  const statements = [];
392
405
  for (const table of tables) {
393
406
  const { typeName } = getTableNames(table);
394
- statements.push(createExportedInterface(typeName, buildEntityProperties(table)));
407
+ statements.push(createExportedInterface(typeName, buildEntityProperties(table), table.description));
395
408
  }
396
409
  if (statements.length > 0) {
397
410
  addSectionComment(statements, 'Entity Types');
@@ -962,7 +975,7 @@ function buildTableCrudTypeNames(tables) {
962
975
  /**
963
976
  * Generate custom input type statements from TypeRegistry
964
977
  */
965
- function generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes) {
978
+ function generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes, comments = true) {
966
979
  const statements = [];
967
980
  const generatedTypes = new Set();
968
981
  const typesToGenerate = new Set(Array.from(usedInputTypes));
@@ -998,7 +1011,12 @@ function generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes)
998
1011
  for (const field of typeInfo.inputFields) {
999
1012
  const optional = !isRequired(field.type);
1000
1013
  const tsType = typeRefToTs(field.type);
1001
- properties.push({ name: field.name, type: tsType, optional });
1014
+ properties.push({
1015
+ name: field.name,
1016
+ type: tsType,
1017
+ optional,
1018
+ description: stripSmartComments(field.description, comments),
1019
+ });
1002
1020
  // Follow nested Input types
1003
1021
  const baseType = getTypeBaseName(field.type);
1004
1022
  if (baseType &&
@@ -1007,12 +1025,17 @@ function generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes)
1007
1025
  typesToGenerate.add(baseType);
1008
1026
  }
1009
1027
  }
1010
- statements.push(createExportedInterface(typeName, properties));
1028
+ statements.push(createExportedInterface(typeName, properties, stripSmartComments(typeInfo.description, comments)));
1011
1029
  }
1012
1030
  else if (typeInfo.kind === 'ENUM' && typeInfo.enumValues) {
1013
1031
  const unionType = createStringLiteralUnion(typeInfo.enumValues);
1014
1032
  const typeAlias = t.tsTypeAliasDeclaration(t.identifier(typeName), null, unionType);
1015
- statements.push(t.exportNamedDeclaration(typeAlias));
1033
+ const enumExportDecl = t.exportNamedDeclaration(typeAlias);
1034
+ const enumDescription = stripSmartComments(typeInfo.description, comments);
1035
+ if (enumDescription) {
1036
+ addJSDocComment(enumExportDecl, enumDescription.split('\n'));
1037
+ }
1038
+ statements.push(enumExportDecl);
1016
1039
  }
1017
1040
  else {
1018
1041
  // Add comment for unsupported type kind
@@ -1046,7 +1069,7 @@ export function collectPayloadTypeNames(operations) {
1046
1069
  /**
1047
1070
  * Generate payload/return type statements
1048
1071
  */
1049
- function generatePayloadTypes(typeRegistry, usedPayloadTypes, alreadyGeneratedTypes) {
1072
+ function generatePayloadTypes(typeRegistry, usedPayloadTypes, alreadyGeneratedTypes, comments = true) {
1050
1073
  const statements = [];
1051
1074
  const generatedTypes = new Set(alreadyGeneratedTypes);
1052
1075
  const typesToGenerate = new Set(Array.from(usedPayloadTypes));
@@ -1094,6 +1117,7 @@ function generatePayloadTypes(typeRegistry, usedPayloadTypes, alreadyGeneratedTy
1094
1117
  name: field.name,
1095
1118
  type: isNullable ? `${tsType} | null` : tsType,
1096
1119
  optional: isNullable,
1120
+ description: stripSmartComments(field.description, comments),
1097
1121
  });
1098
1122
  // Follow nested OBJECT types
1099
1123
  if (baseType &&
@@ -1105,7 +1129,7 @@ function generatePayloadTypes(typeRegistry, usedPayloadTypes, alreadyGeneratedTy
1105
1129
  }
1106
1130
  }
1107
1131
  }
1108
- statements.push(createExportedInterface(typeName, interfaceProps));
1132
+ statements.push(createExportedInterface(typeName, interfaceProps, stripSmartComments(typeInfo.description, comments)));
1109
1133
  // Build Select type
1110
1134
  const selectMembers = [];
1111
1135
  for (const field of typeInfo.fields) {
@@ -1186,7 +1210,7 @@ function generateConnectionFieldsMap(tables, tableByName) {
1186
1210
  /**
1187
1211
  * Generate comprehensive input-types.ts file using Babel AST
1188
1212
  */
1189
- export function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloadTypes) {
1213
+ export function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloadTypes, comments = true) {
1190
1214
  const statements = [];
1191
1215
  const tablesList = tables ?? [];
1192
1216
  const hasTables = tablesList.length > 0;
@@ -1198,7 +1222,7 @@ export function generateInputTypesFile(typeRegistry, usedInputTypes, tables, use
1198
1222
  // 1. Scalar filter types
1199
1223
  statements.push(...generateScalarFilterTypes());
1200
1224
  // 2. Enum types used by table fields
1201
- statements.push(...generateEnumTypes(typeRegistry, enumTypes));
1225
+ statements.push(...generateEnumTypes(typeRegistry, enumTypes, comments));
1202
1226
  // 2b. Unknown/custom scalar aliases for schema-specific scalars
1203
1227
  statements.push(...generateCustomScalarTypes(customScalarTypes));
1204
1228
  // 3. Entity and relation types (if tables provided)
@@ -1222,7 +1246,7 @@ export function generateInputTypesFile(typeRegistry, usedInputTypes, tables, use
1222
1246
  statements.push(...generateConnectionFieldsMap(tablesList, tableByName));
1223
1247
  // 7. Custom input types from TypeRegistry
1224
1248
  const tableCrudTypes = tables ? buildTableCrudTypeNames(tables) : undefined;
1225
- statements.push(...generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes));
1249
+ statements.push(...generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes, comments));
1226
1250
  // 8. Payload/return types for custom operations
1227
1251
  if (usedPayloadTypes && usedPayloadTypes.size > 0) {
1228
1252
  const alreadyGeneratedTypes = new Set();
@@ -1232,7 +1256,7 @@ export function generateInputTypesFile(typeRegistry, usedInputTypes, tables, use
1232
1256
  alreadyGeneratedTypes.add(typeName);
1233
1257
  }
1234
1258
  }
1235
- statements.push(...generatePayloadTypes(typeRegistry, usedPayloadTypes, alreadyGeneratedTypes));
1259
+ statements.push(...generatePayloadTypes(typeRegistry, usedPayloadTypes, alreadyGeneratedTypes, comments));
1236
1260
  }
1237
1261
  // Generate code with file header
1238
1262
  const header = getGeneratedFileHeader('GraphQL types for ORM client');
@@ -94,8 +94,9 @@ export function generateListQueryHook(table, options = {}) {
94
94
  };
95
95
  // Hook
96
96
  if (reactQueryEnabled) {
97
+ const descLine = table.description || `Query hook for fetching ${typeName} list`;
97
98
  const docLines = [
98
- `Query hook for fetching ${typeName} list`,
99
+ descLine,
99
100
  '',
100
101
  '@example',
101
102
  '```tsx',
@@ -167,7 +168,7 @@ export function generateListQueryHook(table, options = {}) {
167
168
  ]);
168
169
  const f1Decl = exportAsyncDeclareFunction(fetchFnName, createSTypeParam(selectTypeName), [createFunctionParam('params', f1ParamType)], typeRef('Promise', [listResultTypeAST(sRef())]));
169
170
  addJSDocComment(f1Decl, [
170
- `Fetch ${typeName} list without React hooks`,
171
+ table.description || `Fetch ${typeName} list without React hooks`,
171
172
  '',
172
173
  '@example',
173
174
  '```ts',
@@ -209,7 +210,7 @@ export function generateListQueryHook(table, options = {}) {
209
210
  createFunctionParam('params', p1ParamType),
210
211
  ], typeRef('Promise', [t.tsVoidKeyword()]));
211
212
  addJSDocComment(p1Decl, [
212
- `Prefetch ${typeName} list for SSR or cache warming`,
213
+ table.description || `Prefetch ${typeName} list for SSR or cache warming`,
213
214
  '',
214
215
  '@example',
215
216
  '```ts',
@@ -245,9 +246,9 @@ export function generateListQueryHook(table, options = {}) {
245
246
  createFunctionParam('params', pImplParamType),
246
247
  ], pBody, typeRef('Promise', [t.tsVoidKeyword()])));
247
248
  }
248
- const headerText = reactQueryEnabled
249
+ const headerText = table.description || (reactQueryEnabled
249
250
  ? `List query hook for ${typeName}`
250
- : `List query functions for ${typeName}`;
251
+ : `List query functions for ${typeName}`);
251
252
  return {
252
253
  fileName: getListQueryFileName(table),
253
254
  content: generateHookFileCode(headerText, statements),
@@ -331,8 +332,9 @@ export function generateSingleQueryHook(table, options = {}) {
331
332
  };
332
333
  // Hook
333
334
  if (reactQueryEnabled) {
335
+ const singleDescLine = table.description || `Query hook for fetching a single ${typeName}`;
334
336
  const docLines = [
335
- `Query hook for fetching a single ${typeName}`,
337
+ singleDescLine,
336
338
  '',
337
339
  '@example',
338
340
  '```tsx',
@@ -409,7 +411,7 @@ export function generateSingleQueryHook(table, options = {}) {
409
411
  ]);
410
412
  const f1Decl = exportAsyncDeclareFunction(fetchFnName, createSTypeParam(selectTypeName), [createFunctionParam('params', f1ParamType)], typeRef('Promise', [singleResultTypeAST(sRef())]));
411
413
  addJSDocComment(f1Decl, [
412
- `Fetch a single ${typeName} without React hooks`,
414
+ table.description || `Fetch a single ${typeName} without React hooks`,
413
415
  '',
414
416
  '@example',
415
417
  '```ts',
@@ -451,7 +453,7 @@ export function generateSingleQueryHook(table, options = {}) {
451
453
  createFunctionParam('params', p1ParamType),
452
454
  ], typeRef('Promise', [t.tsVoidKeyword()]));
453
455
  addJSDocComment(p1Decl, [
454
- `Prefetch a single ${typeName} for SSR or cache warming`,
456
+ table.description || `Prefetch a single ${typeName} for SSR or cache warming`,
455
457
  '',
456
458
  '@example',
457
459
  '```ts',
@@ -489,9 +491,9 @@ export function generateSingleQueryHook(table, options = {}) {
489
491
  createFunctionParam('params', pImplParamType),
490
492
  ], pBody, typeRef('Promise', [t.tsVoidKeyword()])));
491
493
  }
492
- const headerText = reactQueryEnabled
494
+ const headerText = table.description || (reactQueryEnabled
493
495
  ? `Single item query hook for ${typeName}`
494
- : `Single item query functions for ${typeName}`;
496
+ : `Single item query functions for ${typeName}`);
495
497
  return {
496
498
  fileName: getSingleQueryFileName(table),
497
499
  content: generateHookFileCode(headerText, statements),
@@ -22,6 +22,8 @@ export interface GeneratedSchemaTypesFile {
22
22
  export interface GenerateSchemaTypesOptions {
23
23
  typeRegistry: TypeRegistry;
24
24
  tableTypeNames: Set<string>;
25
+ /** Include descriptions as JSDoc comments. @default true */
26
+ comments?: boolean;
25
27
  }
26
28
  export interface PayloadTypesResult {
27
29
  statements: t.Statement[];
@@ -12,10 +12,10 @@
12
12
  * Uses Babel AST for robust code generation.
13
13
  */
14
14
  import * as t from '@babel/types';
15
- import { generateCode } from './babel-ast';
15
+ import { addJSDocComment, generateCode } from './babel-ast';
16
16
  import { BASE_FILTER_TYPE_NAMES, SCALAR_NAMES, scalarToTsType, } from './scalars';
17
17
  import { getTypeBaseName } from './type-resolver';
18
- import { getGeneratedFileHeader } from './utils';
18
+ import { getGeneratedFileHeader, stripSmartComments } from './utils';
19
19
  const SKIP_TYPES = new Set([
20
20
  ...SCALAR_NAMES,
21
21
  'Query',
@@ -79,7 +79,7 @@ function generateCustomScalarTypes(customScalarTypes) {
79
79
  }
80
80
  return statements;
81
81
  }
82
- function generateEnumTypes(typeRegistry, tableTypeNames) {
82
+ function generateEnumTypes(typeRegistry, tableTypeNames, comments = true) {
83
83
  const statements = [];
84
84
  const generatedTypes = new Set();
85
85
  for (const [typeName, typeInfo] of typeRegistry) {
@@ -91,12 +91,17 @@ function generateEnumTypes(typeRegistry, tableTypeNames) {
91
91
  continue;
92
92
  const unionType = t.tsUnionType(typeInfo.enumValues.map((v) => t.tsLiteralType(t.stringLiteral(v))));
93
93
  const typeAlias = t.tsTypeAliasDeclaration(t.identifier(typeName), null, unionType);
94
- statements.push(t.exportNamedDeclaration(typeAlias));
94
+ const exportDecl = t.exportNamedDeclaration(typeAlias);
95
+ const enumDescription = stripSmartComments(typeInfo.description, comments);
96
+ if (enumDescription) {
97
+ addJSDocComment(exportDecl, enumDescription.split('\n'));
98
+ }
99
+ statements.push(exportDecl);
95
100
  generatedTypes.add(typeName);
96
101
  }
97
102
  return { statements, generatedTypes };
98
103
  }
99
- function generateInputObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated) {
104
+ function generateInputObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated, comments = true) {
100
105
  const statements = [];
101
106
  const generatedTypes = new Set(alreadyGenerated);
102
107
  const typesToGenerate = new Set();
@@ -128,6 +133,10 @@ function generateInputObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated
128
133
  const tsType = typeRefToTs(field.type);
129
134
  const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(tsType))));
130
135
  prop.optional = optional;
136
+ const fieldDescription = stripSmartComments(field.description, comments);
137
+ if (fieldDescription) {
138
+ addJSDocComment(prop, fieldDescription.split('\n'));
139
+ }
131
140
  properties.push(prop);
132
141
  const baseType = getTypeBaseName(field.type);
133
142
  if (baseType &&
@@ -141,11 +150,16 @@ function generateInputObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated
141
150
  }
142
151
  }
143
152
  const interfaceDecl = t.tsInterfaceDeclaration(t.identifier(typeName), null, null, t.tsInterfaceBody(properties));
144
- statements.push(t.exportNamedDeclaration(interfaceDecl));
153
+ const exportDecl = t.exportNamedDeclaration(interfaceDecl);
154
+ const inputDescription = stripSmartComments(typeInfo.description, comments);
155
+ if (inputDescription) {
156
+ addJSDocComment(exportDecl, inputDescription.split('\n'));
157
+ }
158
+ statements.push(exportDecl);
145
159
  }
146
160
  return { statements, generatedTypes };
147
161
  }
148
- function generateUnionTypes(typeRegistry, tableTypeNames, alreadyGenerated) {
162
+ function generateUnionTypes(typeRegistry, tableTypeNames, alreadyGenerated, comments = true) {
149
163
  const statements = [];
150
164
  const generatedTypes = new Set(alreadyGenerated);
151
165
  for (const [typeName, typeInfo] of typeRegistry) {
@@ -159,7 +173,12 @@ function generateUnionTypes(typeRegistry, tableTypeNames, alreadyGenerated) {
159
173
  continue;
160
174
  const unionType = t.tsUnionType(typeInfo.possibleTypes.map((pt) => t.tsTypeReference(t.identifier(pt))));
161
175
  const typeAlias = t.tsTypeAliasDeclaration(t.identifier(typeName), null, unionType);
162
- statements.push(t.exportNamedDeclaration(typeAlias));
176
+ const exportDecl = t.exportNamedDeclaration(typeAlias);
177
+ const unionDescription = stripSmartComments(typeInfo.description, comments);
178
+ if (unionDescription) {
179
+ addJSDocComment(exportDecl, unionDescription.split('\n'));
180
+ }
181
+ statements.push(exportDecl);
163
182
  generatedTypes.add(typeName);
164
183
  }
165
184
  return { statements, generatedTypes };
@@ -187,7 +206,7 @@ function collectReturnTypesFromRootTypes(typeRegistry, tableTypeNames) {
187
206
  processFields(mutationType.fields);
188
207
  return returnTypes;
189
208
  }
190
- function generatePayloadObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated) {
209
+ function generatePayloadObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated, comments = true) {
191
210
  const statements = [];
192
211
  const generatedTypes = new Set(alreadyGenerated);
193
212
  const referencedTableTypes = new Set();
@@ -220,6 +239,10 @@ function generatePayloadObjectTypes(typeRegistry, tableTypeNames, alreadyGenerat
220
239
  const finalType = isNullable ? `${tsType} | null` : tsType;
221
240
  const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(finalType))));
222
241
  prop.optional = isNullable;
242
+ const fieldDescription = stripSmartComments(field.description, comments);
243
+ if (fieldDescription) {
244
+ addJSDocComment(prop, fieldDescription.split('\n'));
245
+ }
223
246
  properties.push(prop);
224
247
  if (baseType && tableTypeNames.has(baseType)) {
225
248
  referencedTableTypes.add(baseType);
@@ -235,22 +258,28 @@ function generatePayloadObjectTypes(typeRegistry, tableTypeNames, alreadyGenerat
235
258
  }
236
259
  }
237
260
  const interfaceDecl = t.tsInterfaceDeclaration(t.identifier(typeName), null, null, t.tsInterfaceBody(properties));
238
- statements.push(t.exportNamedDeclaration(interfaceDecl));
261
+ const exportDecl = t.exportNamedDeclaration(interfaceDecl);
262
+ const payloadDescription = stripSmartComments(typeInfo.description, comments);
263
+ if (payloadDescription) {
264
+ addJSDocComment(exportDecl, payloadDescription.split('\n'));
265
+ }
266
+ statements.push(exportDecl);
239
267
  }
240
268
  return { statements, generatedTypes, referencedTableTypes };
241
269
  }
242
270
  export function generateSchemaTypesFile(options) {
243
271
  const { typeRegistry, tableTypeNames } = options;
272
+ const comments = options.comments !== false;
244
273
  const allStatements = [];
245
274
  let generatedTypes = new Set();
246
275
  const customScalarTypes = collectCustomScalarTypes(typeRegistry);
247
- const enumResult = generateEnumTypes(typeRegistry, tableTypeNames);
276
+ const enumResult = generateEnumTypes(typeRegistry, tableTypeNames, comments);
248
277
  generatedTypes = new Set([...generatedTypes, ...enumResult.generatedTypes]);
249
- const unionResult = generateUnionTypes(typeRegistry, tableTypeNames, generatedTypes);
278
+ const unionResult = generateUnionTypes(typeRegistry, tableTypeNames, generatedTypes, comments);
250
279
  generatedTypes = new Set([...generatedTypes, ...unionResult.generatedTypes]);
251
- const inputResult = generateInputObjectTypes(typeRegistry, tableTypeNames, generatedTypes);
280
+ const inputResult = generateInputObjectTypes(typeRegistry, tableTypeNames, generatedTypes, comments);
252
281
  generatedTypes = new Set([...generatedTypes, ...inputResult.generatedTypes]);
253
- const payloadResult = generatePayloadObjectTypes(typeRegistry, tableTypeNames, generatedTypes);
282
+ const payloadResult = generatePayloadObjectTypes(typeRegistry, tableTypeNames, generatedTypes, comments);
254
283
  const referencedTableTypes = Array.from(payloadResult.referencedTableTypes).sort();
255
284
  const baseFilterImports = Array.from(BASE_FILTER_TYPE_NAMES).sort();
256
285
  const allTypesImports = [...referencedTableTypes, ...baseFilterImports];
@@ -27,6 +27,7 @@ function exportAllFrom(modulePath) {
27
27
  */
28
28
  export function generateSharedTypes(options) {
29
29
  const { tables, customOperations } = options;
30
+ const commentsEnabled = options.config.codegen?.comments !== false;
30
31
  const files = [];
31
32
  // Collect table type names for import path resolution
32
33
  const tableTypeNames = new Set(tables.map((t) => getTableNames(t).typeName));
@@ -38,6 +39,7 @@ export function generateSharedTypes(options) {
38
39
  const schemaTypesResult = generateSchemaTypesFile({
39
40
  typeRegistry: customOperations.typeRegistry,
40
41
  tableTypeNames,
42
+ comments: commentsEnabled,
41
43
  });
42
44
  // Only include if there's meaningful content
43
45
  if (schemaTypesResult.content.split('\n').length > 10) {
@@ -179,6 +179,20 @@ export declare function hasValidPrimaryKey(table: CleanTable): boolean;
179
179
  * e.g., "cars" for list queries, "car" for detail queries
180
180
  */
181
181
  export declare function getQueryKeyPrefix(table: CleanTable): string;
182
+ /**
183
+ * Strip PostGraphile smart comments and boilerplate from a description string.
184
+ *
185
+ * Smart comments are lines starting with `@` (e.g., `@omit`, `@name newName`).
186
+ * Boilerplate descriptions are generic PostGraphile-generated text that repeats
187
+ * on every mutation input, clientMutationId field, etc.
188
+ *
189
+ * This returns only the meaningful human-readable portion of the comment,
190
+ * or undefined if the result is empty or boilerplate.
191
+ *
192
+ * @param description - Raw description from GraphQL introspection
193
+ * @returns Cleaned description, or undefined if empty/boilerplate
194
+ */
195
+ export declare function stripSmartComments(description: string | null | undefined, enabled?: boolean): string | undefined;
182
196
  /**
183
197
  * Generate a doc comment header for generated files
184
198
  */