@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.
- package/core/codegen/hooks-docs-generator.js +16 -16
- package/core/codegen/mutations.js +6 -6
- package/core/codegen/orm/custom-ops-generator.d.ts +2 -2
- package/core/codegen/orm/custom-ops-generator.js +15 -6
- package/core/codegen/orm/docs-generator.js +6 -6
- package/core/codegen/orm/index.js +4 -3
- package/core/codegen/orm/input-types-generator.d.ts +1 -1
- package/core/codegen/orm/input-types-generator.js +41 -17
- package/core/codegen/queries.js +12 -10
- package/core/codegen/schema-types-generator.d.ts +2 -0
- package/core/codegen/schema-types-generator.js +41 -12
- package/core/codegen/shared/index.js +2 -0
- package/core/codegen/utils.d.ts +14 -0
- package/core/codegen/utils.js +87 -0
- package/core/introspect/infer-tables.d.ts +5 -0
- package/core/introspect/infer-tables.js +54 -4
- package/core/pipeline/index.js +2 -1
- package/esm/core/codegen/hooks-docs-generator.js +16 -16
- package/esm/core/codegen/mutations.js +6 -6
- package/esm/core/codegen/orm/custom-ops-generator.d.ts +2 -2
- package/esm/core/codegen/orm/custom-ops-generator.js +17 -8
- package/esm/core/codegen/orm/docs-generator.js +6 -6
- package/esm/core/codegen/orm/index.js +4 -3
- package/esm/core/codegen/orm/input-types-generator.d.ts +1 -1
- package/esm/core/codegen/orm/input-types-generator.js +43 -19
- package/esm/core/codegen/queries.js +12 -10
- package/esm/core/codegen/schema-types-generator.d.ts +2 -0
- package/esm/core/codegen/schema-types-generator.js +43 -14
- package/esm/core/codegen/shared/index.js +2 -0
- package/esm/core/codegen/utils.d.ts +14 -0
- package/esm/core/codegen/utils.js +86 -0
- package/esm/core/introspect/infer-tables.d.ts +5 -0
- package/esm/core/introspect/infer-tables.js +55 -5
- package/esm/core/pipeline/index.js +2 -1
- package/esm/generators/field-selector.js +32 -7
- package/esm/generators/mutations.d.ts +1 -2
- package/esm/generators/mutations.js +12 -9
- package/esm/generators/naming-helpers.d.ts +48 -0
- package/esm/generators/naming-helpers.js +154 -0
- package/esm/generators/select.d.ts +1 -12
- package/esm/generators/select.js +96 -71
- package/esm/types/config.d.ts +6 -0
- package/esm/types/config.js +1 -0
- package/esm/types/query.d.ts +9 -0
- package/esm/types/schema.d.ts +8 -0
- package/generators/field-selector.js +32 -7
- package/generators/mutations.d.ts +1 -2
- package/generators/mutations.js +12 -9
- package/generators/naming-helpers.d.ts +48 -0
- package/generators/naming-helpers.js +169 -0
- package/generators/select.d.ts +1 -12
- package/generators/select.js +98 -72
- package/package.json +6 -6
- package/types/config.d.ts +6 -0
- package/types/config.js +1 -0
- package/types/query.d.ts +9 -0
- 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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
*/
|