@constructive-io/graphql-codegen 3.3.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -4
- package/core/ast.js +6 -5
- package/core/codegen/custom-mutations.js +22 -22
- package/core/codegen/custom-queries.js +24 -17
- package/core/codegen/hooks-ast.d.ts +1 -1
- package/core/codegen/hooks-ast.js +22 -5
- package/core/codegen/index.d.ts +1 -1
- package/core/codegen/mutations.js +16 -12
- package/core/codegen/orm/custom-ops-generator.js +37 -3
- package/core/codegen/orm/input-types-generator.js +161 -89
- package/core/codegen/orm/model-generator.js +72 -73
- package/core/codegen/orm/select-types.d.ts +27 -17
- package/core/codegen/queries.js +37 -25
- package/core/codegen/schema-types-generator.js +21 -0
- package/core/codegen/templates/hooks-selection.ts +12 -0
- package/core/codegen/templates/query-builder.ts +103 -59
- package/core/codegen/templates/select-types.ts +59 -33
- package/core/codegen/types.js +26 -0
- package/core/codegen/utils.d.ts +1 -1
- package/core/codegen/utils.js +1 -1
- package/core/custom-ast.js +9 -8
- package/core/database/index.js +2 -3
- package/core/index.d.ts +2 -0
- package/core/index.js +2 -0
- package/core/introspect/infer-tables.js +144 -58
- package/core/introspect/transform-schema.d.ts +1 -1
- package/core/introspect/transform-schema.js +3 -1
- package/esm/core/ast.js +6 -5
- package/esm/core/codegen/custom-mutations.js +23 -23
- package/esm/core/codegen/custom-queries.js +25 -18
- package/esm/core/codegen/hooks-ast.d.ts +1 -1
- package/esm/core/codegen/hooks-ast.js +22 -5
- package/esm/core/codegen/index.d.ts +1 -1
- package/esm/core/codegen/mutations.js +16 -12
- package/esm/core/codegen/orm/custom-ops-generator.js +38 -4
- package/esm/core/codegen/orm/input-types-generator.js +163 -91
- package/esm/core/codegen/orm/model-generator.js +73 -74
- package/esm/core/codegen/orm/select-types.d.ts +27 -17
- package/esm/core/codegen/queries.js +37 -25
- package/esm/core/codegen/schema-types-generator.js +21 -0
- package/esm/core/codegen/types.js +26 -0
- package/esm/core/codegen/utils.d.ts +1 -1
- package/esm/core/codegen/utils.js +1 -1
- package/esm/core/custom-ast.js +9 -8
- package/esm/core/database/index.js +2 -3
- package/esm/core/index.d.ts +2 -0
- package/esm/core/index.js +2 -0
- package/esm/core/introspect/infer-tables.js +144 -58
- package/esm/core/introspect/transform-schema.d.ts +1 -1
- package/esm/core/introspect/transform-schema.js +3 -1
- package/esm/generators/field-selector.js +1 -0
- package/esm/generators/index.d.ts +3 -0
- package/esm/generators/index.js +3 -0
- package/esm/generators/mutations.js +4 -4
- package/esm/generators/select.js +4 -4
- package/esm/index.d.ts +1 -1
- package/esm/index.js +1 -1
- package/esm/types/schema.d.ts +5 -3
- package/generators/field-selector.js +1 -0
- package/generators/index.d.ts +3 -0
- package/generators/index.js +3 -0
- package/generators/mutations.js +3 -3
- package/generators/select.js +3 -3
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +10 -10
- package/types/schema.d.ts +5 -3
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as t from '@babel/types';
|
|
8
8
|
import { generateCode } from '../babel-ast';
|
|
9
|
-
import { getFilterTypeName, getGeneratedFileHeader, getOrderByTypeName, getPrimaryKeyInfo, getTableNames, hasValidPrimaryKey, lcFirst, } from '../utils';
|
|
9
|
+
import { getFilterTypeName, getGeneratedFileHeader, getOrderByTypeName, getPrimaryKeyInfo, getSingleRowQueryName, getTableNames, hasValidPrimaryKey, lcFirst, } from '../utils';
|
|
10
10
|
function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false) {
|
|
11
11
|
const specifiers = namedImports.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
|
|
12
12
|
const decl = t.importDeclaration(specifiers, t.stringLiteral(moduleSpecifier));
|
|
13
13
|
decl.importKind = typeOnly ? 'type' : 'value';
|
|
14
14
|
return decl;
|
|
15
15
|
}
|
|
16
|
-
function buildMethodBody(builderFn, args, operation, typeName, fieldName) {
|
|
16
|
+
function buildMethodBody(builderFn, args, operation, typeName, fieldName, extraProps = []) {
|
|
17
17
|
const destructureDecl = t.variableDeclaration('const', [
|
|
18
18
|
t.variableDeclarator(t.objectPattern([
|
|
19
19
|
t.objectProperty(t.identifier('document'), t.identifier('document'), false, true),
|
|
@@ -28,6 +28,7 @@ function buildMethodBody(builderFn, args, operation, typeName, fieldName) {
|
|
|
28
28
|
t.objectProperty(t.identifier('fieldName'), t.stringLiteral(fieldName)),
|
|
29
29
|
t.objectProperty(t.identifier('document'), t.identifier('document'), false, true),
|
|
30
30
|
t.objectProperty(t.identifier('variables'), t.identifier('variables'), false, true),
|
|
31
|
+
...extraProps,
|
|
31
32
|
]),
|
|
32
33
|
]));
|
|
33
34
|
return [destructureDecl, returnStmt];
|
|
@@ -38,10 +39,6 @@ function createClassMethod(name, typeParameters, params, returnType, body) {
|
|
|
38
39
|
method.returnType = returnType;
|
|
39
40
|
return method;
|
|
40
41
|
}
|
|
41
|
-
function createDeclareMethod(name, typeParameters, params, returnType) {
|
|
42
|
-
const method = t.tsDeclareMethod(null, t.identifier(name), typeParameters, params, returnType);
|
|
43
|
-
return method;
|
|
44
|
-
}
|
|
45
42
|
function createTypeParam(constraintTypeName, defaultType) {
|
|
46
43
|
const param = t.tsTypeParameter(t.tsTypeReference(t.identifier(constraintTypeName)), defaultType ?? null, 'S');
|
|
47
44
|
return t.tsTypeParameterDeclaration([param]);
|
|
@@ -85,6 +82,8 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
85
82
|
const pkFields = getPrimaryKeyInfo(table);
|
|
86
83
|
const pkField = pkFields[0];
|
|
87
84
|
const pluralQueryName = table.query?.all ?? pluralName;
|
|
85
|
+
const singleQueryName = table.query?.one;
|
|
86
|
+
const singleResultFieldName = getSingleRowQueryName(table);
|
|
88
87
|
const createMutationName = table.query?.create ?? `create${typeName}`;
|
|
89
88
|
const updateMutationName = table.query?.update;
|
|
90
89
|
const deleteMutationName = table.query?.delete;
|
|
@@ -129,7 +128,6 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
129
128
|
classBody.push(t.classMethod('constructor', t.identifier('constructor'), [paramProp], t.blockStatement([])));
|
|
130
129
|
// Reusable type reference factories
|
|
131
130
|
const sRef = () => t.tsTypeReference(t.identifier('S'));
|
|
132
|
-
const selectRef = () => t.tsTypeReference(t.identifier(selectTypeName));
|
|
133
131
|
const pkTsType = () => tsTypeFromPrimitive(pkField.tsType);
|
|
134
132
|
// ── findMany ───────────────────────────────────────────────────────────
|
|
135
133
|
{
|
|
@@ -148,17 +146,12 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
148
146
|
])))),
|
|
149
147
|
]),
|
|
150
148
|
])));
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
149
|
+
const implParam = t.identifier('args');
|
|
150
|
+
implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
154
151
|
argsType(sRef()),
|
|
155
152
|
t.tsTypeLiteral([requiredSelectProp()]),
|
|
156
153
|
strictSelectGuard(selectTypeName),
|
|
157
154
|
]));
|
|
158
|
-
classBody.push(createDeclareMethod('findMany', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
|
|
159
|
-
// Implementation
|
|
160
|
-
const implParam = t.identifier('args');
|
|
161
|
-
implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef()));
|
|
162
155
|
const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
|
|
163
156
|
const bodyArgs = [
|
|
164
157
|
t.stringLiteral(typeName),
|
|
@@ -180,7 +173,7 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
180
173
|
t.stringLiteral(orderByTypeName),
|
|
181
174
|
t.identifier('connectionFieldsMap'),
|
|
182
175
|
];
|
|
183
|
-
classBody.push(createClassMethod('findMany',
|
|
176
|
+
classBody.push(createClassMethod('findMany', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildFindManyDocument', bodyArgs, 'query', typeName, pluralQueryName)));
|
|
184
177
|
}
|
|
185
178
|
// ── findFirst ──────────────────────────────────────────────────────────
|
|
186
179
|
{
|
|
@@ -198,17 +191,12 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
198
191
|
]))),
|
|
199
192
|
]),
|
|
200
193
|
])));
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
194
|
+
const implParam = t.identifier('args');
|
|
195
|
+
implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
204
196
|
argsType(sRef()),
|
|
205
197
|
t.tsTypeLiteral([requiredSelectProp()]),
|
|
206
198
|
strictSelectGuard(selectTypeName),
|
|
207
199
|
]));
|
|
208
|
-
classBody.push(createDeclareMethod('findFirst', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
|
|
209
|
-
// Implementation
|
|
210
|
-
const implParam = t.identifier('args');
|
|
211
|
-
implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef()));
|
|
212
200
|
const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
|
|
213
201
|
const bodyArgs = [
|
|
214
202
|
t.stringLiteral(typeName),
|
|
@@ -220,15 +208,13 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
220
208
|
t.stringLiteral(whereTypeName),
|
|
221
209
|
t.identifier('connectionFieldsMap'),
|
|
222
210
|
];
|
|
223
|
-
classBody.push(createClassMethod('findFirst',
|
|
211
|
+
classBody.push(createClassMethod('findFirst', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildFindFirstDocument', bodyArgs, 'query', typeName, pluralQueryName)));
|
|
224
212
|
}
|
|
225
213
|
// ── findOne ────────────────────────────────────────────────────────────
|
|
226
|
-
|
|
227
|
-
if (singleQueryName && hasValidPrimaryKey(table)) {
|
|
228
|
-
const pkGqlType = pkField.gqlType.replace(/!/g, '') + '!';
|
|
214
|
+
if (hasValidPrimaryKey(table)) {
|
|
229
215
|
const retType = (sel) => t.tsTypeAnnotation(t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([
|
|
230
216
|
t.tsTypeLiteral([
|
|
231
|
-
t.tsPropertySignature(t.identifier(
|
|
217
|
+
t.tsPropertySignature(t.identifier(singleResultFieldName), t.tsTypeAnnotation(t.tsUnionType([
|
|
232
218
|
t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([
|
|
233
219
|
t.tsTypeReference(t.identifier(relationTypeName)),
|
|
234
220
|
sel,
|
|
@@ -242,33 +228,59 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
242
228
|
prop.optional = false;
|
|
243
229
|
return prop;
|
|
244
230
|
};
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
231
|
+
const implParam = t.identifier('args');
|
|
232
|
+
implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
248
233
|
t.tsTypeLiteral([pkProp(), requiredSelectProp()]),
|
|
249
234
|
strictSelectGuard(selectTypeName),
|
|
250
235
|
]));
|
|
251
|
-
classBody.push(createDeclareMethod('findOne', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
|
|
252
|
-
// Implementation
|
|
253
|
-
const implParam = t.identifier('args');
|
|
254
|
-
implParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
255
|
-
pkProp(),
|
|
256
|
-
(() => {
|
|
257
|
-
const prop = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName))));
|
|
258
|
-
return prop;
|
|
259
|
-
})(),
|
|
260
|
-
]));
|
|
261
236
|
const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
237
|
+
if (singleQueryName) {
|
|
238
|
+
const pkGqlType = pkField.gqlType.replace(/!/g, '') + '!';
|
|
239
|
+
const bodyArgs = [
|
|
240
|
+
t.stringLiteral(typeName),
|
|
241
|
+
t.stringLiteral(singleQueryName),
|
|
242
|
+
t.memberExpression(t.identifier('args'), t.identifier(pkField.name)),
|
|
243
|
+
selectExpr,
|
|
244
|
+
t.stringLiteral(pkField.name),
|
|
245
|
+
t.stringLiteral(pkGqlType),
|
|
246
|
+
t.identifier('connectionFieldsMap'),
|
|
247
|
+
];
|
|
248
|
+
classBody.push(createClassMethod('findOne', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildFindOneDocument', bodyArgs, 'query', typeName, singleResultFieldName)));
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
const idExpr = t.memberExpression(t.identifier('args'), t.identifier(pkField.name));
|
|
252
|
+
const findOneArgs = t.objectExpression([
|
|
253
|
+
t.objectProperty(t.identifier('where'), t.objectExpression([
|
|
254
|
+
t.objectProperty(t.identifier(pkField.name), t.objectExpression([
|
|
255
|
+
t.objectProperty(t.identifier('equalTo'), idExpr),
|
|
256
|
+
])),
|
|
257
|
+
])),
|
|
258
|
+
t.objectProperty(t.identifier('first'), t.numericLiteral(1)),
|
|
259
|
+
]);
|
|
260
|
+
const bodyArgs = [
|
|
261
|
+
t.stringLiteral(typeName),
|
|
262
|
+
t.stringLiteral(pluralQueryName),
|
|
263
|
+
selectExpr,
|
|
264
|
+
findOneArgs,
|
|
265
|
+
t.stringLiteral(whereTypeName),
|
|
266
|
+
t.stringLiteral(orderByTypeName),
|
|
267
|
+
t.identifier('connectionFieldsMap'),
|
|
268
|
+
];
|
|
269
|
+
const firstNodeExpr = t.optionalMemberExpression(t.optionalMemberExpression(t.memberExpression(t.identifier('data'), t.identifier(pluralQueryName)), t.identifier('nodes'), false, true), t.numericLiteral(0), true, true);
|
|
270
|
+
const transformDataParam = t.identifier('data');
|
|
271
|
+
const transformedNodesProp = t.tsPropertySignature(t.identifier('nodes'), t.tsTypeAnnotation(t.tsArrayType(t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([
|
|
272
|
+
t.tsTypeReference(t.identifier(relationTypeName)),
|
|
273
|
+
sRef(),
|
|
274
|
+
])))));
|
|
275
|
+
transformedNodesProp.optional = true;
|
|
276
|
+
const transformedCollectionProp = t.tsPropertySignature(t.identifier(pluralQueryName), t.tsTypeAnnotation(t.tsTypeLiteral([transformedNodesProp])));
|
|
277
|
+
transformedCollectionProp.optional = true;
|
|
278
|
+
transformDataParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeLiteral([transformedCollectionProp]));
|
|
279
|
+
const transformFn = t.arrowFunctionExpression([transformDataParam], t.objectExpression([
|
|
280
|
+
t.objectProperty(t.stringLiteral(singleResultFieldName), t.logicalExpression('??', firstNodeExpr, t.nullLiteral())),
|
|
281
|
+
]));
|
|
282
|
+
classBody.push(createClassMethod('findOne', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildFindManyDocument', bodyArgs, 'query', typeName, singleResultFieldName, [t.objectProperty(t.identifier('transform'), transformFn)])));
|
|
283
|
+
}
|
|
272
284
|
}
|
|
273
285
|
// ── create ─────────────────────────────────────────────────────────────
|
|
274
286
|
{
|
|
@@ -284,17 +296,12 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
284
296
|
]))),
|
|
285
297
|
]),
|
|
286
298
|
])));
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
299
|
+
const implParam = t.identifier('args');
|
|
300
|
+
implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
290
301
|
argsType(sRef()),
|
|
291
302
|
t.tsTypeLiteral([requiredSelectProp()]),
|
|
292
303
|
strictSelectGuard(selectTypeName),
|
|
293
304
|
]));
|
|
294
|
-
classBody.push(createDeclareMethod('create', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
|
|
295
|
-
// Implementation
|
|
296
|
-
const implParam = t.identifier('args');
|
|
297
|
-
implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef()));
|
|
298
305
|
const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
|
|
299
306
|
const bodyArgs = [
|
|
300
307
|
t.stringLiteral(typeName),
|
|
@@ -305,7 +312,7 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
305
312
|
t.stringLiteral(createInputTypeName),
|
|
306
313
|
t.identifier('connectionFieldsMap'),
|
|
307
314
|
];
|
|
308
|
-
classBody.push(createClassMethod('create',
|
|
315
|
+
classBody.push(createClassMethod('create', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildCreateDocument', bodyArgs, 'mutation', typeName, createMutationName)));
|
|
309
316
|
}
|
|
310
317
|
// ── update ─────────────────────────────────────────────────────────────
|
|
311
318
|
if (updateMutationName) {
|
|
@@ -331,18 +338,14 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
331
338
|
]))),
|
|
332
339
|
]),
|
|
333
340
|
])));
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
341
|
+
const implParam = t.identifier('args');
|
|
342
|
+
implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
337
343
|
argsType(sRef()),
|
|
338
344
|
t.tsTypeLiteral([requiredSelectProp()]),
|
|
339
345
|
strictSelectGuard(selectTypeName),
|
|
340
346
|
]));
|
|
341
|
-
classBody.push(createDeclareMethod('update', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
|
|
342
|
-
// Implementation
|
|
343
|
-
const implParam = t.identifier('args');
|
|
344
|
-
implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef()));
|
|
345
347
|
const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
|
|
348
|
+
const patchFieldName = table.query?.patchFieldName ?? lcFirst(typeName) + 'Patch';
|
|
346
349
|
const bodyArgs = [
|
|
347
350
|
t.stringLiteral(typeName),
|
|
348
351
|
t.stringLiteral(updateMutationName),
|
|
@@ -352,9 +355,10 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
352
355
|
t.memberExpression(t.identifier('args'), t.identifier('data')),
|
|
353
356
|
t.stringLiteral(updateInputTypeName),
|
|
354
357
|
t.stringLiteral(pkField.name),
|
|
358
|
+
t.stringLiteral(patchFieldName),
|
|
355
359
|
t.identifier('connectionFieldsMap'),
|
|
356
360
|
];
|
|
357
|
-
classBody.push(createClassMethod('update',
|
|
361
|
+
classBody.push(createClassMethod('update', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildUpdateByPkDocument', bodyArgs, 'mutation', typeName, updateMutationName)));
|
|
358
362
|
}
|
|
359
363
|
// ── delete ─────────────────────────────────────────────────────────────
|
|
360
364
|
if (deleteMutationName) {
|
|
@@ -376,17 +380,12 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
376
380
|
]))),
|
|
377
381
|
]),
|
|
378
382
|
])));
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
383
|
+
const implParam = t.identifier('args');
|
|
384
|
+
implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
|
|
382
385
|
argsType(sRef()),
|
|
383
386
|
t.tsTypeLiteral([requiredSelectProp()]),
|
|
384
387
|
strictSelectGuard(selectTypeName),
|
|
385
388
|
]));
|
|
386
|
-
classBody.push(createDeclareMethod('delete', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
|
|
387
|
-
// Implementation
|
|
388
|
-
const implParam = t.identifier('args');
|
|
389
|
-
implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef()));
|
|
390
389
|
const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
|
|
391
390
|
const bodyArgs = [
|
|
392
391
|
t.stringLiteral(typeName),
|
|
@@ -398,7 +397,7 @@ export function generateModelFile(table, _useSharedTypes) {
|
|
|
398
397
|
selectExpr,
|
|
399
398
|
t.identifier('connectionFieldsMap'),
|
|
400
399
|
];
|
|
401
|
-
classBody.push(createClassMethod('delete',
|
|
400
|
+
classBody.push(createClassMethod('delete', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildDeleteByPkDocument', bodyArgs, 'mutation', typeName, deleteMutationName)));
|
|
402
401
|
}
|
|
403
402
|
const classDecl = t.classDeclaration(t.identifier(modelName), null, t.classBody(classBody));
|
|
404
403
|
statements.push(t.exportNamedDeclaration(classDecl));
|
|
@@ -44,25 +44,27 @@ export interface NestedSelectConfig {
|
|
|
44
44
|
filter?: Record<string, unknown>;
|
|
45
45
|
orderBy?: string[];
|
|
46
46
|
}
|
|
47
|
+
type DepthLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
|
|
48
|
+
type DecrementDepth = {
|
|
49
|
+
0: 0;
|
|
50
|
+
1: 0;
|
|
51
|
+
2: 1;
|
|
52
|
+
3: 2;
|
|
53
|
+
4: 3;
|
|
54
|
+
5: 4;
|
|
55
|
+
6: 5;
|
|
56
|
+
7: 6;
|
|
57
|
+
8: 7;
|
|
58
|
+
9: 8;
|
|
59
|
+
10: 9;
|
|
60
|
+
};
|
|
47
61
|
/**
|
|
48
62
|
* Recursively validates select objects, rejecting unknown keys.
|
|
49
63
|
*
|
|
50
|
-
* NOTE:
|
|
51
|
-
*
|
|
52
|
-
* with `S extends XxxSelect` constraints, which provides full
|
|
53
|
-
* autocompletion via TypeScript's contextual typing.
|
|
54
|
-
*
|
|
55
|
-
* @example
|
|
56
|
-
* // This will cause a type error because 'invalid' doesn't exist:
|
|
57
|
-
* type Result = DeepExact<{ id: true, invalid: true }, { id?: boolean }>;
|
|
58
|
-
* // Result = never (causes assignment error)
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* // This works because all fields are valid:
|
|
62
|
-
* type Result = DeepExact<{ id: true }, { id?: boolean; name?: boolean }>;
|
|
63
|
-
* // Result = { id: true }
|
|
64
|
+
* NOTE: Depth is intentionally capped to avoid circular-instantiation issues
|
|
65
|
+
* in very large cyclic schemas.
|
|
64
66
|
*/
|
|
65
|
-
export type DeepExact<T, Shape> = T extends Shape ? Exclude<keyof T, keyof Shape> extends never ? {
|
|
67
|
+
export type DeepExact<T, Shape, Depth extends DepthLevel = 10> = Depth extends 0 ? T extends Shape ? T : never : T extends Shape ? Exclude<keyof T, keyof Shape> extends never ? {
|
|
66
68
|
[K in keyof T]: K extends keyof Shape ? T[K] extends {
|
|
67
69
|
select: infer NS;
|
|
68
70
|
} ? Extract<Shape[K], {
|
|
@@ -70,10 +72,10 @@ export type DeepExact<T, Shape> = T extends Shape ? Exclude<keyof T, keyof Shape
|
|
|
70
72
|
}> extends {
|
|
71
73
|
select?: infer ShapeNS;
|
|
72
74
|
} ? DeepExact<Omit<T[K], 'select'> & {
|
|
73
|
-
select: DeepExact<NS, NonNullable<ShapeNS
|
|
75
|
+
select: DeepExact<NS, NonNullable<ShapeNS>, DecrementDepth[Depth]>;
|
|
74
76
|
}, Extract<Shape[K], {
|
|
75
77
|
select?: unknown;
|
|
76
|
-
}
|
|
78
|
+
}>, DecrementDepth[Depth]> : never : T[K] : never;
|
|
77
79
|
} : never : never;
|
|
78
80
|
/**
|
|
79
81
|
* Enforces exact select shape while keeping contextual typing on `S extends XxxSelect`.
|
|
@@ -81,6 +83,13 @@ export type DeepExact<T, Shape> = T extends Shape ? Exclude<keyof T, keyof Shape
|
|
|
81
83
|
* `{ select: S } & StrictSelect<S, XxxSelect>`.
|
|
82
84
|
*/
|
|
83
85
|
export type StrictSelect<S, Shape> = S extends DeepExact<S, Shape> ? {} : never;
|
|
86
|
+
/**
|
|
87
|
+
* Hook-optimized strict select variant.
|
|
88
|
+
*
|
|
89
|
+
* Uses a shallower recursion depth to keep editor autocomplete responsive
|
|
90
|
+
* in large schemas while still validating common nested-select mistakes.
|
|
91
|
+
*/
|
|
92
|
+
export type HookStrictSelect<S, Shape> = S extends DeepExact<S, Shape, 5> ? {} : never;
|
|
84
93
|
/**
|
|
85
94
|
* Infers the result type from a select configuration
|
|
86
95
|
*
|
|
@@ -205,3 +214,4 @@ export type MutationResult<TEntity, TSelect, TPayloadKey extends string> = Query
|
|
|
205
214
|
[EntityKey: string]: ResolveSelectResult<TEntity, TSelect>;
|
|
206
215
|
};
|
|
207
216
|
}>;
|
|
217
|
+
export {};
|
|
@@ -38,7 +38,12 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName, filterTypeName, orderByTypeName], true));
|
|
41
|
-
statements.push(createImportDeclaration('../../orm/select-types', [
|
|
41
|
+
statements.push(createImportDeclaration('../../orm/select-types', [
|
|
42
|
+
'FindManyArgs',
|
|
43
|
+
'InferSelectResult',
|
|
44
|
+
'ConnectionResult',
|
|
45
|
+
'HookStrictSelect',
|
|
46
|
+
], true));
|
|
42
47
|
// Re-exports
|
|
43
48
|
statements.push(createTypeReExport([selectTypeName, relationTypeName, filterTypeName, orderByTypeName], '../../orm/input-types'));
|
|
44
49
|
// Query key
|
|
@@ -155,8 +160,10 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
155
160
|
const fetchFnName = `fetch${ucFirst(pluralName)}Query`;
|
|
156
161
|
{
|
|
157
162
|
// Overload 1: with fields
|
|
158
|
-
const f1ParamType = t.
|
|
159
|
-
t.
|
|
163
|
+
const f1ParamType = t.tsIntersectionType([
|
|
164
|
+
t.tsTypeLiteral([
|
|
165
|
+
t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(withFieldsListSelectionType(sRef(), selectTypeName, filterTypeName, orderByTypeName))),
|
|
166
|
+
]),
|
|
160
167
|
]);
|
|
161
168
|
const f1Decl = exportAsyncDeclareFunction(fetchFnName, createSTypeParam(selectTypeName), [createFunctionParam('params', f1ParamType)], typeRef('Promise', [listResultTypeAST(sRef())]));
|
|
162
169
|
addJSDocComment(f1Decl, [
|
|
@@ -186,15 +193,17 @@ export function generateListQueryHook(table, options = {}) {
|
|
|
186
193
|
if (reactQueryEnabled) {
|
|
187
194
|
const prefetchFnName = `prefetch${ucFirst(pluralName)}Query`;
|
|
188
195
|
// Overload 1: with fields
|
|
189
|
-
const
|
|
190
|
-
t.
|
|
191
|
-
|
|
196
|
+
const p1BaseParamType = t.tsIntersectionType([
|
|
197
|
+
t.tsTypeLiteral([
|
|
198
|
+
t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(withFieldsListSelectionType(sRef(), selectTypeName, filterTypeName, orderByTypeName))),
|
|
199
|
+
]),
|
|
200
|
+
]);
|
|
192
201
|
const p1ParamType = hasRelationships && useCentralizedKeys
|
|
193
202
|
? t.tsIntersectionType([
|
|
194
|
-
|
|
203
|
+
p1BaseParamType,
|
|
195
204
|
scopeTypeLiteral(scopeTypeName),
|
|
196
205
|
])
|
|
197
|
-
:
|
|
206
|
+
: p1BaseParamType;
|
|
198
207
|
const p1Decl = exportAsyncDeclareFunction(prefetchFnName, createSTypeParam(selectTypeName), [
|
|
199
208
|
createFunctionParam('queryClient', typeRef('QueryClient')),
|
|
200
209
|
createFunctionParam('params', p1ParamType),
|
|
@@ -277,7 +286,7 @@ export function generateSingleQueryHook(table, options = {}) {
|
|
|
277
286
|
}
|
|
278
287
|
}
|
|
279
288
|
statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName], true));
|
|
280
|
-
statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', '
|
|
289
|
+
statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'HookStrictSelect'], true));
|
|
281
290
|
// Re-exports
|
|
282
291
|
statements.push(createTypeReExport([selectTypeName, relationTypeName], '../../orm/input-types'));
|
|
283
292
|
// Query key
|
|
@@ -345,12 +354,11 @@ export function generateSingleQueryHook(table, options = {}) {
|
|
|
345
354
|
docLines.push('```');
|
|
346
355
|
}
|
|
347
356
|
// Overload 1: with fields
|
|
348
|
-
const o1Props = [
|
|
349
|
-
t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)),
|
|
350
|
-
t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName))),
|
|
351
|
-
];
|
|
352
357
|
const o1ParamType = t.tsIntersectionType([
|
|
353
|
-
t.tsTypeLiteral(
|
|
358
|
+
t.tsTypeLiteral([
|
|
359
|
+
t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)),
|
|
360
|
+
t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName))),
|
|
361
|
+
]),
|
|
354
362
|
buildSingleOptionsType(singleResultTypeAST(sRef()), typeRef('TData')),
|
|
355
363
|
]);
|
|
356
364
|
const o1 = exportDeclareFunction(hookName, createSAndTDataTypeParams(selectTypeName, singleResultTypeAST(sRef())), [createFunctionParam('params', o1ParamType)], typeRef('UseQueryResult', [typeRef('TData')]));
|
|
@@ -393,11 +401,13 @@ export function generateSingleQueryHook(table, options = {}) {
|
|
|
393
401
|
const fetchFnName = `fetch${ucFirst(singularName)}Query`;
|
|
394
402
|
{
|
|
395
403
|
// Overload 1: with fields
|
|
396
|
-
const
|
|
397
|
-
t.
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
404
|
+
const f1ParamType = t.tsIntersectionType([
|
|
405
|
+
t.tsTypeLiteral([
|
|
406
|
+
t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)),
|
|
407
|
+
t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName))),
|
|
408
|
+
]),
|
|
409
|
+
]);
|
|
410
|
+
const f1Decl = exportAsyncDeclareFunction(fetchFnName, createSTypeParam(selectTypeName), [createFunctionParam('params', f1ParamType)], typeRef('Promise', [singleResultTypeAST(sRef())]));
|
|
401
411
|
addJSDocComment(f1Decl, [
|
|
402
412
|
`Fetch a single ${typeName} without React hooks`,
|
|
403
413
|
'',
|
|
@@ -424,16 +434,18 @@ export function generateSingleQueryHook(table, options = {}) {
|
|
|
424
434
|
if (reactQueryEnabled) {
|
|
425
435
|
const prefetchFnName = `prefetch${ucFirst(singularName)}Query`;
|
|
426
436
|
// Overload 1: with fields
|
|
427
|
-
const
|
|
428
|
-
t.
|
|
429
|
-
|
|
430
|
-
|
|
437
|
+
const p1BaseParamType = t.tsIntersectionType([
|
|
438
|
+
t.tsTypeLiteral([
|
|
439
|
+
t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)),
|
|
440
|
+
t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName))),
|
|
441
|
+
]),
|
|
442
|
+
]);
|
|
431
443
|
const p1ParamType = hasRelationships && useCentralizedKeys
|
|
432
444
|
? t.tsIntersectionType([
|
|
433
|
-
|
|
445
|
+
p1BaseParamType,
|
|
434
446
|
scopeTypeLiteral(scopeTypeName),
|
|
435
447
|
])
|
|
436
|
-
:
|
|
448
|
+
: p1BaseParamType;
|
|
437
449
|
const p1Decl = exportAsyncDeclareFunction(prefetchFnName, createSTypeParam(selectTypeName), [
|
|
438
450
|
createFunctionParam('queryClient', typeRef('QueryClient')),
|
|
439
451
|
createFunctionParam('params', p1ParamType),
|
|
@@ -60,6 +60,25 @@ function shouldSkipType(typeName, tableTypeNames) {
|
|
|
60
60
|
}
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
63
|
+
function collectCustomScalarTypes(typeRegistry) {
|
|
64
|
+
const customScalarTypes = new Set();
|
|
65
|
+
for (const [typeName, typeInfo] of typeRegistry) {
|
|
66
|
+
if (typeInfo.kind !== 'SCALAR')
|
|
67
|
+
continue;
|
|
68
|
+
if (SCALAR_NAMES.has(typeName))
|
|
69
|
+
continue;
|
|
70
|
+
customScalarTypes.add(typeName);
|
|
71
|
+
}
|
|
72
|
+
return Array.from(customScalarTypes).sort();
|
|
73
|
+
}
|
|
74
|
+
function generateCustomScalarTypes(customScalarTypes) {
|
|
75
|
+
const statements = [];
|
|
76
|
+
for (const scalarType of customScalarTypes) {
|
|
77
|
+
const alias = t.tsTypeAliasDeclaration(t.identifier(scalarType), null, t.tsUnknownKeyword());
|
|
78
|
+
statements.push(t.exportNamedDeclaration(alias));
|
|
79
|
+
}
|
|
80
|
+
return statements;
|
|
81
|
+
}
|
|
63
82
|
function generateEnumTypes(typeRegistry, tableTypeNames) {
|
|
64
83
|
const statements = [];
|
|
65
84
|
const generatedTypes = new Set();
|
|
@@ -224,6 +243,7 @@ export function generateSchemaTypesFile(options) {
|
|
|
224
243
|
const { typeRegistry, tableTypeNames } = options;
|
|
225
244
|
const allStatements = [];
|
|
226
245
|
let generatedTypes = new Set();
|
|
246
|
+
const customScalarTypes = collectCustomScalarTypes(typeRegistry);
|
|
227
247
|
const enumResult = generateEnumTypes(typeRegistry, tableTypeNames);
|
|
228
248
|
generatedTypes = new Set([...generatedTypes, ...enumResult.generatedTypes]);
|
|
229
249
|
const unionResult = generateUnionTypes(typeRegistry, tableTypeNames, generatedTypes);
|
|
@@ -239,6 +259,7 @@ export function generateSchemaTypesFile(options) {
|
|
|
239
259
|
typesImport.importKind = 'type';
|
|
240
260
|
allStatements.push(typesImport);
|
|
241
261
|
}
|
|
262
|
+
allStatements.push(...generateCustomScalarTypes(customScalarTypes));
|
|
242
263
|
allStatements.push(...enumResult.statements);
|
|
243
264
|
allStatements.push(...unionResult.statements);
|
|
244
265
|
allStatements.push(...inputResult.statements);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Types generator - generates types.ts with entity interfaces using Babel AST
|
|
3
3
|
*/
|
|
4
4
|
import * as t from '@babel/types';
|
|
5
|
+
import { SCALAR_NAMES } from './scalars';
|
|
5
6
|
import { generateCode } from './babel-ast';
|
|
6
7
|
import { fieldTypeToTs, getGeneratedFileHeader, getScalarFields, } from './utils';
|
|
7
8
|
/** All filter type configurations - scalar and list filters */
|
|
@@ -151,12 +152,34 @@ function createInterfaceDeclaration(name, properties) {
|
|
|
151
152
|
const interfaceDecl = t.tsInterfaceDeclaration(t.identifier(name), null, null, t.tsInterfaceBody(props));
|
|
152
153
|
return t.exportNamedDeclaration(interfaceDecl);
|
|
153
154
|
}
|
|
155
|
+
function createTypeAlias(name, typeNode) {
|
|
156
|
+
const typeAlias = t.tsTypeAliasDeclaration(t.identifier(name), null, typeNode);
|
|
157
|
+
return t.exportNamedDeclaration(typeAlias);
|
|
158
|
+
}
|
|
159
|
+
function collectCustomScalarTypes(tables, excludedTypeNames) {
|
|
160
|
+
const customScalarTypes = new Set();
|
|
161
|
+
const tableTypeNames = new Set(tables.map((table) => table.name));
|
|
162
|
+
for (const table of tables) {
|
|
163
|
+
for (const field of getScalarFields(table)) {
|
|
164
|
+
const cleanType = field.type.gqlType.replace(/!/g, '');
|
|
165
|
+
if (SCALAR_NAMES.has(cleanType))
|
|
166
|
+
continue;
|
|
167
|
+
if (excludedTypeNames.has(cleanType))
|
|
168
|
+
continue;
|
|
169
|
+
if (tableTypeNames.has(cleanType))
|
|
170
|
+
continue;
|
|
171
|
+
customScalarTypes.add(cleanType);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return Array.from(customScalarTypes).sort();
|
|
175
|
+
}
|
|
154
176
|
/**
|
|
155
177
|
* Generate types.ts content with all entity interfaces and base filter types
|
|
156
178
|
*/
|
|
157
179
|
export function generateTypesFile(tables, options = {}) {
|
|
158
180
|
const { enumsFromSchemaTypes = [] } = options;
|
|
159
181
|
const enumSet = new Set(enumsFromSchemaTypes);
|
|
182
|
+
const customScalarTypes = collectCustomScalarTypes(tables, enumSet);
|
|
160
183
|
const statements = [];
|
|
161
184
|
// Collect which enums are actually used by entity fields
|
|
162
185
|
const usedEnums = new Set();
|
|
@@ -179,6 +202,9 @@ export function generateTypesFile(tables, options = {}) {
|
|
|
179
202
|
importDecl.importKind = 'type';
|
|
180
203
|
statements.push(importDecl);
|
|
181
204
|
}
|
|
205
|
+
for (const scalarType of customScalarTypes) {
|
|
206
|
+
statements.push(createTypeAlias(scalarType, t.tsUnknownKeyword()));
|
|
207
|
+
}
|
|
182
208
|
// Generate entity interfaces
|
|
183
209
|
for (const table of tables) {
|
|
184
210
|
const scalarFields = getScalarFields(table);
|
|
@@ -72,7 +72,7 @@ export declare function getUpdateMutationFileName(table: CleanTable): string;
|
|
|
72
72
|
export declare function getDeleteMutationFileName(table: CleanTable): string;
|
|
73
73
|
/**
|
|
74
74
|
* Get the GraphQL query name for fetching all rows
|
|
75
|
-
* Uses inflection from
|
|
75
|
+
* Uses inflection from introspection, falls back to convention
|
|
76
76
|
*/
|
|
77
77
|
export declare function getAllRowsQueryName(table: CleanTable): string;
|
|
78
78
|
/**
|
|
@@ -128,7 +128,7 @@ export function getDeleteMutationFileName(table) {
|
|
|
128
128
|
// ============================================================================
|
|
129
129
|
/**
|
|
130
130
|
* Get the GraphQL query name for fetching all rows
|
|
131
|
-
* Uses inflection from
|
|
131
|
+
* Uses inflection from introspection, falls back to convention
|
|
132
132
|
*/
|
|
133
133
|
export function getAllRowsQueryName(table) {
|
|
134
134
|
return (table.query?.all ||
|
package/esm/core/custom-ast.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as t from 'gql-ast';
|
|
2
|
+
import { Kind } from 'graphql';
|
|
2
3
|
/**
|
|
3
4
|
* Get custom AST for MetaField type - handles PostgreSQL types that need subfield selections
|
|
4
5
|
*/
|
|
@@ -76,28 +77,28 @@ export function geometryPointAst(name) {
|
|
|
76
77
|
export function geometryCollectionAst(name) {
|
|
77
78
|
// Manually create inline fragment since gql-ast doesn't support it
|
|
78
79
|
const inlineFragment = {
|
|
79
|
-
kind:
|
|
80
|
+
kind: Kind.INLINE_FRAGMENT,
|
|
80
81
|
typeCondition: {
|
|
81
|
-
kind:
|
|
82
|
+
kind: Kind.NAMED_TYPE,
|
|
82
83
|
name: {
|
|
83
|
-
kind:
|
|
84
|
+
kind: Kind.NAME,
|
|
84
85
|
value: 'GeometryPoint',
|
|
85
86
|
},
|
|
86
87
|
},
|
|
87
88
|
selectionSet: {
|
|
88
|
-
kind:
|
|
89
|
+
kind: Kind.SELECTION_SET,
|
|
89
90
|
selections: [
|
|
90
91
|
{
|
|
91
|
-
kind:
|
|
92
|
+
kind: Kind.FIELD,
|
|
92
93
|
name: {
|
|
93
|
-
kind:
|
|
94
|
+
kind: Kind.NAME,
|
|
94
95
|
value: 'x',
|
|
95
96
|
},
|
|
96
97
|
},
|
|
97
98
|
{
|
|
98
|
-
kind:
|
|
99
|
+
kind: Kind.FIELD,
|
|
99
100
|
name: {
|
|
100
|
-
kind:
|
|
101
|
+
kind: Kind.NAME,
|
|
101
102
|
value: 'y',
|
|
102
103
|
},
|
|
103
104
|
},
|