@constructive-io/graphql-codegen 2.20.1 → 2.21.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 +15 -3
- package/__tests__/codegen/react-query-optional.test.d.ts +1 -0
- package/__tests__/codegen/react-query-optional.test.js +292 -0
- package/cli/codegen/custom-mutations.d.ts +7 -1
- package/cli/codegen/custom-mutations.js +11 -3
- package/cli/codegen/custom-queries.d.ts +4 -0
- package/cli/codegen/custom-queries.js +48 -38
- package/cli/codegen/index.js +5 -2
- package/cli/codegen/mutations.d.ts +12 -4
- package/cli/codegen/mutations.js +29 -7
- package/cli/codegen/queries.d.ts +7 -3
- package/cli/codegen/queries.js +154 -136
- package/esm/__tests__/codegen/react-query-optional.test.d.ts +1 -0
- package/esm/__tests__/codegen/react-query-optional.test.js +290 -0
- package/esm/cli/codegen/custom-mutations.d.ts +7 -1
- package/esm/cli/codegen/custom-mutations.js +11 -3
- package/esm/cli/codegen/custom-queries.d.ts +4 -0
- package/esm/cli/codegen/custom-queries.js +48 -38
- package/esm/cli/codegen/index.js +5 -2
- package/esm/cli/codegen/mutations.d.ts +12 -4
- package/esm/cli/codegen/mutations.js +29 -7
- package/esm/cli/codegen/queries.d.ts +7 -3
- package/esm/cli/codegen/queries.js +154 -136
- package/esm/types/config.d.ts +16 -1
- package/esm/types/config.js +6 -0
- package/package.json +2 -2
- package/types/config.d.ts +16 -1
- package/types/config.js +6 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for React Query optional flag in code generators
|
|
3
|
+
*
|
|
4
|
+
* Verifies that when reactQueryEnabled is false:
|
|
5
|
+
* - Query generators skip React Query imports and hooks
|
|
6
|
+
* - Mutation generators return null (since they require React Query)
|
|
7
|
+
* - Standalone fetch functions are still generated for queries
|
|
8
|
+
*/
|
|
9
|
+
import { generateListQueryHook, generateSingleQueryHook, generateAllQueryHooks } from '../../cli/codegen/queries';
|
|
10
|
+
import { generateCreateMutationHook, generateUpdateMutationHook, generateDeleteMutationHook, generateAllMutationHooks } from '../../cli/codegen/mutations';
|
|
11
|
+
import { generateCustomQueryHook, generateAllCustomQueryHooks } from '../../cli/codegen/custom-queries';
|
|
12
|
+
import { generateCustomMutationHook, generateAllCustomMutationHooks } from '../../cli/codegen/custom-mutations';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Test Fixtures
|
|
15
|
+
// ============================================================================
|
|
16
|
+
const fieldTypes = {
|
|
17
|
+
uuid: { gqlType: 'UUID', isArray: false },
|
|
18
|
+
string: { gqlType: 'String', isArray: false },
|
|
19
|
+
int: { gqlType: 'Int', isArray: false },
|
|
20
|
+
datetime: { gqlType: 'Datetime', isArray: false },
|
|
21
|
+
};
|
|
22
|
+
const emptyRelations = {
|
|
23
|
+
belongsTo: [],
|
|
24
|
+
hasOne: [],
|
|
25
|
+
hasMany: [],
|
|
26
|
+
manyToMany: [],
|
|
27
|
+
};
|
|
28
|
+
function createTable(partial) {
|
|
29
|
+
return {
|
|
30
|
+
name: partial.name,
|
|
31
|
+
fields: partial.fields ?? [],
|
|
32
|
+
relations: partial.relations ?? emptyRelations,
|
|
33
|
+
query: partial.query,
|
|
34
|
+
inflection: partial.inflection,
|
|
35
|
+
constraints: partial.constraints,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const userTable = createTable({
|
|
39
|
+
name: 'User',
|
|
40
|
+
fields: [
|
|
41
|
+
{ name: 'id', type: fieldTypes.uuid },
|
|
42
|
+
{ name: 'email', type: fieldTypes.string },
|
|
43
|
+
{ name: 'name', type: fieldTypes.string },
|
|
44
|
+
{ name: 'createdAt', type: fieldTypes.datetime },
|
|
45
|
+
],
|
|
46
|
+
query: {
|
|
47
|
+
all: 'users',
|
|
48
|
+
one: 'user',
|
|
49
|
+
create: 'createUser',
|
|
50
|
+
update: 'updateUser',
|
|
51
|
+
delete: 'deleteUser',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
function createTypeRef(kind, name, ofType) {
|
|
55
|
+
return { kind, name, ofType };
|
|
56
|
+
}
|
|
57
|
+
const sampleQueryOperation = {
|
|
58
|
+
name: 'currentUser',
|
|
59
|
+
kind: 'query',
|
|
60
|
+
args: [],
|
|
61
|
+
returnType: createTypeRef('OBJECT', 'User'),
|
|
62
|
+
description: 'Get the current authenticated user',
|
|
63
|
+
};
|
|
64
|
+
const sampleMutationOperation = {
|
|
65
|
+
name: 'login',
|
|
66
|
+
kind: 'mutation',
|
|
67
|
+
args: [
|
|
68
|
+
{ name: 'email', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) },
|
|
69
|
+
{ name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) },
|
|
70
|
+
],
|
|
71
|
+
returnType: createTypeRef('OBJECT', 'LoginPayload'),
|
|
72
|
+
description: 'Authenticate user',
|
|
73
|
+
};
|
|
74
|
+
const emptyTypeRegistry = new Map();
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// Tests - Query Generators with reactQueryEnabled: false
|
|
77
|
+
// ============================================================================
|
|
78
|
+
describe('Query generators with reactQueryEnabled: false', () => {
|
|
79
|
+
describe('generateListQueryHook', () => {
|
|
80
|
+
it('should not include React Query imports when disabled', () => {
|
|
81
|
+
const result = generateListQueryHook(userTable, { reactQueryEnabled: false });
|
|
82
|
+
expect(result.content).not.toContain('@tanstack/react-query');
|
|
83
|
+
expect(result.content).not.toContain('useQuery');
|
|
84
|
+
expect(result.content).not.toContain('UseQueryOptions');
|
|
85
|
+
});
|
|
86
|
+
it('should not include useXxxQuery hook when disabled', () => {
|
|
87
|
+
const result = generateListQueryHook(userTable, { reactQueryEnabled: false });
|
|
88
|
+
expect(result.content).not.toContain('export function useUsersQuery');
|
|
89
|
+
});
|
|
90
|
+
it('should not include prefetch function when disabled', () => {
|
|
91
|
+
const result = generateListQueryHook(userTable, { reactQueryEnabled: false });
|
|
92
|
+
expect(result.content).not.toContain('export async function prefetchUsersQuery');
|
|
93
|
+
});
|
|
94
|
+
it('should still include standalone fetch function when disabled', () => {
|
|
95
|
+
const result = generateListQueryHook(userTable, { reactQueryEnabled: false });
|
|
96
|
+
expect(result.content).toContain('export async function fetchUsersQuery');
|
|
97
|
+
});
|
|
98
|
+
it('should still include GraphQL document when disabled', () => {
|
|
99
|
+
const result = generateListQueryHook(userTable, { reactQueryEnabled: false });
|
|
100
|
+
expect(result.content).toContain('usersQueryDocument');
|
|
101
|
+
});
|
|
102
|
+
it('should still include query key factory when disabled', () => {
|
|
103
|
+
const result = generateListQueryHook(userTable, { reactQueryEnabled: false });
|
|
104
|
+
expect(result.content).toContain('usersQueryKey');
|
|
105
|
+
});
|
|
106
|
+
it('should update file header when disabled', () => {
|
|
107
|
+
const result = generateListQueryHook(userTable, { reactQueryEnabled: false });
|
|
108
|
+
expect(result.content).toContain('List query functions for User');
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe('generateSingleQueryHook', () => {
|
|
112
|
+
it('should not include React Query imports when disabled', () => {
|
|
113
|
+
const result = generateSingleQueryHook(userTable, { reactQueryEnabled: false });
|
|
114
|
+
expect(result.content).not.toContain('@tanstack/react-query');
|
|
115
|
+
expect(result.content).not.toContain('useQuery');
|
|
116
|
+
});
|
|
117
|
+
it('should not include useXxxQuery hook when disabled', () => {
|
|
118
|
+
const result = generateSingleQueryHook(userTable, { reactQueryEnabled: false });
|
|
119
|
+
expect(result.content).not.toContain('export function useUserQuery');
|
|
120
|
+
});
|
|
121
|
+
it('should still include standalone fetch function when disabled', () => {
|
|
122
|
+
const result = generateSingleQueryHook(userTable, { reactQueryEnabled: false });
|
|
123
|
+
expect(result.content).toContain('export async function fetchUserQuery');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe('generateAllQueryHooks', () => {
|
|
127
|
+
it('should generate files without React Query when disabled', () => {
|
|
128
|
+
const results = generateAllQueryHooks([userTable], { reactQueryEnabled: false });
|
|
129
|
+
expect(results.length).toBe(2); // list + single
|
|
130
|
+
for (const result of results) {
|
|
131
|
+
expect(result.content).not.toContain('@tanstack/react-query');
|
|
132
|
+
expect(result.content).not.toContain('useQuery');
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Tests - Query Generators with reactQueryEnabled: true (default)
|
|
139
|
+
// ============================================================================
|
|
140
|
+
describe('Query generators with reactQueryEnabled: true (default)', () => {
|
|
141
|
+
describe('generateListQueryHook', () => {
|
|
142
|
+
it('should include React Query imports by default', () => {
|
|
143
|
+
const result = generateListQueryHook(userTable);
|
|
144
|
+
expect(result.content).toContain('@tanstack/react-query');
|
|
145
|
+
expect(result.content).toContain('useQuery');
|
|
146
|
+
});
|
|
147
|
+
it('should include useXxxQuery hook by default', () => {
|
|
148
|
+
const result = generateListQueryHook(userTable);
|
|
149
|
+
expect(result.content).toContain('export function useUsersQuery');
|
|
150
|
+
});
|
|
151
|
+
it('should include prefetch function by default', () => {
|
|
152
|
+
const result = generateListQueryHook(userTable);
|
|
153
|
+
expect(result.content).toContain('export async function prefetchUsersQuery');
|
|
154
|
+
});
|
|
155
|
+
it('should include standalone fetch function by default', () => {
|
|
156
|
+
const result = generateListQueryHook(userTable);
|
|
157
|
+
expect(result.content).toContain('export async function fetchUsersQuery');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
// ============================================================================
|
|
162
|
+
// Tests - Mutation Generators with reactQueryEnabled: false
|
|
163
|
+
// ============================================================================
|
|
164
|
+
describe('Mutation generators with reactQueryEnabled: false', () => {
|
|
165
|
+
describe('generateCreateMutationHook', () => {
|
|
166
|
+
it('should return null when disabled', () => {
|
|
167
|
+
const result = generateCreateMutationHook(userTable, { reactQueryEnabled: false });
|
|
168
|
+
expect(result).toBeNull();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe('generateUpdateMutationHook', () => {
|
|
172
|
+
it('should return null when disabled', () => {
|
|
173
|
+
const result = generateUpdateMutationHook(userTable, { reactQueryEnabled: false });
|
|
174
|
+
expect(result).toBeNull();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe('generateDeleteMutationHook', () => {
|
|
178
|
+
it('should return null when disabled', () => {
|
|
179
|
+
const result = generateDeleteMutationHook(userTable, { reactQueryEnabled: false });
|
|
180
|
+
expect(result).toBeNull();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('generateAllMutationHooks', () => {
|
|
184
|
+
it('should return empty array when disabled', () => {
|
|
185
|
+
const results = generateAllMutationHooks([userTable], { reactQueryEnabled: false });
|
|
186
|
+
expect(results).toEqual([]);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
// ============================================================================
|
|
191
|
+
// Tests - Mutation Generators with reactQueryEnabled: true (default)
|
|
192
|
+
// ============================================================================
|
|
193
|
+
describe('Mutation generators with reactQueryEnabled: true (default)', () => {
|
|
194
|
+
describe('generateCreateMutationHook', () => {
|
|
195
|
+
it('should return mutation file by default', () => {
|
|
196
|
+
const result = generateCreateMutationHook(userTable);
|
|
197
|
+
expect(result).not.toBeNull();
|
|
198
|
+
expect(result.content).toContain('useMutation');
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
describe('generateAllMutationHooks', () => {
|
|
202
|
+
it('should return mutation files by default', () => {
|
|
203
|
+
const results = generateAllMutationHooks([userTable]);
|
|
204
|
+
expect(results.length).toBeGreaterThan(0);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
// ============================================================================
|
|
209
|
+
// Tests - Custom Query Generators with reactQueryEnabled: false
|
|
210
|
+
// ============================================================================
|
|
211
|
+
describe('Custom query generators with reactQueryEnabled: false', () => {
|
|
212
|
+
describe('generateCustomQueryHook', () => {
|
|
213
|
+
it('should not include React Query imports when disabled', () => {
|
|
214
|
+
const result = generateCustomQueryHook({
|
|
215
|
+
operation: sampleQueryOperation,
|
|
216
|
+
typeRegistry: emptyTypeRegistry,
|
|
217
|
+
reactQueryEnabled: false,
|
|
218
|
+
});
|
|
219
|
+
expect(result.content).not.toContain('@tanstack/react-query');
|
|
220
|
+
expect(result.content).not.toContain('useQuery');
|
|
221
|
+
});
|
|
222
|
+
it('should not include useXxxQuery hook when disabled', () => {
|
|
223
|
+
const result = generateCustomQueryHook({
|
|
224
|
+
operation: sampleQueryOperation,
|
|
225
|
+
typeRegistry: emptyTypeRegistry,
|
|
226
|
+
reactQueryEnabled: false,
|
|
227
|
+
});
|
|
228
|
+
expect(result.content).not.toContain('export function useCurrentUserQuery');
|
|
229
|
+
});
|
|
230
|
+
it('should still include standalone fetch function when disabled', () => {
|
|
231
|
+
const result = generateCustomQueryHook({
|
|
232
|
+
operation: sampleQueryOperation,
|
|
233
|
+
typeRegistry: emptyTypeRegistry,
|
|
234
|
+
reactQueryEnabled: false,
|
|
235
|
+
});
|
|
236
|
+
expect(result.content).toContain('export async function fetchCurrentUserQuery');
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
describe('generateAllCustomQueryHooks', () => {
|
|
240
|
+
it('should generate files without React Query when disabled', () => {
|
|
241
|
+
const results = generateAllCustomQueryHooks({
|
|
242
|
+
operations: [sampleQueryOperation],
|
|
243
|
+
typeRegistry: emptyTypeRegistry,
|
|
244
|
+
reactQueryEnabled: false,
|
|
245
|
+
});
|
|
246
|
+
expect(results.length).toBe(1);
|
|
247
|
+
expect(results[0].content).not.toContain('@tanstack/react-query');
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
// ============================================================================
|
|
252
|
+
// Tests - Custom Mutation Generators with reactQueryEnabled: false
|
|
253
|
+
// ============================================================================
|
|
254
|
+
describe('Custom mutation generators with reactQueryEnabled: false', () => {
|
|
255
|
+
describe('generateCustomMutationHook', () => {
|
|
256
|
+
it('should return null when disabled', () => {
|
|
257
|
+
const result = generateCustomMutationHook({
|
|
258
|
+
operation: sampleMutationOperation,
|
|
259
|
+
typeRegistry: emptyTypeRegistry,
|
|
260
|
+
reactQueryEnabled: false,
|
|
261
|
+
});
|
|
262
|
+
expect(result).toBeNull();
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
describe('generateAllCustomMutationHooks', () => {
|
|
266
|
+
it('should return empty array when disabled', () => {
|
|
267
|
+
const results = generateAllCustomMutationHooks({
|
|
268
|
+
operations: [sampleMutationOperation],
|
|
269
|
+
typeRegistry: emptyTypeRegistry,
|
|
270
|
+
reactQueryEnabled: false,
|
|
271
|
+
});
|
|
272
|
+
expect(results).toEqual([]);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
// ============================================================================
|
|
277
|
+
// Tests - Custom Mutation Generators with reactQueryEnabled: true (default)
|
|
278
|
+
// ============================================================================
|
|
279
|
+
describe('Custom mutation generators with reactQueryEnabled: true (default)', () => {
|
|
280
|
+
describe('generateCustomMutationHook', () => {
|
|
281
|
+
it('should return mutation file by default', () => {
|
|
282
|
+
const result = generateCustomMutationHook({
|
|
283
|
+
operation: sampleMutationOperation,
|
|
284
|
+
typeRegistry: emptyTypeRegistry,
|
|
285
|
+
});
|
|
286
|
+
expect(result).not.toBeNull();
|
|
287
|
+
expect(result.content).toContain('useMutation');
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
@@ -21,18 +21,24 @@ 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
|
+
reactQueryEnabled?: boolean;
|
|
24
26
|
}
|
|
25
27
|
/**
|
|
26
28
|
* Generate a custom mutation hook file
|
|
29
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
27
30
|
*/
|
|
28
|
-
export declare function generateCustomMutationHook(options: GenerateCustomMutationHookOptions): GeneratedCustomMutationFile;
|
|
31
|
+
export declare function generateCustomMutationHook(options: GenerateCustomMutationHookOptions): GeneratedCustomMutationFile | null;
|
|
29
32
|
export interface GenerateAllCustomMutationHooksOptions {
|
|
30
33
|
operations: CleanOperation[];
|
|
31
34
|
typeRegistry: TypeRegistry;
|
|
32
35
|
maxDepth?: number;
|
|
33
36
|
skipQueryField?: boolean;
|
|
37
|
+
/** Whether to generate React Query hooks (default: true for backwards compatibility) */
|
|
38
|
+
reactQueryEnabled?: boolean;
|
|
34
39
|
}
|
|
35
40
|
/**
|
|
36
41
|
* Generate all custom mutation hook files
|
|
42
|
+
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
37
43
|
*/
|
|
38
44
|
export declare function generateAllCustomMutationHooks(options: GenerateAllCustomMutationHooksOptions): GeneratedCustomMutationFile[];
|
|
@@ -3,9 +3,14 @@ import { buildCustomMutationString } from './schema-gql-ast';
|
|
|
3
3
|
import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, } from './type-resolver';
|
|
4
4
|
/**
|
|
5
5
|
* Generate a custom mutation hook file
|
|
6
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
6
7
|
*/
|
|
7
8
|
export function generateCustomMutationHook(options) {
|
|
8
|
-
const { operation } = options;
|
|
9
|
+
const { operation, reactQueryEnabled = true } = options;
|
|
10
|
+
// Mutations require React Query - skip generation when disabled
|
|
11
|
+
if (!reactQueryEnabled) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
9
14
|
try {
|
|
10
15
|
return generateCustomMutationHookInternal(options);
|
|
11
16
|
}
|
|
@@ -131,9 +136,10 @@ function generateHookBody(operation, documentConstName, variablesTypeName, resul
|
|
|
131
136
|
}
|
|
132
137
|
/**
|
|
133
138
|
* Generate all custom mutation hook files
|
|
139
|
+
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
134
140
|
*/
|
|
135
141
|
export function generateAllCustomMutationHooks(options) {
|
|
136
|
-
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
142
|
+
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true } = options;
|
|
137
143
|
return operations
|
|
138
144
|
.filter((op) => op.kind === 'mutation')
|
|
139
145
|
.map((operation) => generateCustomMutationHook({
|
|
@@ -141,5 +147,7 @@ export function generateAllCustomMutationHooks(options) {
|
|
|
141
147
|
typeRegistry,
|
|
142
148
|
maxDepth,
|
|
143
149
|
skipQueryField,
|
|
144
|
-
|
|
150
|
+
reactQueryEnabled,
|
|
151
|
+
}))
|
|
152
|
+
.filter((result) => result !== null);
|
|
145
153
|
}
|
|
@@ -21,6 +21,8 @@ export interface GenerateCustomQueryHookOptions {
|
|
|
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
|
+
reactQueryEnabled?: boolean;
|
|
24
26
|
}
|
|
25
27
|
/**
|
|
26
28
|
* Generate a custom query hook file
|
|
@@ -31,6 +33,8 @@ export interface GenerateAllCustomQueryHooksOptions {
|
|
|
31
33
|
typeRegistry: TypeRegistry;
|
|
32
34
|
maxDepth?: number;
|
|
33
35
|
skipQueryField?: boolean;
|
|
36
|
+
/** Whether to generate React Query hooks (default: true for backwards compatibility) */
|
|
37
|
+
reactQueryEnabled?: boolean;
|
|
34
38
|
}
|
|
35
39
|
/**
|
|
36
40
|
* Generate all custom query hook files
|
|
@@ -6,7 +6,7 @@ import { ucFirst } from './utils';
|
|
|
6
6
|
* Generate a custom query hook file
|
|
7
7
|
*/
|
|
8
8
|
export function generateCustomQueryHook(options) {
|
|
9
|
-
const { operation, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
9
|
+
const { operation, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true } = options;
|
|
10
10
|
const project = createProject();
|
|
11
11
|
const hookName = getOperationHookName(operation.name, 'query');
|
|
12
12
|
const fileName = getOperationFileName(operation.name, 'query');
|
|
@@ -23,20 +23,25 @@ export function generateCustomQueryHook(options) {
|
|
|
23
23
|
});
|
|
24
24
|
const sourceFile = createSourceFile(project, fileName);
|
|
25
25
|
// Add file header
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
const headerText = reactQueryEnabled
|
|
27
|
+
? `Custom query hook for ${operation.name}`
|
|
28
|
+
: `Custom query functions for ${operation.name}`;
|
|
29
|
+
sourceFile.insertText(0, createFileHeader(headerText) + '\n\n');
|
|
30
|
+
// Add imports - conditionally include React Query imports
|
|
31
|
+
const imports = [];
|
|
32
|
+
if (reactQueryEnabled) {
|
|
33
|
+
imports.push(createImport({
|
|
30
34
|
moduleSpecifier: '@tanstack/react-query',
|
|
31
35
|
namedImports: ['useQuery'],
|
|
32
36
|
typeOnlyNamedImports: ['UseQueryOptions', 'QueryClient'],
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
imports.push(createImport({
|
|
40
|
+
moduleSpecifier: '../client',
|
|
41
|
+
namedImports: ['execute'],
|
|
42
|
+
typeOnlyNamedImports: ['ExecuteOptions'],
|
|
43
|
+
}));
|
|
44
|
+
sourceFile.addImportDeclarations(imports);
|
|
40
45
|
// Add query document constant
|
|
41
46
|
sourceFile.addVariableStatement(createConst(documentConstName, '`\n' + queryDocument + '`', {
|
|
42
47
|
docs: ['GraphQL query document'],
|
|
@@ -62,17 +67,19 @@ export function generateCustomQueryHook(options) {
|
|
|
62
67
|
docs: ['Query key factory for caching'],
|
|
63
68
|
}));
|
|
64
69
|
}
|
|
65
|
-
// Generate hook function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
// Generate hook function (only if React Query is enabled)
|
|
71
|
+
if (reactQueryEnabled) {
|
|
72
|
+
const hookParams = generateHookParameters(operation, variablesTypeName, resultTypeName);
|
|
73
|
+
const hookBody = generateHookBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
|
|
74
|
+
const hookDoc = generateHookDoc(operation, hookName);
|
|
75
|
+
sourceFile.addFunction({
|
|
76
|
+
name: hookName,
|
|
77
|
+
isExported: true,
|
|
78
|
+
parameters: hookParams,
|
|
79
|
+
statements: hookBody,
|
|
80
|
+
docs: [{ description: hookDoc }],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
76
83
|
// Add standalone functions section
|
|
77
84
|
sourceFile.addStatements('\n// ============================================================================');
|
|
78
85
|
sourceFile.addStatements('// Standalone Functions (non-React)');
|
|
@@ -91,20 +98,22 @@ export function generateCustomQueryHook(options) {
|
|
|
91
98
|
statements: fetchBody,
|
|
92
99
|
docs: [{ description: fetchDoc }],
|
|
93
100
|
});
|
|
94
|
-
// Generate prefetch function
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
101
|
+
// Generate prefetch function (only if React Query is enabled)
|
|
102
|
+
if (reactQueryEnabled) {
|
|
103
|
+
const prefetchFnName = `prefetch${ucFirst(operation.name)}Query`;
|
|
104
|
+
const prefetchParams = generatePrefetchParameters(operation, variablesTypeName);
|
|
105
|
+
const prefetchBody = generatePrefetchBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
|
|
106
|
+
const prefetchDoc = generatePrefetchDoc(operation, prefetchFnName);
|
|
107
|
+
sourceFile.addFunction({
|
|
108
|
+
name: prefetchFnName,
|
|
109
|
+
isExported: true,
|
|
110
|
+
isAsync: true,
|
|
111
|
+
parameters: prefetchParams,
|
|
112
|
+
returnType: 'Promise<void>',
|
|
113
|
+
statements: prefetchBody,
|
|
114
|
+
docs: [{ description: prefetchDoc }],
|
|
115
|
+
});
|
|
116
|
+
}
|
|
108
117
|
return {
|
|
109
118
|
fileName,
|
|
110
119
|
content: getFormattedOutput(sourceFile),
|
|
@@ -342,7 +351,7 @@ await ${fnName}(queryClient);
|
|
|
342
351
|
* Generate all custom query hook files
|
|
343
352
|
*/
|
|
344
353
|
export function generateAllCustomQueryHooks(options) {
|
|
345
|
-
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
354
|
+
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true } = options;
|
|
346
355
|
return operations
|
|
347
356
|
.filter((op) => op.kind === 'query')
|
|
348
357
|
.map((operation) => generateCustomQueryHook({
|
|
@@ -350,5 +359,6 @@ export function generateAllCustomQueryHooks(options) {
|
|
|
350
359
|
typeRegistry,
|
|
351
360
|
maxDepth,
|
|
352
361
|
skipQueryField,
|
|
362
|
+
reactQueryEnabled,
|
|
353
363
|
}));
|
|
354
364
|
}
|
package/esm/cli/codegen/index.js
CHANGED
|
@@ -23,6 +23,7 @@ export function generate(options) {
|
|
|
23
23
|
// Extract codegen options
|
|
24
24
|
const maxDepth = config.codegen.maxFieldDepth;
|
|
25
25
|
const skipQueryField = config.codegen.skipQueryField;
|
|
26
|
+
const reactQueryEnabled = config.reactQuery.enabled;
|
|
26
27
|
// 1. Generate client.ts
|
|
27
28
|
files.push({
|
|
28
29
|
path: 'client.ts',
|
|
@@ -34,7 +35,7 @@ export function generate(options) {
|
|
|
34
35
|
content: generateTypesFile(tables),
|
|
35
36
|
});
|
|
36
37
|
// 3. Generate table-based query hooks (queries/*.ts)
|
|
37
|
-
const queryHooks = generateAllQueryHooks(tables);
|
|
38
|
+
const queryHooks = generateAllQueryHooks(tables, { reactQueryEnabled });
|
|
38
39
|
for (const hook of queryHooks) {
|
|
39
40
|
files.push({
|
|
40
41
|
path: `queries/${hook.fileName}`,
|
|
@@ -49,6 +50,7 @@ export function generate(options) {
|
|
|
49
50
|
typeRegistry: customOperations.typeRegistry,
|
|
50
51
|
maxDepth,
|
|
51
52
|
skipQueryField,
|
|
53
|
+
reactQueryEnabled,
|
|
52
54
|
});
|
|
53
55
|
for (const hook of customQueryHooks) {
|
|
54
56
|
files.push({
|
|
@@ -65,7 +67,7 @@ export function generate(options) {
|
|
|
65
67
|
: generateQueriesBarrel(tables),
|
|
66
68
|
});
|
|
67
69
|
// 6. Generate table-based mutation hooks (mutations/*.ts)
|
|
68
|
-
const mutationHooks = generateAllMutationHooks(tables);
|
|
70
|
+
const mutationHooks = generateAllMutationHooks(tables, { reactQueryEnabled });
|
|
69
71
|
for (const hook of mutationHooks) {
|
|
70
72
|
files.push({
|
|
71
73
|
path: `mutations/${hook.fileName}`,
|
|
@@ -80,6 +82,7 @@ export function generate(options) {
|
|
|
80
82
|
typeRegistry: customOperations.typeRegistry,
|
|
81
83
|
maxDepth,
|
|
82
84
|
skipQueryField,
|
|
85
|
+
reactQueryEnabled,
|
|
83
86
|
});
|
|
84
87
|
for (const hook of customMutationHooks) {
|
|
85
88
|
files.push({
|
|
@@ -12,19 +12,27 @@ export interface GeneratedMutationFile {
|
|
|
12
12
|
fileName: string;
|
|
13
13
|
content: string;
|
|
14
14
|
}
|
|
15
|
+
export interface MutationGeneratorOptions {
|
|
16
|
+
/** Whether to generate React Query hooks (default: true for backwards compatibility) */
|
|
17
|
+
reactQueryEnabled?: boolean;
|
|
18
|
+
}
|
|
15
19
|
/**
|
|
16
20
|
* Generate create mutation hook file content using AST
|
|
21
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
17
22
|
*/
|
|
18
|
-
export declare function generateCreateMutationHook(table: CleanTable): GeneratedMutationFile;
|
|
23
|
+
export declare function generateCreateMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
|
|
19
24
|
/**
|
|
20
25
|
* Generate update mutation hook file content using AST
|
|
26
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
21
27
|
*/
|
|
22
|
-
export declare function generateUpdateMutationHook(table: CleanTable): GeneratedMutationFile | null;
|
|
28
|
+
export declare function generateUpdateMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
|
|
23
29
|
/**
|
|
24
30
|
* Generate delete mutation hook file content using AST
|
|
31
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
25
32
|
*/
|
|
26
|
-
export declare function generateDeleteMutationHook(table: CleanTable): GeneratedMutationFile | null;
|
|
33
|
+
export declare function generateDeleteMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
|
|
27
34
|
/**
|
|
28
35
|
* Generate all mutation hook files for all tables
|
|
36
|
+
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
29
37
|
*/
|
|
30
|
-
export declare function generateAllMutationHooks(tables: CleanTable[]): GeneratedMutationFile[];
|
|
38
|
+
export declare function generateAllMutationHooks(tables: CleanTable[], options?: MutationGeneratorOptions): GeneratedMutationFile[];
|
|
@@ -6,8 +6,14 @@ import { getTableNames, getCreateMutationHookName, getUpdateMutationHookName, ge
|
|
|
6
6
|
// ============================================================================
|
|
7
7
|
/**
|
|
8
8
|
* Generate create mutation hook file content using AST
|
|
9
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
9
10
|
*/
|
|
10
|
-
export function generateCreateMutationHook(table) {
|
|
11
|
+
export function generateCreateMutationHook(table, options = {}) {
|
|
12
|
+
const { reactQueryEnabled = true } = options;
|
|
13
|
+
// Mutations require React Query - skip generation when disabled
|
|
14
|
+
if (!reactQueryEnabled) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
11
17
|
const project = createProject();
|
|
12
18
|
const { typeName, singularName } = getTableNames(table);
|
|
13
19
|
const hookName = getCreateMutationHookName(table);
|
|
@@ -137,8 +143,14 @@ mutate({
|
|
|
137
143
|
// ============================================================================
|
|
138
144
|
/**
|
|
139
145
|
* Generate update mutation hook file content using AST
|
|
146
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
140
147
|
*/
|
|
141
|
-
export function generateUpdateMutationHook(table) {
|
|
148
|
+
export function generateUpdateMutationHook(table, options = {}) {
|
|
149
|
+
const { reactQueryEnabled = true } = options;
|
|
150
|
+
// Mutations require React Query - skip generation when disabled
|
|
151
|
+
if (!reactQueryEnabled) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
142
154
|
// Check if update mutation exists
|
|
143
155
|
if (table.query?.update === null) {
|
|
144
156
|
return null;
|
|
@@ -272,8 +284,14 @@ mutate({
|
|
|
272
284
|
// ============================================================================
|
|
273
285
|
/**
|
|
274
286
|
* Generate delete mutation hook file content using AST
|
|
287
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
275
288
|
*/
|
|
276
|
-
export function generateDeleteMutationHook(table) {
|
|
289
|
+
export function generateDeleteMutationHook(table, options = {}) {
|
|
290
|
+
const { reactQueryEnabled = true } = options;
|
|
291
|
+
// Mutations require React Query - skip generation when disabled
|
|
292
|
+
if (!reactQueryEnabled) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
277
295
|
// Check if delete mutation exists
|
|
278
296
|
if (table.query?.delete === null) {
|
|
279
297
|
return null;
|
|
@@ -386,16 +404,20 @@ mutate({
|
|
|
386
404
|
// ============================================================================
|
|
387
405
|
/**
|
|
388
406
|
* Generate all mutation hook files for all tables
|
|
407
|
+
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
389
408
|
*/
|
|
390
|
-
export function generateAllMutationHooks(tables) {
|
|
409
|
+
export function generateAllMutationHooks(tables, options = {}) {
|
|
391
410
|
const files = [];
|
|
392
411
|
for (const table of tables) {
|
|
393
|
-
|
|
394
|
-
|
|
412
|
+
const createHook = generateCreateMutationHook(table, options);
|
|
413
|
+
if (createHook) {
|
|
414
|
+
files.push(createHook);
|
|
415
|
+
}
|
|
416
|
+
const updateHook = generateUpdateMutationHook(table, options);
|
|
395
417
|
if (updateHook) {
|
|
396
418
|
files.push(updateHook);
|
|
397
419
|
}
|
|
398
|
-
const deleteHook = generateDeleteMutationHook(table);
|
|
420
|
+
const deleteHook = generateDeleteMutationHook(table, options);
|
|
399
421
|
if (deleteHook) {
|
|
400
422
|
files.push(deleteHook);
|
|
401
423
|
}
|