@constructive-io/graphql-codegen 2.23.3 → 2.24.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 +147 -2
- package/cli/codegen/babel-ast.d.ts +46 -0
- package/cli/codegen/babel-ast.js +145 -0
- package/cli/codegen/barrel.d.ts +7 -2
- package/cli/codegen/barrel.js +159 -97
- 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 +246 -335
- package/cli/codegen/index.d.ts +3 -0
- package/cli/codegen/index.js +72 -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 +3 -19
- package/cli/codegen/mutations.js +372 -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 +403 -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 +3 -11
- package/cli/codegen/queries.js +582 -389
- 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/esm/cli/codegen/babel-ast.d.ts +46 -0
- package/esm/cli/codegen/babel-ast.js +97 -0
- package/esm/cli/codegen/barrel.d.ts +7 -2
- package/esm/cli/codegen/barrel.js +126 -97
- 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 +214 -336
- package/esm/cli/codegen/index.d.ts +3 -0
- package/esm/cli/codegen/index.js +68 -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 +3 -19
- package/esm/cli/codegen/mutations.js +340 -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 +371 -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 +3 -11
- package/esm/cli/codegen/queries.js +550 -390
- 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/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
|
@@ -1,381 +1,258 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as t from '@babel/types';
|
|
2
|
+
import { generateCode, addJSDocComment, typedParam } from './babel-ast';
|
|
2
3
|
import { buildCustomQueryString } from './schema-gql-ast';
|
|
3
4
|
import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, getQueryKeyName, createTypeTracker, } from './type-resolver';
|
|
4
|
-
import { ucFirst } from './utils';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import { ucFirst, getGeneratedFileHeader } from './utils';
|
|
6
|
+
function generateVariablesProperties(args, tracker) {
|
|
7
|
+
return args.map((arg) => ({
|
|
8
|
+
name: arg.name,
|
|
9
|
+
type: typeRefToTsType(arg.type, tracker),
|
|
10
|
+
optional: !isTypeRequired(arg.type),
|
|
11
|
+
docs: arg.description ? [arg.description] : undefined,
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
8
14
|
export function generateCustomQueryHook(options) {
|
|
9
|
-
const { operation, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames } = options;
|
|
10
|
-
const project = createProject();
|
|
15
|
+
const { operation, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames, useCentralizedKeys = true, } = options;
|
|
11
16
|
const hookName = getOperationHookName(operation.name, 'query');
|
|
12
17
|
const fileName = getOperationFileName(operation.name, 'query');
|
|
13
18
|
const variablesTypeName = getOperationVariablesTypeName(operation.name, 'query');
|
|
14
19
|
const resultTypeName = getOperationResultTypeName(operation.name, 'query');
|
|
15
20
|
const documentConstName = getDocumentConstName(operation.name, 'query');
|
|
16
21
|
const queryKeyName = getQueryKeyName(operation.name);
|
|
17
|
-
// Create type tracker to collect referenced types (with table type awareness)
|
|
18
22
|
const tracker = createTypeTracker({ tableTypeNames });
|
|
19
|
-
// Generate GraphQL document
|
|
20
23
|
const queryDocument = buildCustomQueryString({
|
|
21
24
|
operation,
|
|
22
25
|
typeRegistry,
|
|
23
26
|
maxDepth,
|
|
24
27
|
skipQueryField,
|
|
25
28
|
});
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
: `Custom query functions for ${operation.name}`;
|
|
31
|
-
sourceFile.insertText(0, createFileHeader(headerText) + '\n\n');
|
|
32
|
-
// Generate variables interface if there are arguments (with tracking)
|
|
33
|
-
let variablesProps = [];
|
|
34
|
-
if (operation.args.length > 0) {
|
|
35
|
-
variablesProps = generateVariablesProperties(operation.args, tracker);
|
|
36
|
-
}
|
|
37
|
-
// Generate result interface (with tracking)
|
|
29
|
+
const statements = [];
|
|
30
|
+
const variablesProps = operation.args.length > 0
|
|
31
|
+
? generateVariablesProperties(operation.args, tracker)
|
|
32
|
+
: [];
|
|
38
33
|
const resultType = typeRefToTsType(operation.returnType, tracker);
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
];
|
|
42
|
-
// Get importable types from tracker (separated by source)
|
|
43
|
-
const schemaTypes = tracker.getImportableTypes(); // From schema-types.ts
|
|
44
|
-
const tableTypes = tracker.getTableTypes(); // From types.ts
|
|
45
|
-
// Add imports - conditionally include React Query imports
|
|
46
|
-
const imports = [];
|
|
34
|
+
const schemaTypes = tracker.getImportableTypes();
|
|
35
|
+
const tableTypes = tracker.getTableTypes();
|
|
47
36
|
if (reactQueryEnabled) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
37
|
+
const reactQueryImport = t.importDeclaration([t.importSpecifier(t.identifier('useQuery'), t.identifier('useQuery'))], t.stringLiteral('@tanstack/react-query'));
|
|
38
|
+
statements.push(reactQueryImport);
|
|
39
|
+
const reactQueryTypeImport = t.importDeclaration([
|
|
40
|
+
t.importSpecifier(t.identifier('UseQueryOptions'), t.identifier('UseQueryOptions')),
|
|
41
|
+
t.importSpecifier(t.identifier('QueryClient'), t.identifier('QueryClient')),
|
|
42
|
+
], t.stringLiteral('@tanstack/react-query'));
|
|
43
|
+
reactQueryTypeImport.importKind = 'type';
|
|
44
|
+
statements.push(reactQueryTypeImport);
|
|
53
45
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Add types.ts import for table entity types
|
|
46
|
+
const clientImport = t.importDeclaration([t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], t.stringLiteral('../client'));
|
|
47
|
+
statements.push(clientImport);
|
|
48
|
+
const clientTypeImport = t.importDeclaration([t.importSpecifier(t.identifier('ExecuteOptions'), t.identifier('ExecuteOptions'))], t.stringLiteral('../client'));
|
|
49
|
+
clientTypeImport.importKind = 'type';
|
|
50
|
+
statements.push(clientTypeImport);
|
|
60
51
|
if (tableTypes.length > 0) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}));
|
|
52
|
+
const typesImport = t.importDeclaration(tableTypes.map((tt) => t.importSpecifier(t.identifier(tt), t.identifier(tt))), t.stringLiteral('../types'));
|
|
53
|
+
typesImport.importKind = 'type';
|
|
54
|
+
statements.push(typesImport);
|
|
65
55
|
}
|
|
66
|
-
// Add schema-types import for Input/Payload/Enum types
|
|
67
56
|
if (schemaTypes.length > 0) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}));
|
|
57
|
+
const schemaTypesImport = t.importDeclaration(schemaTypes.map((st) => t.importSpecifier(t.identifier(st), t.identifier(st))), t.stringLiteral('../schema-types'));
|
|
58
|
+
schemaTypesImport.importKind = 'type';
|
|
59
|
+
statements.push(schemaTypesImport);
|
|
72
60
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
docs: ['GraphQL query document'],
|
|
77
|
-
}));
|
|
78
|
-
// Add variables interface
|
|
79
|
-
if (operation.args.length > 0) {
|
|
80
|
-
sourceFile.addInterface(createInterface(variablesTypeName, variablesProps));
|
|
61
|
+
if (useCentralizedKeys) {
|
|
62
|
+
const queryKeyImport = t.importDeclaration([t.importSpecifier(t.identifier('customQueryKeys'), t.identifier('customQueryKeys'))], t.stringLiteral('../query-keys'));
|
|
63
|
+
statements.push(queryKeyImport);
|
|
81
64
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
65
|
+
const queryDocConst = t.variableDeclaration('const', [
|
|
66
|
+
t.variableDeclarator(t.identifier(documentConstName), t.templateLiteral([t.templateElement({ raw: '\n' + queryDocument, cooked: '\n' + queryDocument }, true)], [])),
|
|
67
|
+
]);
|
|
68
|
+
const queryDocExport = t.exportNamedDeclaration(queryDocConst);
|
|
69
|
+
addJSDocComment(queryDocExport, ['GraphQL query document']);
|
|
70
|
+
statements.push(queryDocExport);
|
|
85
71
|
if (operation.args.length > 0) {
|
|
86
|
-
|
|
87
|
-
|
|
72
|
+
const variablesInterfaceProps = variablesProps.map((vp) => {
|
|
73
|
+
const prop = t.tsPropertySignature(t.identifier(vp.name), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(vp.type))));
|
|
74
|
+
prop.optional = vp.optional;
|
|
75
|
+
return prop;
|
|
76
|
+
});
|
|
77
|
+
const variablesInterface = t.tsInterfaceDeclaration(t.identifier(variablesTypeName), null, null, t.tsInterfaceBody(variablesInterfaceProps));
|
|
78
|
+
statements.push(t.exportNamedDeclaration(variablesInterface));
|
|
79
|
+
}
|
|
80
|
+
const resultInterfaceBody = t.tsInterfaceBody([
|
|
81
|
+
t.tsPropertySignature(t.identifier(operation.name), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(resultType)))),
|
|
82
|
+
]);
|
|
83
|
+
const resultInterface = t.tsInterfaceDeclaration(t.identifier(resultTypeName), null, null, resultInterfaceBody);
|
|
84
|
+
statements.push(t.exportNamedDeclaration(resultInterface));
|
|
85
|
+
if (useCentralizedKeys) {
|
|
86
|
+
const queryKeyConst = t.variableDeclaration('const', [
|
|
87
|
+
t.variableDeclarator(t.identifier(queryKeyName), t.memberExpression(t.identifier('customQueryKeys'), t.identifier(operation.name))),
|
|
88
|
+
]);
|
|
89
|
+
const queryKeyExport = t.exportNamedDeclaration(queryKeyConst);
|
|
90
|
+
addJSDocComment(queryKeyExport, ['Query key factory - re-exported from query-keys.ts']);
|
|
91
|
+
statements.push(queryKeyExport);
|
|
92
|
+
}
|
|
93
|
+
else if (operation.args.length > 0) {
|
|
94
|
+
const queryKeyArrow = t.arrowFunctionExpression([typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)), true)], t.tsAsExpression(t.arrayExpression([t.stringLiteral(operation.name), t.identifier('variables')]), t.tsTypeReference(t.identifier('const'))));
|
|
95
|
+
const queryKeyConst = t.variableDeclaration('const', [
|
|
96
|
+
t.variableDeclarator(t.identifier(queryKeyName), queryKeyArrow),
|
|
97
|
+
]);
|
|
98
|
+
const queryKeyExport = t.exportNamedDeclaration(queryKeyConst);
|
|
99
|
+
addJSDocComment(queryKeyExport, ['Query key factory for caching']);
|
|
100
|
+
statements.push(queryKeyExport);
|
|
88
101
|
}
|
|
89
102
|
else {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
103
|
+
const queryKeyArrow = t.arrowFunctionExpression([], t.tsAsExpression(t.arrayExpression([t.stringLiteral(operation.name)]), t.tsTypeReference(t.identifier('const'))));
|
|
104
|
+
const queryKeyConst = t.variableDeclaration('const', [
|
|
105
|
+
t.variableDeclarator(t.identifier(queryKeyName), queryKeyArrow),
|
|
106
|
+
]);
|
|
107
|
+
const queryKeyExport = t.exportNamedDeclaration(queryKeyConst);
|
|
108
|
+
addJSDocComment(queryKeyExport, ['Query key factory for caching']);
|
|
109
|
+
statements.push(queryKeyExport);
|
|
93
110
|
}
|
|
94
|
-
// Generate hook function (only if React Query is enabled)
|
|
95
111
|
if (reactQueryEnabled) {
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
112
|
+
const hasArgs = operation.args.length > 0;
|
|
113
|
+
const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type));
|
|
114
|
+
const hookBodyStatements = [];
|
|
115
|
+
const useQueryOptions = [];
|
|
116
|
+
if (hasArgs) {
|
|
117
|
+
useQueryOptions.push(t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(queryKeyName), [t.identifier('variables')])));
|
|
118
|
+
useQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
|
|
119
|
+
t.identifier(documentConstName),
|
|
120
|
+
t.identifier('variables'),
|
|
121
|
+
]))));
|
|
122
|
+
if (hasRequiredArgs) {
|
|
123
|
+
useQueryOptions.push(t.objectProperty(t.identifier('enabled'), t.logicalExpression('&&', t.unaryExpression('!', t.unaryExpression('!', t.identifier('variables'))), t.binaryExpression('!==', t.optionalMemberExpression(t.identifier('options'), t.identifier('enabled'), false, true), t.booleanLiteral(false)))));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
useQueryOptions.push(t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(queryKeyName), [])));
|
|
128
|
+
useQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [t.identifier(documentConstName)]))));
|
|
129
|
+
}
|
|
130
|
+
useQueryOptions.push(t.spreadElement(t.identifier('options')));
|
|
131
|
+
hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useQuery'), [t.objectExpression(useQueryOptions)])));
|
|
132
|
+
const hookParams = [];
|
|
133
|
+
if (hasArgs) {
|
|
134
|
+
hookParams.push(typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)), !hasRequiredArgs));
|
|
135
|
+
}
|
|
136
|
+
const optionsTypeStr = `Omit<UseQueryOptions<${resultTypeName}, Error>, 'queryKey' | 'queryFn'>`;
|
|
137
|
+
const optionsParam = t.identifier('options');
|
|
138
|
+
optionsParam.optional = true;
|
|
139
|
+
optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr)));
|
|
140
|
+
hookParams.push(optionsParam);
|
|
141
|
+
const hookFunc = t.functionDeclaration(t.identifier(hookName), hookParams, t.blockStatement(hookBodyStatements));
|
|
142
|
+
const hookExport = t.exportNamedDeclaration(hookFunc);
|
|
143
|
+
const description = operation.description || `Query hook for ${operation.name}`;
|
|
144
|
+
const argNames = operation.args.map((a) => a.name).join(', ');
|
|
145
|
+
const exampleCall = hasArgs ? `${hookName}({ ${argNames} })` : `${hookName}()`;
|
|
146
|
+
addJSDocComment(hookExport, [
|
|
147
|
+
description,
|
|
148
|
+
'',
|
|
149
|
+
'@example',
|
|
150
|
+
'```tsx',
|
|
151
|
+
`const { data, isLoading } = ${exampleCall};`,
|
|
152
|
+
'',
|
|
153
|
+
`if (data?.${operation.name}) {`,
|
|
154
|
+
` console.log(data.${operation.name});`,
|
|
155
|
+
'}',
|
|
156
|
+
'```',
|
|
157
|
+
]);
|
|
158
|
+
statements.push(hookExport);
|
|
106
159
|
}
|
|
107
|
-
// Add standalone functions section
|
|
108
|
-
sourceFile.addStatements('\n// ============================================================================');
|
|
109
|
-
sourceFile.addStatements('// Standalone Functions (non-React)');
|
|
110
|
-
sourceFile.addStatements('// ============================================================================\n');
|
|
111
|
-
// Generate standalone fetch function
|
|
112
160
|
const fetchFnName = `fetch${ucFirst(operation.name)}Query`;
|
|
113
|
-
const fetchParams = generateFetchParameters(operation, variablesTypeName);
|
|
114
|
-
const fetchBody = generateFetchBody(operation, documentConstName, variablesTypeName, resultTypeName);
|
|
115
|
-
const fetchDoc = generateFetchDoc(operation, fetchFnName);
|
|
116
|
-
sourceFile.addFunction({
|
|
117
|
-
name: fetchFnName,
|
|
118
|
-
isExported: true,
|
|
119
|
-
isAsync: true,
|
|
120
|
-
parameters: fetchParams,
|
|
121
|
-
returnType: `Promise<${resultTypeName}>`,
|
|
122
|
-
statements: fetchBody,
|
|
123
|
-
docs: [{ description: fetchDoc }],
|
|
124
|
-
});
|
|
125
|
-
// Generate prefetch function (only if React Query is enabled)
|
|
126
|
-
if (reactQueryEnabled) {
|
|
127
|
-
const prefetchFnName = `prefetch${ucFirst(operation.name)}Query`;
|
|
128
|
-
const prefetchParams = generatePrefetchParameters(operation, variablesTypeName);
|
|
129
|
-
const prefetchBody = generatePrefetchBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
|
|
130
|
-
const prefetchDoc = generatePrefetchDoc(operation, prefetchFnName);
|
|
131
|
-
sourceFile.addFunction({
|
|
132
|
-
name: prefetchFnName,
|
|
133
|
-
isExported: true,
|
|
134
|
-
isAsync: true,
|
|
135
|
-
parameters: prefetchParams,
|
|
136
|
-
returnType: 'Promise<void>',
|
|
137
|
-
statements: prefetchBody,
|
|
138
|
-
docs: [{ description: prefetchDoc }],
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
return {
|
|
142
|
-
fileName,
|
|
143
|
-
content: getFormattedOutput(sourceFile),
|
|
144
|
-
operationName: operation.name,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
// ============================================================================
|
|
148
|
-
// Helper functions
|
|
149
|
-
// ============================================================================
|
|
150
|
-
/**
|
|
151
|
-
* Generate interface properties from CleanArguments
|
|
152
|
-
*/
|
|
153
|
-
function generateVariablesProperties(args, tracker) {
|
|
154
|
-
return args.map((arg) => ({
|
|
155
|
-
name: arg.name,
|
|
156
|
-
type: typeRefToTsType(arg.type, tracker),
|
|
157
|
-
optional: !isTypeRequired(arg.type),
|
|
158
|
-
docs: arg.description ? [arg.description] : undefined,
|
|
159
|
-
}));
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Generate hook function parameters
|
|
163
|
-
*/
|
|
164
|
-
function generateHookParameters(operation, variablesTypeName, resultTypeName) {
|
|
165
|
-
const params = [];
|
|
166
|
-
// Add variables parameter if there are required args
|
|
167
|
-
const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type));
|
|
168
|
-
if (operation.args.length > 0) {
|
|
169
|
-
params.push({
|
|
170
|
-
name: 'variables',
|
|
171
|
-
type: variablesTypeName,
|
|
172
|
-
hasQuestionToken: !hasRequiredArgs,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
// Add options parameter
|
|
176
|
-
params.push({
|
|
177
|
-
name: 'options',
|
|
178
|
-
type: `Omit<UseQueryOptions<${resultTypeName}, Error>, 'queryKey' | 'queryFn'>`,
|
|
179
|
-
hasQuestionToken: true,
|
|
180
|
-
});
|
|
181
|
-
return params;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Generate hook function body
|
|
185
|
-
*/
|
|
186
|
-
function generateHookBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName) {
|
|
187
161
|
const hasArgs = operation.args.length > 0;
|
|
188
162
|
const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type));
|
|
163
|
+
const fetchBodyStatements = [];
|
|
189
164
|
if (hasArgs) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
queryKey: ${queryKeyName}(variables),
|
|
196
|
-
queryFn: () => execute<${resultTypeName}, ${variablesTypeName}>(
|
|
197
|
-
${documentConstName},
|
|
198
|
-
variables
|
|
199
|
-
),
|
|
200
|
-
${enabledCondition}
|
|
201
|
-
...options,
|
|
202
|
-
});`;
|
|
165
|
+
fetchBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('execute'), [
|
|
166
|
+
t.identifier(documentConstName),
|
|
167
|
+
t.identifier('variables'),
|
|
168
|
+
t.identifier('options'),
|
|
169
|
+
])));
|
|
203
170
|
}
|
|
204
171
|
else {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
});`;
|
|
172
|
+
fetchBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('execute'), [
|
|
173
|
+
t.identifier(documentConstName),
|
|
174
|
+
t.identifier('undefined'),
|
|
175
|
+
t.identifier('options'),
|
|
176
|
+
])));
|
|
211
177
|
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Generate hook JSDoc documentation
|
|
215
|
-
*/
|
|
216
|
-
function generateHookDoc(operation, hookName) {
|
|
217
|
-
const description = operation.description
|
|
218
|
-
? operation.description
|
|
219
|
-
: `Query hook for ${operation.name}`;
|
|
220
|
-
const hasArgs = operation.args.length > 0;
|
|
221
|
-
let example;
|
|
178
|
+
const fetchParams = [];
|
|
222
179
|
if (hasArgs) {
|
|
223
|
-
|
|
224
|
-
example = `
|
|
225
|
-
@example
|
|
226
|
-
\`\`\`tsx
|
|
227
|
-
const { data, isLoading } = ${hookName}({ ${argNames} });
|
|
228
|
-
|
|
229
|
-
if (data?.${operation.name}) {
|
|
230
|
-
console.log(data.${operation.name});
|
|
231
|
-
}
|
|
232
|
-
\`\`\``;
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
example = `
|
|
236
|
-
@example
|
|
237
|
-
\`\`\`tsx
|
|
238
|
-
const { data, isLoading } = ${hookName}();
|
|
239
|
-
|
|
240
|
-
if (data?.${operation.name}) {
|
|
241
|
-
console.log(data.${operation.name});
|
|
242
|
-
}
|
|
243
|
-
\`\`\``;
|
|
244
|
-
}
|
|
245
|
-
return description + '\n' + example;
|
|
246
|
-
}
|
|
247
|
-
// ============================================================================
|
|
248
|
-
// Standalone function generators
|
|
249
|
-
// ============================================================================
|
|
250
|
-
/**
|
|
251
|
-
* Generate fetch function parameters
|
|
252
|
-
*/
|
|
253
|
-
function generateFetchParameters(operation, variablesTypeName) {
|
|
254
|
-
const params = [];
|
|
255
|
-
if (operation.args.length > 0) {
|
|
256
|
-
const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type));
|
|
257
|
-
params.push({
|
|
258
|
-
name: 'variables',
|
|
259
|
-
type: variablesTypeName,
|
|
260
|
-
hasQuestionToken: !hasRequiredArgs,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
params.push({
|
|
264
|
-
name: 'options',
|
|
265
|
-
type: 'ExecuteOptions',
|
|
266
|
-
hasQuestionToken: true,
|
|
267
|
-
});
|
|
268
|
-
return params;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Generate fetch function body
|
|
272
|
-
*/
|
|
273
|
-
function generateFetchBody(operation, documentConstName, variablesTypeName, resultTypeName) {
|
|
274
|
-
if (operation.args.length > 0) {
|
|
275
|
-
return `return execute<${resultTypeName}, ${variablesTypeName}>(
|
|
276
|
-
${documentConstName},
|
|
277
|
-
variables,
|
|
278
|
-
options
|
|
279
|
-
);`;
|
|
280
|
-
}
|
|
281
|
-
else {
|
|
282
|
-
return `return execute<${resultTypeName}>(${documentConstName}, undefined, options);`;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Generate fetch function documentation
|
|
287
|
-
*/
|
|
288
|
-
function generateFetchDoc(operation, fnName) {
|
|
289
|
-
const description = `Fetch ${operation.name} without React hooks`;
|
|
290
|
-
if (operation.args.length > 0) {
|
|
291
|
-
const argNames = operation.args.map((a) => a.name).join(', ');
|
|
292
|
-
return `${description}
|
|
293
|
-
|
|
294
|
-
@example
|
|
295
|
-
\`\`\`ts
|
|
296
|
-
const data = await ${fnName}({ ${argNames} });
|
|
297
|
-
\`\`\``;
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
return `${description}
|
|
301
|
-
|
|
302
|
-
@example
|
|
303
|
-
\`\`\`ts
|
|
304
|
-
const data = await ${fnName}();
|
|
305
|
-
\`\`\``;
|
|
180
|
+
fetchParams.push(typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)), !hasRequiredArgs));
|
|
306
181
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
name
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
@example
|
|
369
|
-
\`\`\`ts
|
|
370
|
-
await ${fnName}(queryClient);
|
|
371
|
-
\`\`\``;
|
|
182
|
+
fetchParams.push(typedParam('options', t.tsTypeReference(t.identifier('ExecuteOptions')), true));
|
|
183
|
+
const fetchFunc = t.functionDeclaration(t.identifier(fetchFnName), fetchParams, t.blockStatement(fetchBodyStatements));
|
|
184
|
+
fetchFunc.async = true;
|
|
185
|
+
fetchFunc.returnType = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Promise'), t.tsTypeParameterInstantiation([t.tsTypeReference(t.identifier(resultTypeName))])));
|
|
186
|
+
const fetchExport = t.exportNamedDeclaration(fetchFunc);
|
|
187
|
+
const argNames = operation.args.map((a) => a.name).join(', ');
|
|
188
|
+
const fetchExampleCall = hasArgs ? `${fetchFnName}({ ${argNames} })` : `${fetchFnName}()`;
|
|
189
|
+
addJSDocComment(fetchExport, [
|
|
190
|
+
`Fetch ${operation.name} without React hooks`,
|
|
191
|
+
'',
|
|
192
|
+
'@example',
|
|
193
|
+
'```ts',
|
|
194
|
+
`const data = await ${fetchExampleCall};`,
|
|
195
|
+
'```',
|
|
196
|
+
]);
|
|
197
|
+
statements.push(fetchExport);
|
|
198
|
+
if (reactQueryEnabled) {
|
|
199
|
+
const prefetchFnName = `prefetch${ucFirst(operation.name)}Query`;
|
|
200
|
+
const prefetchBodyStatements = [];
|
|
201
|
+
const prefetchQueryOptions = [];
|
|
202
|
+
if (hasArgs) {
|
|
203
|
+
prefetchQueryOptions.push(t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(queryKeyName), [t.identifier('variables')])));
|
|
204
|
+
prefetchQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
|
|
205
|
+
t.identifier(documentConstName),
|
|
206
|
+
t.identifier('variables'),
|
|
207
|
+
t.identifier('options'),
|
|
208
|
+
]))));
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
prefetchQueryOptions.push(t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(queryKeyName), [])));
|
|
212
|
+
prefetchQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
|
|
213
|
+
t.identifier(documentConstName),
|
|
214
|
+
t.identifier('undefined'),
|
|
215
|
+
t.identifier('options'),
|
|
216
|
+
]))));
|
|
217
|
+
}
|
|
218
|
+
prefetchBodyStatements.push(t.expressionStatement(t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), [t.objectExpression(prefetchQueryOptions)]))));
|
|
219
|
+
const prefetchParams = [
|
|
220
|
+
typedParam('queryClient', t.tsTypeReference(t.identifier('QueryClient'))),
|
|
221
|
+
];
|
|
222
|
+
if (hasArgs) {
|
|
223
|
+
prefetchParams.push(typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)), !hasRequiredArgs));
|
|
224
|
+
}
|
|
225
|
+
prefetchParams.push(typedParam('options', t.tsTypeReference(t.identifier('ExecuteOptions')), true));
|
|
226
|
+
const prefetchFunc = t.functionDeclaration(t.identifier(prefetchFnName), prefetchParams, t.blockStatement(prefetchBodyStatements));
|
|
227
|
+
prefetchFunc.async = true;
|
|
228
|
+
prefetchFunc.returnType = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Promise'), t.tsTypeParameterInstantiation([t.tsVoidKeyword()])));
|
|
229
|
+
const prefetchExport = t.exportNamedDeclaration(prefetchFunc);
|
|
230
|
+
const prefetchExampleCall = hasArgs
|
|
231
|
+
? `${prefetchFnName}(queryClient, { ${argNames} })`
|
|
232
|
+
: `${prefetchFnName}(queryClient)`;
|
|
233
|
+
addJSDocComment(prefetchExport, [
|
|
234
|
+
`Prefetch ${operation.name} for SSR or cache warming`,
|
|
235
|
+
'',
|
|
236
|
+
'@example',
|
|
237
|
+
'```ts',
|
|
238
|
+
`await ${prefetchExampleCall};`,
|
|
239
|
+
'```',
|
|
240
|
+
]);
|
|
241
|
+
statements.push(prefetchExport);
|
|
372
242
|
}
|
|
243
|
+
const code = generateCode(statements);
|
|
244
|
+
const headerText = reactQueryEnabled
|
|
245
|
+
? `Custom query hook for ${operation.name}`
|
|
246
|
+
: `Custom query functions for ${operation.name}`;
|
|
247
|
+
const content = getGeneratedFileHeader(headerText) + '\n\n' + code;
|
|
248
|
+
return {
|
|
249
|
+
fileName,
|
|
250
|
+
content,
|
|
251
|
+
operationName: operation.name,
|
|
252
|
+
};
|
|
373
253
|
}
|
|
374
|
-
/**
|
|
375
|
-
* Generate all custom query hook files
|
|
376
|
-
*/
|
|
377
254
|
export function generateAllCustomQueryHooks(options) {
|
|
378
|
-
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames } = options;
|
|
255
|
+
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames, useCentralizedKeys = true, } = options;
|
|
379
256
|
return operations
|
|
380
257
|
.filter((op) => op.kind === 'query')
|
|
381
258
|
.map((operation) => generateCustomQueryHook({
|
|
@@ -385,5 +262,6 @@ export function generateAllCustomQueryHooks(options) {
|
|
|
385
262
|
skipQueryField,
|
|
386
263
|
reactQueryEnabled,
|
|
387
264
|
tableTypeNames,
|
|
265
|
+
useCentralizedKeys,
|
|
388
266
|
}));
|
|
389
267
|
}
|
|
@@ -69,3 +69,6 @@ export { generateAllMutationHooks, generateCreateMutationHook, generateUpdateMut
|
|
|
69
69
|
export { generateAllCustomQueryHooks, generateCustomQueryHook, } from './custom-queries';
|
|
70
70
|
export { generateAllCustomMutationHooks, generateCustomMutationHook, } from './custom-mutations';
|
|
71
71
|
export { generateQueriesBarrel, generateMutationsBarrel, generateMainBarrel, generateCustomQueriesBarrel, generateCustomMutationsBarrel, } from './barrel';
|
|
72
|
+
export { generateQueryKeysFile } from './query-keys';
|
|
73
|
+
export { generateMutationKeysFile } from './mutation-keys';
|
|
74
|
+
export { generateInvalidationFile } from './invalidation';
|