@constructive-io/graphql-codegen 2.24.0 → 2.24.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/cli/codegen/babel-ast.d.ts +7 -0
- package/cli/codegen/babel-ast.js +15 -0
- package/cli/codegen/barrel.js +43 -14
- package/cli/codegen/custom-mutations.js +4 -4
- package/cli/codegen/custom-queries.js +12 -22
- package/cli/codegen/gql-ast.js +22 -1
- package/cli/codegen/index.js +1 -0
- package/cli/codegen/mutations.d.ts +2 -0
- package/cli/codegen/mutations.js +26 -13
- package/cli/codegen/orm/input-types-generator.js +22 -0
- package/cli/codegen/queries.d.ts +1 -1
- package/cli/codegen/queries.js +112 -35
- package/cli/codegen/utils.d.ts +6 -0
- package/cli/codegen/utils.js +19 -0
- package/esm/cli/codegen/babel-ast.d.ts +7 -0
- package/esm/cli/codegen/babel-ast.js +14 -0
- package/esm/cli/codegen/barrel.js +44 -15
- package/esm/cli/codegen/custom-mutations.js +5 -5
- package/esm/cli/codegen/custom-queries.js +13 -23
- package/esm/cli/codegen/gql-ast.js +23 -2
- package/esm/cli/codegen/index.js +1 -0
- package/esm/cli/codegen/mutations.d.ts +2 -0
- package/esm/cli/codegen/mutations.js +27 -14
- package/esm/cli/codegen/orm/input-types-generator.js +22 -0
- package/esm/cli/codegen/queries.d.ts +1 -1
- package/esm/cli/codegen/queries.js +114 -37
- package/esm/cli/codegen/utils.d.ts +6 -0
- package/esm/cli/codegen/utils.js +18 -0
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as t from '@babel/types';
|
|
2
|
-
import { generateCode, addJSDocComment, typedParam } from './babel-ast';
|
|
2
|
+
import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast';
|
|
3
3
|
import { buildCreateMutationAST, buildUpdateMutationAST, buildDeleteMutationAST, printGraphQL, } from './gql-ast';
|
|
4
4
|
import { getTableNames, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, getCreateMutationFileName, getUpdateMutationFileName, getDeleteMutationFileName, getCreateMutationName, getUpdateMutationName, getDeleteMutationName, getScalarFields, getPrimaryKeyInfo, fieldTypeToTs, ucFirst, lcFirst, getGeneratedFileHeader, } from './utils';
|
|
5
5
|
function isAutoGeneratedField(fieldName, pkFieldNames) {
|
|
@@ -14,7 +14,7 @@ function isAutoGeneratedField(fieldName, pkFieldNames) {
|
|
|
14
14
|
return timestampPatterns.includes(name);
|
|
15
15
|
}
|
|
16
16
|
export function generateCreateMutationHook(table, options = {}) {
|
|
17
|
-
const { reactQueryEnabled = true, enumsFromSchemaTypes = [], useCentralizedKeys = true, hasRelationships = false, } = options;
|
|
17
|
+
const { reactQueryEnabled = true, enumsFromSchemaTypes = [], useCentralizedKeys = true, hasRelationships = false, tableTypeNames = new Set(), } = options;
|
|
18
18
|
if (!reactQueryEnabled) {
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
@@ -28,11 +28,16 @@ export function generateCreateMutationHook(table, options = {}) {
|
|
|
28
28
|
const scalarFields = getScalarFields(table);
|
|
29
29
|
const pkFieldNames = new Set(getPrimaryKeyInfo(table).map((pk) => pk.name));
|
|
30
30
|
const usedEnums = new Set();
|
|
31
|
+
const usedTableTypes = new Set();
|
|
31
32
|
for (const field of scalarFields) {
|
|
32
33
|
const cleanType = field.type.gqlType.replace(/!/g, '');
|
|
33
34
|
if (enumSet.has(cleanType)) {
|
|
34
35
|
usedEnums.add(cleanType);
|
|
35
36
|
}
|
|
37
|
+
else if (tableTypeNames.has(cleanType) && cleanType !== typeName) {
|
|
38
|
+
// Track table types used in scalar fields (excluding the main type which is already imported)
|
|
39
|
+
usedTableTypes.add(cleanType);
|
|
40
|
+
}
|
|
36
41
|
}
|
|
37
42
|
const mutationAST = buildCreateMutationAST({ table });
|
|
38
43
|
const mutationDocument = printGraphQL(mutationAST);
|
|
@@ -47,7 +52,9 @@ export function generateCreateMutationHook(table, options = {}) {
|
|
|
47
52
|
statements.push(reactQueryTypeImport);
|
|
48
53
|
const clientImport = t.importDeclaration([t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], t.stringLiteral('../client'));
|
|
49
54
|
statements.push(clientImport);
|
|
50
|
-
|
|
55
|
+
// Import the main type and any other table types used in scalar fields
|
|
56
|
+
const allTypesToImport = [typeName, ...Array.from(usedTableTypes)].sort();
|
|
57
|
+
const typesImport = t.importDeclaration(allTypesToImport.map((t_) => t.importSpecifier(t.identifier(t_), t.identifier(t_))), t.stringLiteral('../types'));
|
|
51
58
|
typesImport.importKind = 'type';
|
|
52
59
|
statements.push(typesImport);
|
|
53
60
|
if (usedEnums.size > 0) {
|
|
@@ -108,9 +115,9 @@ export function generateCreateMutationHook(table, options = {}) {
|
|
|
108
115
|
if (useCentralizedKeys) {
|
|
109
116
|
mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.callExpression(t.memberExpression(t.identifier(mutationKeysName), t.identifier('create')), [])));
|
|
110
117
|
}
|
|
111
|
-
mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))], t.
|
|
112
|
-
t.identifier(`${mutationName}
|
|
113
|
-
t.identifier(
|
|
118
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')], [
|
|
119
|
+
t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationResult`)),
|
|
120
|
+
t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)),
|
|
114
121
|
]))));
|
|
115
122
|
const invalidateQueryKey = useCentralizedKeys
|
|
116
123
|
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [])
|
|
@@ -151,7 +158,7 @@ export function generateCreateMutationHook(table, options = {}) {
|
|
|
151
158
|
};
|
|
152
159
|
}
|
|
153
160
|
export function generateUpdateMutationHook(table, options = {}) {
|
|
154
|
-
const { reactQueryEnabled = true, enumsFromSchemaTypes = [], useCentralizedKeys = true, hasRelationships = false, } = options;
|
|
161
|
+
const { reactQueryEnabled = true, enumsFromSchemaTypes = [], useCentralizedKeys = true, hasRelationships = false, tableTypeNames = new Set(), } = options;
|
|
155
162
|
if (!reactQueryEnabled) {
|
|
156
163
|
return null;
|
|
157
164
|
}
|
|
@@ -170,11 +177,15 @@ export function generateUpdateMutationHook(table, options = {}) {
|
|
|
170
177
|
const pkField = pkFields[0];
|
|
171
178
|
const pkFieldNames = new Set(pkFields.map((pk) => pk.name));
|
|
172
179
|
const usedEnums = new Set();
|
|
180
|
+
const usedTableTypes = new Set();
|
|
173
181
|
for (const field of scalarFields) {
|
|
174
182
|
const cleanType = field.type.gqlType.replace(/!/g, '');
|
|
175
183
|
if (enumSet.has(cleanType)) {
|
|
176
184
|
usedEnums.add(cleanType);
|
|
177
185
|
}
|
|
186
|
+
else if (tableTypeNames.has(cleanType) && cleanType !== typeName) {
|
|
187
|
+
usedTableTypes.add(cleanType);
|
|
188
|
+
}
|
|
178
189
|
}
|
|
179
190
|
const mutationAST = buildUpdateMutationAST({ table });
|
|
180
191
|
const mutationDocument = printGraphQL(mutationAST);
|
|
@@ -189,7 +200,9 @@ export function generateUpdateMutationHook(table, options = {}) {
|
|
|
189
200
|
statements.push(reactQueryTypeImport);
|
|
190
201
|
const clientImport = t.importDeclaration([t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], t.stringLiteral('../client'));
|
|
191
202
|
statements.push(clientImport);
|
|
192
|
-
|
|
203
|
+
// Import the main type and any other table types used in scalar fields
|
|
204
|
+
const allTypesToImportUpdate = [typeName, ...Array.from(usedTableTypes)].sort();
|
|
205
|
+
const typesImport = t.importDeclaration(allTypesToImportUpdate.map((t_) => t.importSpecifier(t.identifier(t_), t.identifier(t_))), t.stringLiteral('../types'));
|
|
193
206
|
typesImport.importKind = 'type';
|
|
194
207
|
statements.push(typesImport);
|
|
195
208
|
if (usedEnums.size > 0) {
|
|
@@ -256,9 +269,9 @@ export function generateUpdateMutationHook(table, options = {}) {
|
|
|
256
269
|
if (useCentralizedKeys) {
|
|
257
270
|
mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.memberExpression(t.identifier(mutationKeysName), t.identifier('all'))));
|
|
258
271
|
}
|
|
259
|
-
mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))], t.
|
|
260
|
-
t.identifier(`${mutationName}
|
|
261
|
-
t.identifier(
|
|
272
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')], [
|
|
273
|
+
t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationResult`)),
|
|
274
|
+
t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)),
|
|
262
275
|
]))));
|
|
263
276
|
const detailQueryKey = useCentralizedKeys
|
|
264
277
|
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name))])
|
|
@@ -378,9 +391,9 @@ export function generateDeleteMutationHook(table, options = {}) {
|
|
|
378
391
|
if (useCentralizedKeys) {
|
|
379
392
|
mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.memberExpression(t.identifier(mutationKeysName), t.identifier('all'))));
|
|
380
393
|
}
|
|
381
|
-
mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))], t.
|
|
382
|
-
t.identifier(`${mutationName}
|
|
383
|
-
t.identifier(
|
|
394
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')], [
|
|
395
|
+
t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationResult`)),
|
|
396
|
+
t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)),
|
|
384
397
|
]))));
|
|
385
398
|
const detailQueryKey = useCentralizedKeys
|
|
386
399
|
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name))])
|
|
@@ -193,6 +193,22 @@ const SCALAR_FILTER_CONFIGS = [
|
|
|
193
193
|
operators: ['equality', 'distinct', 'inArray', 'comparison', 'inet'],
|
|
194
194
|
},
|
|
195
195
|
{ name: 'FullTextFilter', tsType: 'string', operators: ['fulltext'] },
|
|
196
|
+
// List filters (for array fields like string[], int[], uuid[])
|
|
197
|
+
{
|
|
198
|
+
name: 'StringListFilter',
|
|
199
|
+
tsType: 'string[]',
|
|
200
|
+
operators: ['equality', 'distinct', 'comparison', 'listArray'],
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: 'IntListFilter',
|
|
204
|
+
tsType: 'number[]',
|
|
205
|
+
operators: ['equality', 'distinct', 'comparison', 'listArray'],
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'UUIDListFilter',
|
|
209
|
+
tsType: 'string[]',
|
|
210
|
+
operators: ['equality', 'distinct', 'comparison', 'listArray'],
|
|
211
|
+
},
|
|
196
212
|
];
|
|
197
213
|
/**
|
|
198
214
|
* Build filter properties based on operator sets
|
|
@@ -232,6 +248,12 @@ function buildScalarFilterProperties(config) {
|
|
|
232
248
|
if (operators.includes('fulltext')) {
|
|
233
249
|
props.push({ name: 'matches', type: 'string', optional: true });
|
|
234
250
|
}
|
|
251
|
+
// List/Array operators (contains, overlaps, anyEqualTo, etc.)
|
|
252
|
+
if (operators.includes('listArray')) {
|
|
253
|
+
// Extract base type from array type (e.g., 'string[]' -> 'string')
|
|
254
|
+
const baseType = tsType.replace('[]', '');
|
|
255
|
+
props.push({ name: 'contains', type: tsType, optional: true }, { name: 'containedBy', type: tsType, optional: true }, { name: 'overlaps', type: tsType, optional: true }, { name: 'anyEqualTo', type: baseType, optional: true }, { name: 'anyNotEqualTo', type: baseType, optional: true }, { name: 'anyLessThan', type: baseType, optional: true }, { name: 'anyLessThanOrEqualTo', type: baseType, optional: true }, { name: 'anyGreaterThan', type: baseType, optional: true }, { name: 'anyGreaterThanOrEqualTo', type: baseType, optional: true });
|
|
256
|
+
}
|
|
235
257
|
return props;
|
|
236
258
|
}
|
|
237
259
|
/**
|
|
@@ -17,5 +17,5 @@ export interface QueryGeneratorOptions {
|
|
|
17
17
|
hasRelationships?: boolean;
|
|
18
18
|
}
|
|
19
19
|
export declare function generateListQueryHook(table: CleanTable, options?: QueryGeneratorOptions): GeneratedQueryFile;
|
|
20
|
-
export declare function generateSingleQueryHook(table: CleanTable, options?: QueryGeneratorOptions): GeneratedQueryFile;
|
|
20
|
+
export declare function generateSingleQueryHook(table: CleanTable, options?: QueryGeneratorOptions): GeneratedQueryFile | null;
|
|
21
21
|
export declare function generateAllQueryHooks(tables: CleanTable[], options?: QueryGeneratorOptions): GeneratedQueryFile[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as t from '@babel/types';
|
|
2
|
-
import { generateCode, addJSDocComment, typedParam } from './babel-ast';
|
|
2
|
+
import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast';
|
|
3
3
|
import { buildListQueryAST, buildSingleQueryAST, printGraphQL, } from './gql-ast';
|
|
4
|
-
import { getTableNames, getListQueryHookName, getSingleQueryHookName, getListQueryFileName, getSingleQueryFileName, getAllRowsQueryName, getSingleRowQueryName, getFilterTypeName, getOrderByTypeName, getScalarFields, getScalarFilterType, getPrimaryKeyInfo, toScreamingSnake, ucFirst, lcFirst, getGeneratedFileHeader, } from './utils';
|
|
4
|
+
import { getTableNames, getListQueryHookName, getSingleQueryHookName, getListQueryFileName, getSingleQueryFileName, getAllRowsQueryName, getSingleRowQueryName, getFilterTypeName, getConditionTypeName, getOrderByTypeName, getScalarFields, getScalarFilterType, getPrimaryKeyInfo, hasValidPrimaryKey, fieldTypeToTs, toScreamingSnake, ucFirst, lcFirst, getGeneratedFileHeader, } from './utils';
|
|
5
5
|
function createUnionType(values) {
|
|
6
6
|
return t.tsUnionType(values.map((v) => t.tsLiteralType(t.stringLiteral(v))));
|
|
7
7
|
}
|
|
@@ -34,6 +34,7 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
34
34
|
const hookName = getListQueryHookName(table);
|
|
35
35
|
const queryName = getAllRowsQueryName(table);
|
|
36
36
|
const filterTypeName = getFilterTypeName(table);
|
|
37
|
+
const conditionTypeName = getConditionTypeName(table);
|
|
37
38
|
const orderByTypeName = getOrderByTypeName(table);
|
|
38
39
|
const scalarFields = getScalarFields(table);
|
|
39
40
|
const keysName = `${lcFirst(typeName)}Keys`;
|
|
@@ -98,6 +99,59 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
98
99
|
})
|
|
99
100
|
.filter((f) => f !== null);
|
|
100
101
|
statements.push(createFilterInterfaceDeclaration(filterTypeName, fieldFilters, false));
|
|
102
|
+
// Generate Condition interface (simple equality filter with scalar types)
|
|
103
|
+
// Track non-primitive types (enums) that need to be imported
|
|
104
|
+
const enumTypesUsed = new Set();
|
|
105
|
+
const conditionProperties = scalarFields.map((field) => {
|
|
106
|
+
const tsType = fieldTypeToTs(field.type);
|
|
107
|
+
const isPrimitive = tsType === 'string' ||
|
|
108
|
+
tsType === 'number' ||
|
|
109
|
+
tsType === 'boolean' ||
|
|
110
|
+
tsType === 'unknown' ||
|
|
111
|
+
tsType.endsWith('[]');
|
|
112
|
+
let typeAnnotation;
|
|
113
|
+
if (field.type.isArray) {
|
|
114
|
+
const baseType = tsType.replace('[]', '');
|
|
115
|
+
const isBasePrimitive = baseType === 'string' ||
|
|
116
|
+
baseType === 'number' ||
|
|
117
|
+
baseType === 'boolean' ||
|
|
118
|
+
baseType === 'unknown';
|
|
119
|
+
if (!isBasePrimitive) {
|
|
120
|
+
enumTypesUsed.add(baseType);
|
|
121
|
+
}
|
|
122
|
+
typeAnnotation = t.tsArrayType(baseType === 'string'
|
|
123
|
+
? t.tsStringKeyword()
|
|
124
|
+
: baseType === 'number'
|
|
125
|
+
? t.tsNumberKeyword()
|
|
126
|
+
: baseType === 'boolean'
|
|
127
|
+
? t.tsBooleanKeyword()
|
|
128
|
+
: t.tsTypeReference(t.identifier(baseType)));
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
if (!isPrimitive) {
|
|
132
|
+
enumTypesUsed.add(tsType);
|
|
133
|
+
}
|
|
134
|
+
typeAnnotation =
|
|
135
|
+
tsType === 'string'
|
|
136
|
+
? t.tsStringKeyword()
|
|
137
|
+
: tsType === 'number'
|
|
138
|
+
? t.tsNumberKeyword()
|
|
139
|
+
: tsType === 'boolean'
|
|
140
|
+
? t.tsBooleanKeyword()
|
|
141
|
+
: t.tsTypeReference(t.identifier(tsType));
|
|
142
|
+
}
|
|
143
|
+
const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(typeAnnotation));
|
|
144
|
+
prop.optional = true;
|
|
145
|
+
return prop;
|
|
146
|
+
});
|
|
147
|
+
// Add import for enum types if any are used
|
|
148
|
+
if (enumTypesUsed.size > 0) {
|
|
149
|
+
const schemaTypesImport = t.importDeclaration(Array.from(enumTypesUsed).map((et) => t.importSpecifier(t.identifier(et), t.identifier(et))), t.stringLiteral('../schema-types'));
|
|
150
|
+
schemaTypesImport.importKind = 'type';
|
|
151
|
+
statements.push(schemaTypesImport);
|
|
152
|
+
}
|
|
153
|
+
const conditionInterface = t.tsInterfaceDeclaration(t.identifier(conditionTypeName), null, null, t.tsInterfaceBody(conditionProperties));
|
|
154
|
+
statements.push(conditionInterface);
|
|
101
155
|
const orderByValues = [
|
|
102
156
|
...scalarFields.flatMap((f) => [
|
|
103
157
|
`${toScreamingSnake(f.name)}_ASC`,
|
|
@@ -115,16 +169,36 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
115
169
|
p.optional = true;
|
|
116
170
|
return p;
|
|
117
171
|
})(),
|
|
172
|
+
(() => {
|
|
173
|
+
const p = t.tsPropertySignature(t.identifier('last'), t.tsTypeAnnotation(t.tsNumberKeyword()));
|
|
174
|
+
p.optional = true;
|
|
175
|
+
return p;
|
|
176
|
+
})(),
|
|
118
177
|
(() => {
|
|
119
178
|
const p = t.tsPropertySignature(t.identifier('offset'), t.tsTypeAnnotation(t.tsNumberKeyword()));
|
|
120
179
|
p.optional = true;
|
|
121
180
|
return p;
|
|
122
181
|
})(),
|
|
182
|
+
(() => {
|
|
183
|
+
const p = t.tsPropertySignature(t.identifier('before'), t.tsTypeAnnotation(t.tsStringKeyword()));
|
|
184
|
+
p.optional = true;
|
|
185
|
+
return p;
|
|
186
|
+
})(),
|
|
187
|
+
(() => {
|
|
188
|
+
const p = t.tsPropertySignature(t.identifier('after'), t.tsTypeAnnotation(t.tsStringKeyword()));
|
|
189
|
+
p.optional = true;
|
|
190
|
+
return p;
|
|
191
|
+
})(),
|
|
123
192
|
(() => {
|
|
124
193
|
const p = t.tsPropertySignature(t.identifier('filter'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(filterTypeName))));
|
|
125
194
|
p.optional = true;
|
|
126
195
|
return p;
|
|
127
196
|
})(),
|
|
197
|
+
(() => {
|
|
198
|
+
const p = t.tsPropertySignature(t.identifier('condition'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(conditionTypeName))));
|
|
199
|
+
p.optional = true;
|
|
200
|
+
return p;
|
|
201
|
+
})(),
|
|
128
202
|
(() => {
|
|
129
203
|
const p = t.tsPropertySignature(t.identifier('orderBy'), t.tsTypeAnnotation(t.tsArrayType(t.tsTypeReference(t.identifier(orderByTypeName)))));
|
|
130
204
|
p.optional = true;
|
|
@@ -184,9 +258,9 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
184
258
|
hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useQuery'), [
|
|
185
259
|
t.objectExpression([
|
|
186
260
|
t.objectProperty(t.identifier('queryKey'), t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('list')), [t.identifier('variables'), t.identifier('scope')])),
|
|
187
|
-
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.
|
|
188
|
-
t.identifier(`${
|
|
189
|
-
t.identifier(
|
|
261
|
+
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
|
|
262
|
+
t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)),
|
|
263
|
+
t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)),
|
|
190
264
|
]))),
|
|
191
265
|
t.spreadElement(t.identifier('queryOptions')),
|
|
192
266
|
]),
|
|
@@ -196,9 +270,9 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
196
270
|
hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useQuery'), [
|
|
197
271
|
t.objectExpression([
|
|
198
272
|
t.objectProperty(t.identifier('queryKey'), t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('list')), [t.identifier('variables')])),
|
|
199
|
-
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.
|
|
200
|
-
t.identifier(`${
|
|
201
|
-
t.identifier(
|
|
273
|
+
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
|
|
274
|
+
t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)),
|
|
275
|
+
t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)),
|
|
202
276
|
]))),
|
|
203
277
|
t.spreadElement(t.identifier('options')),
|
|
204
278
|
]),
|
|
@@ -210,9 +284,9 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
210
284
|
t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(`${queryName}QueryKey`), [
|
|
211
285
|
t.identifier('variables'),
|
|
212
286
|
])),
|
|
213
|
-
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.
|
|
214
|
-
t.identifier(`${
|
|
215
|
-
t.identifier(
|
|
287
|
+
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
|
|
288
|
+
t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)),
|
|
289
|
+
t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)),
|
|
216
290
|
]))),
|
|
217
291
|
t.spreadElement(t.identifier('options')),
|
|
218
292
|
]),
|
|
@@ -260,10 +334,9 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
260
334
|
statements.push(hookExport);
|
|
261
335
|
}
|
|
262
336
|
const fetchFuncBody = t.blockStatement([
|
|
263
|
-
t.returnStatement(t.
|
|
264
|
-
t.identifier(`${
|
|
265
|
-
t.identifier(
|
|
266
|
-
t.identifier('options'),
|
|
337
|
+
t.returnStatement(createTypedCallExpression(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], [
|
|
338
|
+
t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)),
|
|
339
|
+
t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)),
|
|
267
340
|
])),
|
|
268
341
|
]);
|
|
269
342
|
const fetchFunc = t.functionDeclaration(t.identifier(`fetch${ucFirst(pluralName)}Query`), [
|
|
@@ -314,10 +387,9 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
314
387
|
t.expressionStatement(t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), [
|
|
315
388
|
t.objectExpression([
|
|
316
389
|
t.objectProperty(t.identifier('queryKey'), prefetchQueryKeyExpr),
|
|
317
|
-
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.
|
|
318
|
-
t.identifier(`${
|
|
319
|
-
t.identifier(
|
|
320
|
-
t.identifier('options'),
|
|
390
|
+
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], [
|
|
391
|
+
t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)),
|
|
392
|
+
t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)),
|
|
321
393
|
]))),
|
|
322
394
|
]),
|
|
323
395
|
]))),
|
|
@@ -347,6 +419,10 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
347
419
|
};
|
|
348
420
|
}
|
|
349
421
|
export function generateSingleQueryHook(table, options = {}) {
|
|
422
|
+
// Skip tables with composite keys - they are handled as custom queries
|
|
423
|
+
if (!hasValidPrimaryKey(table)) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
350
426
|
const { reactQueryEnabled = true, useCentralizedKeys = true, hasRelationships = false, } = options;
|
|
351
427
|
const { typeName, singularName } = getTableNames(table);
|
|
352
428
|
const hookName = getSingleQueryHookName(table);
|
|
@@ -454,9 +530,9 @@ export function generateSingleQueryHook(table, options = {}) {
|
|
|
454
530
|
t.memberExpression(t.identifier('variables'), t.identifier(pkName)),
|
|
455
531
|
t.identifier('scope'),
|
|
456
532
|
])),
|
|
457
|
-
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.
|
|
458
|
-
t.identifier(`${
|
|
459
|
-
t.identifier(
|
|
533
|
+
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
|
|
534
|
+
t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)),
|
|
535
|
+
t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)),
|
|
460
536
|
]))),
|
|
461
537
|
t.spreadElement(t.identifier('queryOptions')),
|
|
462
538
|
]),
|
|
@@ -468,9 +544,9 @@ export function generateSingleQueryHook(table, options = {}) {
|
|
|
468
544
|
t.objectProperty(t.identifier('queryKey'), t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [
|
|
469
545
|
t.memberExpression(t.identifier('variables'), t.identifier(pkName)),
|
|
470
546
|
])),
|
|
471
|
-
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.
|
|
472
|
-
t.identifier(`${
|
|
473
|
-
t.identifier(
|
|
547
|
+
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
|
|
548
|
+
t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)),
|
|
549
|
+
t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)),
|
|
474
550
|
]))),
|
|
475
551
|
t.spreadElement(t.identifier('options')),
|
|
476
552
|
]),
|
|
@@ -482,9 +558,9 @@ export function generateSingleQueryHook(table, options = {}) {
|
|
|
482
558
|
t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(`${queryName}QueryKey`), [
|
|
483
559
|
t.memberExpression(t.identifier('variables'), t.identifier(pkName)),
|
|
484
560
|
])),
|
|
485
|
-
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.
|
|
486
|
-
t.identifier(`${
|
|
487
|
-
t.identifier(
|
|
561
|
+
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
|
|
562
|
+
t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)),
|
|
563
|
+
t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)),
|
|
488
564
|
]))),
|
|
489
565
|
t.spreadElement(t.identifier('options')),
|
|
490
566
|
]),
|
|
@@ -528,10 +604,9 @@ export function generateSingleQueryHook(table, options = {}) {
|
|
|
528
604
|
statements.push(hookExport);
|
|
529
605
|
}
|
|
530
606
|
const fetchFuncBody = t.blockStatement([
|
|
531
|
-
t.returnStatement(t.
|
|
532
|
-
t.identifier(`${
|
|
533
|
-
t.identifier(
|
|
534
|
-
t.identifier('options'),
|
|
607
|
+
t.returnStatement(createTypedCallExpression(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], [
|
|
608
|
+
t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)),
|
|
609
|
+
t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)),
|
|
535
610
|
])),
|
|
536
611
|
]);
|
|
537
612
|
const fetchFunc = t.functionDeclaration(t.identifier(`fetch${ucFirst(singularName)}Query`), [
|
|
@@ -578,10 +653,9 @@ export function generateSingleQueryHook(table, options = {}) {
|
|
|
578
653
|
t.expressionStatement(t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), [
|
|
579
654
|
t.objectExpression([
|
|
580
655
|
t.objectProperty(t.identifier('queryKey'), prefetchQueryKeyExpr),
|
|
581
|
-
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.
|
|
582
|
-
t.identifier(`${
|
|
583
|
-
t.identifier(
|
|
584
|
-
t.identifier('options'),
|
|
656
|
+
t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], [
|
|
657
|
+
t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)),
|
|
658
|
+
t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)),
|
|
585
659
|
]))),
|
|
586
660
|
]),
|
|
587
661
|
]))),
|
|
@@ -614,7 +688,10 @@ export function generateAllQueryHooks(tables, options = {}) {
|
|
|
614
688
|
const files = [];
|
|
615
689
|
for (const table of tables) {
|
|
616
690
|
files.push(generateListQueryHook(table, options));
|
|
617
|
-
|
|
691
|
+
const singleHook = generateSingleQueryHook(table, options);
|
|
692
|
+
if (singleHook) {
|
|
693
|
+
files.push(singleHook);
|
|
694
|
+
}
|
|
618
695
|
}
|
|
619
696
|
return files;
|
|
620
697
|
}
|
|
@@ -171,6 +171,12 @@ export declare function getPrimaryKeyInfo(table: CleanTable): PrimaryKeyField[];
|
|
|
171
171
|
* Get primary key field names (convenience wrapper)
|
|
172
172
|
*/
|
|
173
173
|
export declare function getPrimaryKeyFields(table: CleanTable): string[];
|
|
174
|
+
/**
|
|
175
|
+
* Check if table has a valid single-field primary key
|
|
176
|
+
* Used to determine if a single query hook can be generated
|
|
177
|
+
* Tables with composite keys return false (handled as custom queries)
|
|
178
|
+
*/
|
|
179
|
+
export declare function hasValidPrimaryKey(table: CleanTable): boolean;
|
|
174
180
|
/**
|
|
175
181
|
* Generate query key prefix for a table
|
|
176
182
|
* e.g., "cars" for list queries, "car" for detail queries
|
package/esm/cli/codegen/utils.js
CHANGED
|
@@ -291,6 +291,24 @@ export function getPrimaryKeyInfo(table) {
|
|
|
291
291
|
export function getPrimaryKeyFields(table) {
|
|
292
292
|
return getPrimaryKeyInfo(table).map((pk) => pk.name);
|
|
293
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Check if table has a valid single-field primary key
|
|
296
|
+
* Used to determine if a single query hook can be generated
|
|
297
|
+
* Tables with composite keys return false (handled as custom queries)
|
|
298
|
+
*/
|
|
299
|
+
export function hasValidPrimaryKey(table) {
|
|
300
|
+
// Check for explicit primary key constraint with single field
|
|
301
|
+
const pk = table.constraints?.primaryKey?.[0];
|
|
302
|
+
if (pk && pk.fields.length === 1) {
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
// Check for 'id' field as fallback
|
|
306
|
+
const idField = table.fields.find((f) => f.name.toLowerCase() === 'id');
|
|
307
|
+
if (idField) {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
294
312
|
// ============================================================================
|
|
295
313
|
// Query key generation
|
|
296
314
|
// ============================================================================
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructive-io/graphql-codegen",
|
|
3
|
-
"version": "2.24.
|
|
3
|
+
"version": "2.24.1",
|
|
4
4
|
"description": "CLI-based GraphQL SDK generator for PostGraphile endpoints with React Query hooks",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphql",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"tsx": "^4.21.0",
|
|
84
84
|
"typescript": "^5.9.3"
|
|
85
85
|
},
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "3e08dd946ec5eab57c296e8df180f1dcb1d06514"
|
|
87
87
|
}
|