@constructive-io/graphql-codegen 2.23.3 → 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/README.md +147 -2
- package/cli/codegen/babel-ast.d.ts +53 -0
- package/cli/codegen/babel-ast.js +160 -0
- package/cli/codegen/barrel.d.ts +7 -2
- package/cli/codegen/barrel.js +193 -102
- package/cli/codegen/client.js +61 -0
- package/cli/codegen/custom-mutations.d.ts +2 -12
- package/cli/codegen/custom-mutations.js +116 -124
- package/cli/codegen/custom-queries.d.ts +2 -10
- package/cli/codegen/custom-queries.js +236 -335
- package/cli/codegen/gql-ast.js +22 -1
- package/cli/codegen/index.d.ts +3 -0
- package/cli/codegen/index.js +73 -3
- package/cli/codegen/invalidation.d.ts +20 -0
- package/cli/codegen/invalidation.js +327 -0
- package/cli/codegen/mutation-keys.d.ts +24 -0
- package/cli/codegen/mutation-keys.js +247 -0
- package/cli/codegen/mutations.d.ts +5 -19
- package/cli/codegen/mutations.js +385 -383
- package/cli/codegen/orm/barrel.d.ts +1 -1
- package/cli/codegen/orm/barrel.js +42 -10
- package/cli/codegen/orm/client-generator.d.ts +1 -19
- package/cli/codegen/orm/client-generator.js +108 -77
- package/cli/codegen/orm/custom-ops-generator.d.ts +1 -12
- package/cli/codegen/orm/custom-ops-generator.js +192 -235
- package/cli/codegen/orm/input-types-generator.d.ts +13 -1
- package/cli/codegen/orm/input-types-generator.js +425 -147
- package/cli/codegen/orm/model-generator.d.ts +1 -19
- package/cli/codegen/orm/model-generator.js +229 -234
- package/cli/codegen/queries.d.ts +4 -12
- package/cli/codegen/queries.js +660 -390
- package/cli/codegen/query-keys.d.ts +15 -0
- package/cli/codegen/query-keys.js +477 -0
- package/cli/codegen/scalars.js +1 -0
- package/cli/codegen/schema-types-generator.d.ts +15 -10
- package/cli/codegen/schema-types-generator.js +87 -175
- package/cli/codegen/type-resolver.d.ts +1 -30
- package/cli/codegen/type-resolver.js +0 -53
- package/cli/codegen/types.d.ts +1 -1
- package/cli/codegen/types.js +76 -21
- package/cli/codegen/utils.d.ts +6 -0
- package/cli/codegen/utils.js +19 -0
- package/esm/cli/codegen/babel-ast.d.ts +53 -0
- package/esm/cli/codegen/babel-ast.js +111 -0
- package/esm/cli/codegen/barrel.d.ts +7 -2
- package/esm/cli/codegen/barrel.js +161 -103
- package/esm/cli/codegen/client.js +61 -0
- package/esm/cli/codegen/custom-mutations.d.ts +2 -12
- package/esm/cli/codegen/custom-mutations.js +83 -124
- package/esm/cli/codegen/custom-queries.d.ts +2 -10
- package/esm/cli/codegen/custom-queries.js +204 -336
- package/esm/cli/codegen/gql-ast.js +23 -2
- package/esm/cli/codegen/index.d.ts +3 -0
- package/esm/cli/codegen/index.js +69 -2
- package/esm/cli/codegen/invalidation.d.ts +20 -0
- package/esm/cli/codegen/invalidation.js +291 -0
- package/esm/cli/codegen/mutation-keys.d.ts +24 -0
- package/esm/cli/codegen/mutation-keys.js +211 -0
- package/esm/cli/codegen/mutations.d.ts +5 -19
- package/esm/cli/codegen/mutations.js +353 -384
- package/esm/cli/codegen/orm/barrel.d.ts +1 -1
- package/esm/cli/codegen/orm/barrel.js +10 -11
- package/esm/cli/codegen/orm/client-generator.d.ts +1 -19
- package/esm/cli/codegen/orm/client-generator.js +76 -78
- package/esm/cli/codegen/orm/custom-ops-generator.d.ts +1 -12
- package/esm/cli/codegen/orm/custom-ops-generator.js +160 -236
- package/esm/cli/codegen/orm/input-types-generator.d.ts +13 -1
- package/esm/cli/codegen/orm/input-types-generator.js +393 -148
- package/esm/cli/codegen/orm/model-generator.d.ts +1 -19
- package/esm/cli/codegen/orm/model-generator.js +197 -235
- package/esm/cli/codegen/queries.d.ts +4 -12
- package/esm/cli/codegen/queries.js +628 -391
- package/esm/cli/codegen/query-keys.d.ts +15 -0
- package/esm/cli/codegen/query-keys.js +441 -0
- package/esm/cli/codegen/scalars.js +1 -0
- package/esm/cli/codegen/schema-types-generator.d.ts +15 -10
- package/esm/cli/codegen/schema-types-generator.js +54 -175
- package/esm/cli/codegen/type-resolver.d.ts +1 -30
- package/esm/cli/codegen/type-resolver.js +0 -49
- package/esm/cli/codegen/types.d.ts +1 -1
- package/esm/cli/codegen/types.js +44 -22
- package/esm/cli/codegen/utils.d.ts +6 -0
- package/esm/cli/codegen/utils.js +18 -0
- package/esm/types/config.d.ts +75 -0
- package/esm/types/config.js +18 -0
- package/package.json +6 -4
- package/types/config.d.ts +75 -0
- package/types/config.js +19 -1
- package/cli/codegen/ts-ast.d.ts +0 -124
- package/cli/codegen/ts-ast.js +0 -280
- package/esm/cli/codegen/ts-ast.d.ts +0 -124
- package/esm/cli/codegen/ts-ast.js +0 -260
package/cli/codegen/utils.d.ts
CHANGED
|
@@ -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/cli/codegen/utils.js
CHANGED
|
@@ -35,6 +35,7 @@ exports.isRelationField = isRelationField;
|
|
|
35
35
|
exports.getScalarFields = getScalarFields;
|
|
36
36
|
exports.getPrimaryKeyInfo = getPrimaryKeyInfo;
|
|
37
37
|
exports.getPrimaryKeyFields = getPrimaryKeyFields;
|
|
38
|
+
exports.hasValidPrimaryKey = hasValidPrimaryKey;
|
|
38
39
|
exports.getQueryKeyPrefix = getQueryKeyPrefix;
|
|
39
40
|
exports.getGeneratedFileHeader = getGeneratedFileHeader;
|
|
40
41
|
exports.indent = indent;
|
|
@@ -331,6 +332,24 @@ function getPrimaryKeyInfo(table) {
|
|
|
331
332
|
function getPrimaryKeyFields(table) {
|
|
332
333
|
return getPrimaryKeyInfo(table).map((pk) => pk.name);
|
|
333
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Check if table has a valid single-field primary key
|
|
337
|
+
* Used to determine if a single query hook can be generated
|
|
338
|
+
* Tables with composite keys return false (handled as custom queries)
|
|
339
|
+
*/
|
|
340
|
+
function hasValidPrimaryKey(table) {
|
|
341
|
+
// Check for explicit primary key constraint with single field
|
|
342
|
+
const pk = table.constraints?.primaryKey?.[0];
|
|
343
|
+
if (pk && pk.fields.length === 1) {
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
// Check for 'id' field as fallback
|
|
347
|
+
const idField = table.fields.find((f) => f.name.toLowerCase() === 'id');
|
|
348
|
+
if (idField) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
334
353
|
// ============================================================================
|
|
335
354
|
// Query key generation
|
|
336
355
|
// ============================================================================
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Babel AST utilities for code generation
|
|
3
|
+
*
|
|
4
|
+
* Provides minimal helper functions for building TypeScript AST nodes.
|
|
5
|
+
* Use raw t.* calls for most operations - only helpers that provide
|
|
6
|
+
* real value beyond simple wrapping are included here.
|
|
7
|
+
*/
|
|
8
|
+
import generate from '@babel/generator';
|
|
9
|
+
import * as t from '@babel/types';
|
|
10
|
+
export { t, generate };
|
|
11
|
+
/**
|
|
12
|
+
* Generate code from an array of statements
|
|
13
|
+
*/
|
|
14
|
+
export declare function generateCode(statements: t.Statement[]): string;
|
|
15
|
+
/**
|
|
16
|
+
* Create a block comment
|
|
17
|
+
*/
|
|
18
|
+
export declare const commentBlock: (value: string) => t.CommentBlock;
|
|
19
|
+
/**
|
|
20
|
+
* Create a line comment
|
|
21
|
+
*/
|
|
22
|
+
export declare const commentLine: (value: string) => t.CommentLine;
|
|
23
|
+
/**
|
|
24
|
+
* Add a leading JSDoc comment to a node
|
|
25
|
+
*/
|
|
26
|
+
export declare function addJSDocComment<T extends t.Node>(node: T, lines: string[]): T;
|
|
27
|
+
/**
|
|
28
|
+
* Add a leading single-line comment to a node
|
|
29
|
+
*/
|
|
30
|
+
export declare function addLineComment<T extends t.Node>(node: T, text: string): T;
|
|
31
|
+
/**
|
|
32
|
+
* Create an 'as const' assertion - common pattern worth abstracting
|
|
33
|
+
*/
|
|
34
|
+
export declare function asConst(expression: t.Expression): t.TSAsExpression;
|
|
35
|
+
/**
|
|
36
|
+
* Create an array expression with 'as const' - very common pattern
|
|
37
|
+
*/
|
|
38
|
+
export declare function constArray(elements: (t.Expression | t.SpreadElement)[]): t.TSAsExpression;
|
|
39
|
+
/**
|
|
40
|
+
* Create a typed parameter - saves boilerplate for type annotations
|
|
41
|
+
*/
|
|
42
|
+
export declare function typedParam(name: string, typeAnnotation: t.TSType, optional?: boolean): t.Identifier;
|
|
43
|
+
/**
|
|
44
|
+
* Create keyof typeof expression - complex nested type operators
|
|
45
|
+
*/
|
|
46
|
+
export declare function keyofTypeof(name: string): t.TSTypeOperator;
|
|
47
|
+
/**
|
|
48
|
+
* Create a call expression with TypeScript type parameters
|
|
49
|
+
*
|
|
50
|
+
* This is used to generate typed function calls like:
|
|
51
|
+
* execute<ResultType, VariablesType>(document, variables)
|
|
52
|
+
*/
|
|
53
|
+
export declare function createTypedCallExpression(callee: t.Expression, args: (t.Expression | t.SpreadElement)[], typeParams: t.TSType[]): t.CallExpression;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Babel AST utilities for code generation
|
|
3
|
+
*
|
|
4
|
+
* Provides minimal helper functions for building TypeScript AST nodes.
|
|
5
|
+
* Use raw t.* calls for most operations - only helpers that provide
|
|
6
|
+
* real value beyond simple wrapping are included here.
|
|
7
|
+
*/
|
|
8
|
+
import generate from '@babel/generator';
|
|
9
|
+
import * as t from '@babel/types';
|
|
10
|
+
// Re-export for convenience
|
|
11
|
+
export { t, generate };
|
|
12
|
+
/**
|
|
13
|
+
* Generate code from an array of statements
|
|
14
|
+
*/
|
|
15
|
+
export function generateCode(statements) {
|
|
16
|
+
const program = t.program(statements);
|
|
17
|
+
// @ts-ignore - Babel types mismatch
|
|
18
|
+
return generate(program).code;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a block comment
|
|
22
|
+
*/
|
|
23
|
+
export const commentBlock = (value) => {
|
|
24
|
+
return {
|
|
25
|
+
type: 'CommentBlock',
|
|
26
|
+
value,
|
|
27
|
+
start: null,
|
|
28
|
+
end: null,
|
|
29
|
+
loc: null,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Create a line comment
|
|
34
|
+
*/
|
|
35
|
+
export const commentLine = (value) => {
|
|
36
|
+
return {
|
|
37
|
+
type: 'CommentLine',
|
|
38
|
+
value,
|
|
39
|
+
start: null,
|
|
40
|
+
end: null,
|
|
41
|
+
loc: null,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Add a leading JSDoc comment to a node
|
|
46
|
+
*/
|
|
47
|
+
export function addJSDocComment(node, lines) {
|
|
48
|
+
const commentText = lines.length === 1
|
|
49
|
+
? `* ${lines[0]} `
|
|
50
|
+
: `*\n${lines.map(line => ` * ${line}`).join('\n')}\n `;
|
|
51
|
+
if (!node.leadingComments) {
|
|
52
|
+
node.leadingComments = [];
|
|
53
|
+
}
|
|
54
|
+
node.leadingComments.push(commentBlock(commentText));
|
|
55
|
+
return node;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Add a leading single-line comment to a node
|
|
59
|
+
*/
|
|
60
|
+
export function addLineComment(node, text) {
|
|
61
|
+
if (!node.leadingComments) {
|
|
62
|
+
node.leadingComments = [];
|
|
63
|
+
}
|
|
64
|
+
node.leadingComments.push(commentLine(` ${text}`));
|
|
65
|
+
return node;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create an 'as const' assertion - common pattern worth abstracting
|
|
69
|
+
*/
|
|
70
|
+
export function asConst(expression) {
|
|
71
|
+
return t.tsAsExpression(expression, t.tsTypeReference(t.identifier('const')));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Create an array expression with 'as const' - very common pattern
|
|
75
|
+
*/
|
|
76
|
+
export function constArray(elements) {
|
|
77
|
+
return asConst(t.arrayExpression(elements));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create a typed parameter - saves boilerplate for type annotations
|
|
81
|
+
*/
|
|
82
|
+
export function typedParam(name, typeAnnotation, optional = false) {
|
|
83
|
+
const param = t.identifier(name);
|
|
84
|
+
param.typeAnnotation = t.tsTypeAnnotation(typeAnnotation);
|
|
85
|
+
param.optional = optional;
|
|
86
|
+
return param;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create keyof typeof expression - complex nested type operators
|
|
90
|
+
*/
|
|
91
|
+
export function keyofTypeof(name) {
|
|
92
|
+
const typeofOp = t.tsTypeOperator(t.tsTypeReference(t.identifier(name)));
|
|
93
|
+
typeofOp.operator = 'typeof';
|
|
94
|
+
const keyofOp = t.tsTypeOperator(typeofOp);
|
|
95
|
+
keyofOp.operator = 'keyof';
|
|
96
|
+
return keyofOp;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Create a call expression with TypeScript type parameters
|
|
100
|
+
*
|
|
101
|
+
* This is used to generate typed function calls like:
|
|
102
|
+
* execute<ResultType, VariablesType>(document, variables)
|
|
103
|
+
*/
|
|
104
|
+
export function createTypedCallExpression(callee, args, typeParams) {
|
|
105
|
+
const call = t.callExpression(callee, args);
|
|
106
|
+
if (typeParams.length > 0) {
|
|
107
|
+
// @ts-ignore - Babel types support typeParameters on CallExpression for TS
|
|
108
|
+
call.typeParameters = t.tsTypeParameterInstantiation(typeParams);
|
|
109
|
+
}
|
|
110
|
+
return call;
|
|
111
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Barrel file generators - creates index.ts files for exports
|
|
3
3
|
*
|
|
4
|
-
* Using
|
|
5
|
-
* and ts-morph has issues with insertText + addStatements combination.
|
|
4
|
+
* Using Babel AST for generating barrel (index.ts) files with re-exports.
|
|
6
5
|
*/
|
|
7
6
|
import type { CleanTable } from '../../types/schema';
|
|
8
7
|
/**
|
|
@@ -22,6 +21,12 @@ export declare function generateMutationsBarrel(tables: CleanTable[]): string;
|
|
|
22
21
|
export interface MainBarrelOptions {
|
|
23
22
|
hasSchemaTypes?: boolean;
|
|
24
23
|
hasMutations?: boolean;
|
|
24
|
+
/** Whether query-keys.ts was generated */
|
|
25
|
+
hasQueryKeys?: boolean;
|
|
26
|
+
/** Whether mutation-keys.ts was generated */
|
|
27
|
+
hasMutationKeys?: boolean;
|
|
28
|
+
/** Whether invalidation.ts was generated */
|
|
29
|
+
hasInvalidation?: boolean;
|
|
25
30
|
}
|
|
26
31
|
export declare function generateMainBarrel(tables: CleanTable[], options?: MainBarrelOptions | boolean): string;
|
|
27
32
|
/**
|
|
@@ -1,102 +1,134 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import * as t from '@babel/types';
|
|
2
|
+
import { generateCode, addJSDocComment } from './babel-ast';
|
|
3
|
+
import { getListQueryHookName, getSingleQueryHookName, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, hasValidPrimaryKey, } from './utils';
|
|
3
4
|
import { getOperationHookName } from './type-resolver';
|
|
5
|
+
/**
|
|
6
|
+
* Helper to create export * from './module' statement
|
|
7
|
+
*/
|
|
8
|
+
function exportAllFrom(modulePath) {
|
|
9
|
+
return t.exportAllDeclaration(t.stringLiteral(modulePath));
|
|
10
|
+
}
|
|
4
11
|
/**
|
|
5
12
|
* Generate the queries/index.ts barrel file
|
|
6
13
|
*/
|
|
7
14
|
export function generateQueriesBarrel(tables) {
|
|
8
|
-
const
|
|
15
|
+
const statements = [];
|
|
9
16
|
// Export all query hooks
|
|
10
17
|
for (const table of tables) {
|
|
11
18
|
const listHookName = getListQueryHookName(table);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
19
|
+
statements.push(exportAllFrom(`./${listHookName}`));
|
|
20
|
+
// Only export single query hook if table has valid primary key
|
|
21
|
+
if (hasValidPrimaryKey(table)) {
|
|
22
|
+
const singleHookName = getSingleQueryHookName(table);
|
|
23
|
+
statements.push(exportAllFrom(`./${singleHookName}`));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Add file header as leading comment on first statement
|
|
27
|
+
if (statements.length > 0) {
|
|
28
|
+
addJSDocComment(statements[0], [
|
|
29
|
+
'Query hooks barrel export',
|
|
30
|
+
'@generated by @constructive-io/graphql-codegen',
|
|
31
|
+
'DO NOT EDIT - changes will be overwritten',
|
|
32
|
+
]);
|
|
15
33
|
}
|
|
16
|
-
return
|
|
34
|
+
return generateCode(statements);
|
|
17
35
|
}
|
|
18
36
|
/**
|
|
19
37
|
* Generate the mutations/index.ts barrel file
|
|
20
38
|
*/
|
|
21
39
|
export function generateMutationsBarrel(tables) {
|
|
22
|
-
const
|
|
23
|
-
createFileHeader('Mutation hooks barrel export'),
|
|
24
|
-
'',
|
|
25
|
-
];
|
|
40
|
+
const statements = [];
|
|
26
41
|
// Export all mutation hooks
|
|
27
42
|
for (const table of tables) {
|
|
28
43
|
const createHookName = getCreateMutationHookName(table);
|
|
29
44
|
const updateHookName = getUpdateMutationHookName(table);
|
|
30
45
|
const deleteHookName = getDeleteMutationHookName(table);
|
|
31
|
-
|
|
46
|
+
statements.push(exportAllFrom(`./${createHookName}`));
|
|
32
47
|
// Only add update/delete if they exist
|
|
33
48
|
if (table.query?.update !== null) {
|
|
34
|
-
|
|
49
|
+
statements.push(exportAllFrom(`./${updateHookName}`));
|
|
35
50
|
}
|
|
36
51
|
if (table.query?.delete !== null) {
|
|
37
|
-
|
|
52
|
+
statements.push(exportAllFrom(`./${deleteHookName}`));
|
|
38
53
|
}
|
|
39
54
|
}
|
|
40
|
-
|
|
55
|
+
// Add file header as leading comment on first statement
|
|
56
|
+
if (statements.length > 0) {
|
|
57
|
+
addJSDocComment(statements[0], [
|
|
58
|
+
'Mutation hooks barrel export',
|
|
59
|
+
'@generated by @constructive-io/graphql-codegen',
|
|
60
|
+
'DO NOT EDIT - changes will be overwritten',
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
return generateCode(statements);
|
|
41
64
|
}
|
|
42
65
|
export function generateMainBarrel(tables, options = {}) {
|
|
43
66
|
// Support legacy signature where second arg was just hasSchemaTypes boolean
|
|
44
67
|
const opts = typeof options === 'boolean'
|
|
45
68
|
? { hasSchemaTypes: options, hasMutations: true }
|
|
46
69
|
: options;
|
|
47
|
-
const { hasSchemaTypes = false, hasMutations = true } = opts;
|
|
48
|
-
const tableNames = tables.map((
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
70
|
+
const { hasSchemaTypes = false, hasMutations = true, hasQueryKeys = false, hasMutationKeys = false, hasInvalidation = false, } = opts;
|
|
71
|
+
const tableNames = tables.map((tbl) => tbl.name).join(', ');
|
|
72
|
+
const statements = [];
|
|
73
|
+
// Client configuration
|
|
74
|
+
statements.push(exportAllFrom('./client'));
|
|
75
|
+
// Entity and filter types
|
|
76
|
+
statements.push(exportAllFrom('./types'));
|
|
77
|
+
// Schema types (input, payload, enum types)
|
|
78
|
+
if (hasSchemaTypes) {
|
|
79
|
+
statements.push(exportAllFrom('./schema-types'));
|
|
80
|
+
}
|
|
81
|
+
// Centralized query keys (for cache management)
|
|
82
|
+
if (hasQueryKeys) {
|
|
83
|
+
statements.push(exportAllFrom('./query-keys'));
|
|
84
|
+
}
|
|
85
|
+
// Centralized mutation keys (for tracking in-flight mutations)
|
|
86
|
+
if (hasMutationKeys) {
|
|
87
|
+
statements.push(exportAllFrom('./mutation-keys'));
|
|
88
|
+
}
|
|
89
|
+
// Cache invalidation helpers
|
|
90
|
+
if (hasInvalidation) {
|
|
91
|
+
statements.push(exportAllFrom('./invalidation'));
|
|
92
|
+
}
|
|
93
|
+
// Query hooks
|
|
94
|
+
statements.push(exportAllFrom('./queries'));
|
|
95
|
+
// Mutation hooks
|
|
96
|
+
if (hasMutations) {
|
|
97
|
+
statements.push(exportAllFrom('./mutations'));
|
|
98
|
+
}
|
|
99
|
+
// Add file header as leading comment on first statement
|
|
100
|
+
if (statements.length > 0) {
|
|
101
|
+
addJSDocComment(statements[0], [
|
|
102
|
+
'Auto-generated GraphQL SDK',
|
|
103
|
+
'@generated by @constructive-io/graphql-codegen',
|
|
104
|
+
'',
|
|
105
|
+
`Tables: ${tableNames}`,
|
|
106
|
+
'',
|
|
107
|
+
'Usage:',
|
|
108
|
+
'',
|
|
109
|
+
'1. Configure the client:',
|
|
110
|
+
'```ts',
|
|
111
|
+
"import { configure } from './generated';",
|
|
112
|
+
'',
|
|
113
|
+
'configure({',
|
|
114
|
+
" endpoint: 'https://api.example.com/graphql',",
|
|
115
|
+
" headers: { Authorization: 'Bearer <token>' },",
|
|
116
|
+
'});',
|
|
117
|
+
'```',
|
|
118
|
+
'',
|
|
119
|
+
'2. Use the hooks:',
|
|
120
|
+
'```tsx',
|
|
121
|
+
"import { useCarsQuery, useCreateCarMutation } from './generated';",
|
|
122
|
+
'',
|
|
123
|
+
'function MyComponent() {',
|
|
124
|
+
' const { data, isLoading } = useCarsQuery({ first: 10 });',
|
|
125
|
+
' const { mutate } = useCreateCarMutation();',
|
|
126
|
+
' // ...',
|
|
127
|
+
'}',
|
|
128
|
+
'```',
|
|
129
|
+
]);
|
|
130
|
+
}
|
|
131
|
+
return generateCode(statements);
|
|
100
132
|
}
|
|
101
133
|
// ============================================================================
|
|
102
134
|
// Custom operation barrels (includes both table and custom hooks)
|
|
@@ -105,60 +137,86 @@ ${mutationsExport}`;
|
|
|
105
137
|
* Generate queries barrel including custom query operations
|
|
106
138
|
*/
|
|
107
139
|
export function generateCustomQueriesBarrel(tables, customQueryNames) {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
'',
|
|
111
|
-
'// Table-based query hooks',
|
|
112
|
-
];
|
|
140
|
+
const statements = [];
|
|
141
|
+
const exportedHooks = new Set();
|
|
113
142
|
// Export all table query hooks
|
|
114
143
|
for (const table of tables) {
|
|
115
144
|
const listHookName = getListQueryHookName(table);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
145
|
+
if (!exportedHooks.has(listHookName)) {
|
|
146
|
+
statements.push(exportAllFrom(`./${listHookName}`));
|
|
147
|
+
exportedHooks.add(listHookName);
|
|
148
|
+
}
|
|
149
|
+
// Only export single query hook if table has valid primary key
|
|
150
|
+
if (hasValidPrimaryKey(table)) {
|
|
151
|
+
const singleHookName = getSingleQueryHookName(table);
|
|
152
|
+
if (!exportedHooks.has(singleHookName)) {
|
|
153
|
+
statements.push(exportAllFrom(`./${singleHookName}`));
|
|
154
|
+
exportedHooks.add(singleHookName);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Add custom query hooks (skip if already exported from table hooks)
|
|
159
|
+
for (const name of customQueryNames) {
|
|
160
|
+
const hookName = getOperationHookName(name, 'query');
|
|
161
|
+
if (!exportedHooks.has(hookName)) {
|
|
162
|
+
statements.push(exportAllFrom(`./${hookName}`));
|
|
163
|
+
exportedHooks.add(hookName);
|
|
127
164
|
}
|
|
128
165
|
}
|
|
129
|
-
|
|
166
|
+
// Add file header as leading comment on first statement
|
|
167
|
+
if (statements.length > 0) {
|
|
168
|
+
addJSDocComment(statements[0], [
|
|
169
|
+
'Query hooks barrel export',
|
|
170
|
+
'@generated by @constructive-io/graphql-codegen',
|
|
171
|
+
'DO NOT EDIT - changes will be overwritten',
|
|
172
|
+
]);
|
|
173
|
+
}
|
|
174
|
+
return generateCode(statements);
|
|
130
175
|
}
|
|
131
176
|
/**
|
|
132
177
|
* Generate mutations barrel including custom mutation operations
|
|
133
178
|
*/
|
|
134
179
|
export function generateCustomMutationsBarrel(tables, customMutationNames) {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
'',
|
|
138
|
-
'// Table-based mutation hooks',
|
|
139
|
-
];
|
|
180
|
+
const statements = [];
|
|
181
|
+
const exportedHooks = new Set();
|
|
140
182
|
// Export all table mutation hooks
|
|
141
183
|
for (const table of tables) {
|
|
142
184
|
const createHookName = getCreateMutationHookName(table);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
185
|
+
if (!exportedHooks.has(createHookName)) {
|
|
186
|
+
statements.push(exportAllFrom(`./${createHookName}`));
|
|
187
|
+
exportedHooks.add(createHookName);
|
|
188
|
+
}
|
|
146
189
|
// Only add update/delete if they exist
|
|
147
190
|
if (table.query?.update !== null) {
|
|
148
|
-
|
|
191
|
+
const updateHookName = getUpdateMutationHookName(table);
|
|
192
|
+
if (!exportedHooks.has(updateHookName)) {
|
|
193
|
+
statements.push(exportAllFrom(`./${updateHookName}`));
|
|
194
|
+
exportedHooks.add(updateHookName);
|
|
195
|
+
}
|
|
149
196
|
}
|
|
150
197
|
if (table.query?.delete !== null) {
|
|
151
|
-
|
|
198
|
+
const deleteHookName = getDeleteMutationHookName(table);
|
|
199
|
+
if (!exportedHooks.has(deleteHookName)) {
|
|
200
|
+
statements.push(exportAllFrom(`./${deleteHookName}`));
|
|
201
|
+
exportedHooks.add(deleteHookName);
|
|
202
|
+
}
|
|
152
203
|
}
|
|
153
204
|
}
|
|
154
|
-
// Add custom mutation hooks
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
lines.push(`export * from './${hookName}';`);
|
|
205
|
+
// Add custom mutation hooks (skip if already exported from table hooks)
|
|
206
|
+
for (const name of customMutationNames) {
|
|
207
|
+
const hookName = getOperationHookName(name, 'mutation');
|
|
208
|
+
if (!exportedHooks.has(hookName)) {
|
|
209
|
+
statements.push(exportAllFrom(`./${hookName}`));
|
|
210
|
+
exportedHooks.add(hookName);
|
|
161
211
|
}
|
|
162
212
|
}
|
|
163
|
-
|
|
213
|
+
// Add file header as leading comment on first statement
|
|
214
|
+
if (statements.length > 0) {
|
|
215
|
+
addJSDocComment(statements[0], [
|
|
216
|
+
'Mutation hooks barrel export',
|
|
217
|
+
'@generated by @constructive-io/graphql-codegen',
|
|
218
|
+
'DO NOT EDIT - changes will be overwritten',
|
|
219
|
+
]);
|
|
220
|
+
}
|
|
221
|
+
return generateCode(statements);
|
|
164
222
|
}
|
|
@@ -196,5 +196,66 @@ export async function executeWithErrors<TData = unknown, TVariables = Record<str
|
|
|
196
196
|
errors: json.errors ?? null,
|
|
197
197
|
};
|
|
198
198
|
}
|
|
199
|
+
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// QueryClient Factory
|
|
202
|
+
// ============================================================================
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Default QueryClient configuration optimized for GraphQL
|
|
206
|
+
*
|
|
207
|
+
* These defaults provide a good balance between freshness and performance:
|
|
208
|
+
* - staleTime: 1 minute - data considered fresh, won't refetch
|
|
209
|
+
* - gcTime: 5 minutes - unused data kept in cache
|
|
210
|
+
* - refetchOnWindowFocus: false - don't refetch when tab becomes active
|
|
211
|
+
* - retry: 1 - retry failed requests once
|
|
212
|
+
*/
|
|
213
|
+
export const defaultQueryClientOptions = {
|
|
214
|
+
defaultOptions: {
|
|
215
|
+
queries: {
|
|
216
|
+
staleTime: 1000 * 60, // 1 minute
|
|
217
|
+
gcTime: 1000 * 60 * 5, // 5 minutes
|
|
218
|
+
refetchOnWindowFocus: false,
|
|
219
|
+
retry: 1,
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* QueryClient options type for createQueryClient
|
|
226
|
+
*/
|
|
227
|
+
export interface CreateQueryClientOptions {
|
|
228
|
+
defaultOptions?: {
|
|
229
|
+
queries?: {
|
|
230
|
+
staleTime?: number;
|
|
231
|
+
gcTime?: number;
|
|
232
|
+
refetchOnWindowFocus?: boolean;
|
|
233
|
+
retry?: number | boolean;
|
|
234
|
+
retryDelay?: number | ((attemptIndex: number) => number);
|
|
235
|
+
};
|
|
236
|
+
mutations?: {
|
|
237
|
+
retry?: number | boolean;
|
|
238
|
+
retryDelay?: number | ((attemptIndex: number) => number);
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Note: createQueryClient is available when using with @tanstack/react-query
|
|
244
|
+
// Import QueryClient from '@tanstack/react-query' and use these options:
|
|
245
|
+
//
|
|
246
|
+
// import { QueryClient } from '@tanstack/react-query';
|
|
247
|
+
// const queryClient = new QueryClient(defaultQueryClientOptions);
|
|
248
|
+
//
|
|
249
|
+
// Or merge with your own options:
|
|
250
|
+
// const queryClient = new QueryClient({
|
|
251
|
+
// ...defaultQueryClientOptions,
|
|
252
|
+
// defaultOptions: {
|
|
253
|
+
// ...defaultQueryClientOptions.defaultOptions,
|
|
254
|
+
// queries: {
|
|
255
|
+
// ...defaultQueryClientOptions.defaultOptions.queries,
|
|
256
|
+
// staleTime: 30000, // Override specific options
|
|
257
|
+
// },
|
|
258
|
+
// },
|
|
259
|
+
// });
|
|
199
260
|
`;
|
|
200
261
|
}
|
|
@@ -21,28 +21,18 @@ export interface GenerateCustomMutationHookOptions {
|
|
|
21
21
|
typeRegistry: TypeRegistry;
|
|
22
22
|
maxDepth?: number;
|
|
23
23
|
skipQueryField?: boolean;
|
|
24
|
-
/** Whether to generate React Query hooks (default: true for backwards compatibility) */
|
|
25
24
|
reactQueryEnabled?: boolean;
|
|
26
|
-
/** Table entity type names (for import path resolution) */
|
|
27
25
|
tableTypeNames?: Set<string>;
|
|
26
|
+
useCentralizedKeys?: boolean;
|
|
28
27
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Generate a custom mutation hook file
|
|
31
|
-
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
32
|
-
*/
|
|
33
28
|
export declare function generateCustomMutationHook(options: GenerateCustomMutationHookOptions): GeneratedCustomMutationFile | null;
|
|
34
29
|
export interface GenerateAllCustomMutationHooksOptions {
|
|
35
30
|
operations: CleanOperation[];
|
|
36
31
|
typeRegistry: TypeRegistry;
|
|
37
32
|
maxDepth?: number;
|
|
38
33
|
skipQueryField?: boolean;
|
|
39
|
-
/** Whether to generate React Query hooks (default: true for backwards compatibility) */
|
|
40
34
|
reactQueryEnabled?: boolean;
|
|
41
|
-
/** Table entity type names (for import path resolution) */
|
|
42
35
|
tableTypeNames?: Set<string>;
|
|
36
|
+
useCentralizedKeys?: boolean;
|
|
43
37
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Generate all custom mutation hook files
|
|
46
|
-
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
47
|
-
*/
|
|
48
38
|
export declare function generateAllCustomMutationHooks(options: GenerateAllCustomMutationHooksOptions): GeneratedCustomMutationFile[];
|