@constructive-io/graphql-codegen 2.20.0 → 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 +3 -3
- package/types/config.d.ts +16 -1
- package/types/config.js +6 -0
package/README.md
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
# @constructive-io/graphql-
|
|
1
|
+
# @constructive-io/graphql-codegen
|
|
2
|
+
|
|
3
|
+
<p align="center" width="100%">
|
|
4
|
+
<img height="250" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center" width="100%">
|
|
8
|
+
<a href="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml">
|
|
9
|
+
<img height="20" src="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml/badge.svg" />
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://github.com/constructive-io/constructive/blob/main/LICENSE"><img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/@constructive-io/graphql-codegen"><img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/constructive?filename=graphql%2Fcodegen%2Fpackage.json"/></a>
|
|
13
|
+
</p>
|
|
2
14
|
|
|
3
15
|
CLI-based GraphQL SDK generator for PostGraphile endpoints. Generate type-safe React Query hooks or a Prisma-like ORM client from your GraphQL schema.
|
|
4
16
|
|
|
@@ -39,7 +51,7 @@ CLI-based GraphQL SDK generator for PostGraphile endpoints. Generate type-safe R
|
|
|
39
51
|
## Installation
|
|
40
52
|
|
|
41
53
|
```bash
|
|
42
|
-
pnpm add @constructive-io/graphql-
|
|
54
|
+
pnpm add @constructive-io/graphql-codegen
|
|
43
55
|
```
|
|
44
56
|
|
|
45
57
|
## Quick Start
|
|
@@ -53,7 +65,7 @@ npx graphql-sdk init
|
|
|
53
65
|
Creates a `graphql-sdk.config.ts` file:
|
|
54
66
|
|
|
55
67
|
```typescript
|
|
56
|
-
import { defineConfig } from '@constructive-io/graphql-
|
|
68
|
+
import { defineConfig } from '@constructive-io/graphql-codegen';
|
|
57
69
|
|
|
58
70
|
export default defineConfig({
|
|
59
71
|
endpoint: 'https://api.example.com/graphql',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Tests for React Query optional flag in code generators
|
|
5
|
+
*
|
|
6
|
+
* Verifies that when reactQueryEnabled is false:
|
|
7
|
+
* - Query generators skip React Query imports and hooks
|
|
8
|
+
* - Mutation generators return null (since they require React Query)
|
|
9
|
+
* - Standalone fetch functions are still generated for queries
|
|
10
|
+
*/
|
|
11
|
+
const queries_1 = require("../../cli/codegen/queries");
|
|
12
|
+
const mutations_1 = require("../../cli/codegen/mutations");
|
|
13
|
+
const custom_queries_1 = require("../../cli/codegen/custom-queries");
|
|
14
|
+
const custom_mutations_1 = require("../../cli/codegen/custom-mutations");
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Test Fixtures
|
|
17
|
+
// ============================================================================
|
|
18
|
+
const fieldTypes = {
|
|
19
|
+
uuid: { gqlType: 'UUID', isArray: false },
|
|
20
|
+
string: { gqlType: 'String', isArray: false },
|
|
21
|
+
int: { gqlType: 'Int', isArray: false },
|
|
22
|
+
datetime: { gqlType: 'Datetime', isArray: false },
|
|
23
|
+
};
|
|
24
|
+
const emptyRelations = {
|
|
25
|
+
belongsTo: [],
|
|
26
|
+
hasOne: [],
|
|
27
|
+
hasMany: [],
|
|
28
|
+
manyToMany: [],
|
|
29
|
+
};
|
|
30
|
+
function createTable(partial) {
|
|
31
|
+
return {
|
|
32
|
+
name: partial.name,
|
|
33
|
+
fields: partial.fields ?? [],
|
|
34
|
+
relations: partial.relations ?? emptyRelations,
|
|
35
|
+
query: partial.query,
|
|
36
|
+
inflection: partial.inflection,
|
|
37
|
+
constraints: partial.constraints,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const userTable = createTable({
|
|
41
|
+
name: 'User',
|
|
42
|
+
fields: [
|
|
43
|
+
{ name: 'id', type: fieldTypes.uuid },
|
|
44
|
+
{ name: 'email', type: fieldTypes.string },
|
|
45
|
+
{ name: 'name', type: fieldTypes.string },
|
|
46
|
+
{ name: 'createdAt', type: fieldTypes.datetime },
|
|
47
|
+
],
|
|
48
|
+
query: {
|
|
49
|
+
all: 'users',
|
|
50
|
+
one: 'user',
|
|
51
|
+
create: 'createUser',
|
|
52
|
+
update: 'updateUser',
|
|
53
|
+
delete: 'deleteUser',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
function createTypeRef(kind, name, ofType) {
|
|
57
|
+
return { kind, name, ofType };
|
|
58
|
+
}
|
|
59
|
+
const sampleQueryOperation = {
|
|
60
|
+
name: 'currentUser',
|
|
61
|
+
kind: 'query',
|
|
62
|
+
args: [],
|
|
63
|
+
returnType: createTypeRef('OBJECT', 'User'),
|
|
64
|
+
description: 'Get the current authenticated user',
|
|
65
|
+
};
|
|
66
|
+
const sampleMutationOperation = {
|
|
67
|
+
name: 'login',
|
|
68
|
+
kind: 'mutation',
|
|
69
|
+
args: [
|
|
70
|
+
{ name: 'email', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) },
|
|
71
|
+
{ name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) },
|
|
72
|
+
],
|
|
73
|
+
returnType: createTypeRef('OBJECT', 'LoginPayload'),
|
|
74
|
+
description: 'Authenticate user',
|
|
75
|
+
};
|
|
76
|
+
const emptyTypeRegistry = new Map();
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// Tests - Query Generators with reactQueryEnabled: false
|
|
79
|
+
// ============================================================================
|
|
80
|
+
describe('Query generators with reactQueryEnabled: false', () => {
|
|
81
|
+
describe('generateListQueryHook', () => {
|
|
82
|
+
it('should not include React Query imports when disabled', () => {
|
|
83
|
+
const result = (0, queries_1.generateListQueryHook)(userTable, { reactQueryEnabled: false });
|
|
84
|
+
expect(result.content).not.toContain('@tanstack/react-query');
|
|
85
|
+
expect(result.content).not.toContain('useQuery');
|
|
86
|
+
expect(result.content).not.toContain('UseQueryOptions');
|
|
87
|
+
});
|
|
88
|
+
it('should not include useXxxQuery hook when disabled', () => {
|
|
89
|
+
const result = (0, queries_1.generateListQueryHook)(userTable, { reactQueryEnabled: false });
|
|
90
|
+
expect(result.content).not.toContain('export function useUsersQuery');
|
|
91
|
+
});
|
|
92
|
+
it('should not include prefetch function when disabled', () => {
|
|
93
|
+
const result = (0, queries_1.generateListQueryHook)(userTable, { reactQueryEnabled: false });
|
|
94
|
+
expect(result.content).not.toContain('export async function prefetchUsersQuery');
|
|
95
|
+
});
|
|
96
|
+
it('should still include standalone fetch function when disabled', () => {
|
|
97
|
+
const result = (0, queries_1.generateListQueryHook)(userTable, { reactQueryEnabled: false });
|
|
98
|
+
expect(result.content).toContain('export async function fetchUsersQuery');
|
|
99
|
+
});
|
|
100
|
+
it('should still include GraphQL document when disabled', () => {
|
|
101
|
+
const result = (0, queries_1.generateListQueryHook)(userTable, { reactQueryEnabled: false });
|
|
102
|
+
expect(result.content).toContain('usersQueryDocument');
|
|
103
|
+
});
|
|
104
|
+
it('should still include query key factory when disabled', () => {
|
|
105
|
+
const result = (0, queries_1.generateListQueryHook)(userTable, { reactQueryEnabled: false });
|
|
106
|
+
expect(result.content).toContain('usersQueryKey');
|
|
107
|
+
});
|
|
108
|
+
it('should update file header when disabled', () => {
|
|
109
|
+
const result = (0, queries_1.generateListQueryHook)(userTable, { reactQueryEnabled: false });
|
|
110
|
+
expect(result.content).toContain('List query functions for User');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('generateSingleQueryHook', () => {
|
|
114
|
+
it('should not include React Query imports when disabled', () => {
|
|
115
|
+
const result = (0, queries_1.generateSingleQueryHook)(userTable, { reactQueryEnabled: false });
|
|
116
|
+
expect(result.content).not.toContain('@tanstack/react-query');
|
|
117
|
+
expect(result.content).not.toContain('useQuery');
|
|
118
|
+
});
|
|
119
|
+
it('should not include useXxxQuery hook when disabled', () => {
|
|
120
|
+
const result = (0, queries_1.generateSingleQueryHook)(userTable, { reactQueryEnabled: false });
|
|
121
|
+
expect(result.content).not.toContain('export function useUserQuery');
|
|
122
|
+
});
|
|
123
|
+
it('should still include standalone fetch function when disabled', () => {
|
|
124
|
+
const result = (0, queries_1.generateSingleQueryHook)(userTable, { reactQueryEnabled: false });
|
|
125
|
+
expect(result.content).toContain('export async function fetchUserQuery');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('generateAllQueryHooks', () => {
|
|
129
|
+
it('should generate files without React Query when disabled', () => {
|
|
130
|
+
const results = (0, queries_1.generateAllQueryHooks)([userTable], { reactQueryEnabled: false });
|
|
131
|
+
expect(results.length).toBe(2); // list + single
|
|
132
|
+
for (const result of results) {
|
|
133
|
+
expect(result.content).not.toContain('@tanstack/react-query');
|
|
134
|
+
expect(result.content).not.toContain('useQuery');
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
// ============================================================================
|
|
140
|
+
// Tests - Query Generators with reactQueryEnabled: true (default)
|
|
141
|
+
// ============================================================================
|
|
142
|
+
describe('Query generators with reactQueryEnabled: true (default)', () => {
|
|
143
|
+
describe('generateListQueryHook', () => {
|
|
144
|
+
it('should include React Query imports by default', () => {
|
|
145
|
+
const result = (0, queries_1.generateListQueryHook)(userTable);
|
|
146
|
+
expect(result.content).toContain('@tanstack/react-query');
|
|
147
|
+
expect(result.content).toContain('useQuery');
|
|
148
|
+
});
|
|
149
|
+
it('should include useXxxQuery hook by default', () => {
|
|
150
|
+
const result = (0, queries_1.generateListQueryHook)(userTable);
|
|
151
|
+
expect(result.content).toContain('export function useUsersQuery');
|
|
152
|
+
});
|
|
153
|
+
it('should include prefetch function by default', () => {
|
|
154
|
+
const result = (0, queries_1.generateListQueryHook)(userTable);
|
|
155
|
+
expect(result.content).toContain('export async function prefetchUsersQuery');
|
|
156
|
+
});
|
|
157
|
+
it('should include standalone fetch function by default', () => {
|
|
158
|
+
const result = (0, queries_1.generateListQueryHook)(userTable);
|
|
159
|
+
expect(result.content).toContain('export async function fetchUsersQuery');
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
// ============================================================================
|
|
164
|
+
// Tests - Mutation Generators with reactQueryEnabled: false
|
|
165
|
+
// ============================================================================
|
|
166
|
+
describe('Mutation generators with reactQueryEnabled: false', () => {
|
|
167
|
+
describe('generateCreateMutationHook', () => {
|
|
168
|
+
it('should return null when disabled', () => {
|
|
169
|
+
const result = (0, mutations_1.generateCreateMutationHook)(userTable, { reactQueryEnabled: false });
|
|
170
|
+
expect(result).toBeNull();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
describe('generateUpdateMutationHook', () => {
|
|
174
|
+
it('should return null when disabled', () => {
|
|
175
|
+
const result = (0, mutations_1.generateUpdateMutationHook)(userTable, { reactQueryEnabled: false });
|
|
176
|
+
expect(result).toBeNull();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
describe('generateDeleteMutationHook', () => {
|
|
180
|
+
it('should return null when disabled', () => {
|
|
181
|
+
const result = (0, mutations_1.generateDeleteMutationHook)(userTable, { reactQueryEnabled: false });
|
|
182
|
+
expect(result).toBeNull();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
describe('generateAllMutationHooks', () => {
|
|
186
|
+
it('should return empty array when disabled', () => {
|
|
187
|
+
const results = (0, mutations_1.generateAllMutationHooks)([userTable], { reactQueryEnabled: false });
|
|
188
|
+
expect(results).toEqual([]);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
// ============================================================================
|
|
193
|
+
// Tests - Mutation Generators with reactQueryEnabled: true (default)
|
|
194
|
+
// ============================================================================
|
|
195
|
+
describe('Mutation generators with reactQueryEnabled: true (default)', () => {
|
|
196
|
+
describe('generateCreateMutationHook', () => {
|
|
197
|
+
it('should return mutation file by default', () => {
|
|
198
|
+
const result = (0, mutations_1.generateCreateMutationHook)(userTable);
|
|
199
|
+
expect(result).not.toBeNull();
|
|
200
|
+
expect(result.content).toContain('useMutation');
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
describe('generateAllMutationHooks', () => {
|
|
204
|
+
it('should return mutation files by default', () => {
|
|
205
|
+
const results = (0, mutations_1.generateAllMutationHooks)([userTable]);
|
|
206
|
+
expect(results.length).toBeGreaterThan(0);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
// ============================================================================
|
|
211
|
+
// Tests - Custom Query Generators with reactQueryEnabled: false
|
|
212
|
+
// ============================================================================
|
|
213
|
+
describe('Custom query generators with reactQueryEnabled: false', () => {
|
|
214
|
+
describe('generateCustomQueryHook', () => {
|
|
215
|
+
it('should not include React Query imports when disabled', () => {
|
|
216
|
+
const result = (0, custom_queries_1.generateCustomQueryHook)({
|
|
217
|
+
operation: sampleQueryOperation,
|
|
218
|
+
typeRegistry: emptyTypeRegistry,
|
|
219
|
+
reactQueryEnabled: false,
|
|
220
|
+
});
|
|
221
|
+
expect(result.content).not.toContain('@tanstack/react-query');
|
|
222
|
+
expect(result.content).not.toContain('useQuery');
|
|
223
|
+
});
|
|
224
|
+
it('should not include useXxxQuery hook when disabled', () => {
|
|
225
|
+
const result = (0, custom_queries_1.generateCustomQueryHook)({
|
|
226
|
+
operation: sampleQueryOperation,
|
|
227
|
+
typeRegistry: emptyTypeRegistry,
|
|
228
|
+
reactQueryEnabled: false,
|
|
229
|
+
});
|
|
230
|
+
expect(result.content).not.toContain('export function useCurrentUserQuery');
|
|
231
|
+
});
|
|
232
|
+
it('should still include standalone fetch function when disabled', () => {
|
|
233
|
+
const result = (0, custom_queries_1.generateCustomQueryHook)({
|
|
234
|
+
operation: sampleQueryOperation,
|
|
235
|
+
typeRegistry: emptyTypeRegistry,
|
|
236
|
+
reactQueryEnabled: false,
|
|
237
|
+
});
|
|
238
|
+
expect(result.content).toContain('export async function fetchCurrentUserQuery');
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
describe('generateAllCustomQueryHooks', () => {
|
|
242
|
+
it('should generate files without React Query when disabled', () => {
|
|
243
|
+
const results = (0, custom_queries_1.generateAllCustomQueryHooks)({
|
|
244
|
+
operations: [sampleQueryOperation],
|
|
245
|
+
typeRegistry: emptyTypeRegistry,
|
|
246
|
+
reactQueryEnabled: false,
|
|
247
|
+
});
|
|
248
|
+
expect(results.length).toBe(1);
|
|
249
|
+
expect(results[0].content).not.toContain('@tanstack/react-query');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
// ============================================================================
|
|
254
|
+
// Tests - Custom Mutation Generators with reactQueryEnabled: false
|
|
255
|
+
// ============================================================================
|
|
256
|
+
describe('Custom mutation generators with reactQueryEnabled: false', () => {
|
|
257
|
+
describe('generateCustomMutationHook', () => {
|
|
258
|
+
it('should return null when disabled', () => {
|
|
259
|
+
const result = (0, custom_mutations_1.generateCustomMutationHook)({
|
|
260
|
+
operation: sampleMutationOperation,
|
|
261
|
+
typeRegistry: emptyTypeRegistry,
|
|
262
|
+
reactQueryEnabled: false,
|
|
263
|
+
});
|
|
264
|
+
expect(result).toBeNull();
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
describe('generateAllCustomMutationHooks', () => {
|
|
268
|
+
it('should return empty array when disabled', () => {
|
|
269
|
+
const results = (0, custom_mutations_1.generateAllCustomMutationHooks)({
|
|
270
|
+
operations: [sampleMutationOperation],
|
|
271
|
+
typeRegistry: emptyTypeRegistry,
|
|
272
|
+
reactQueryEnabled: false,
|
|
273
|
+
});
|
|
274
|
+
expect(results).toEqual([]);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
// ============================================================================
|
|
279
|
+
// Tests - Custom Mutation Generators with reactQueryEnabled: true (default)
|
|
280
|
+
// ============================================================================
|
|
281
|
+
describe('Custom mutation generators with reactQueryEnabled: true (default)', () => {
|
|
282
|
+
describe('generateCustomMutationHook', () => {
|
|
283
|
+
it('should return mutation file by default', () => {
|
|
284
|
+
const result = (0, custom_mutations_1.generateCustomMutationHook)({
|
|
285
|
+
operation: sampleMutationOperation,
|
|
286
|
+
typeRegistry: emptyTypeRegistry,
|
|
287
|
+
});
|
|
288
|
+
expect(result).not.toBeNull();
|
|
289
|
+
expect(result.content).toContain('useMutation');
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
});
|
|
@@ -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[];
|
|
@@ -7,9 +7,14 @@ const schema_gql_ast_1 = require("./schema-gql-ast");
|
|
|
7
7
|
const type_resolver_1 = require("./type-resolver");
|
|
8
8
|
/**
|
|
9
9
|
* Generate a custom mutation hook file
|
|
10
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
10
11
|
*/
|
|
11
12
|
function generateCustomMutationHook(options) {
|
|
12
|
-
const { operation } = options;
|
|
13
|
+
const { operation, reactQueryEnabled = true } = options;
|
|
14
|
+
// Mutations require React Query - skip generation when disabled
|
|
15
|
+
if (!reactQueryEnabled) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
13
18
|
try {
|
|
14
19
|
return generateCustomMutationHookInternal(options);
|
|
15
20
|
}
|
|
@@ -135,9 +140,10 @@ function generateHookBody(operation, documentConstName, variablesTypeName, resul
|
|
|
135
140
|
}
|
|
136
141
|
/**
|
|
137
142
|
* Generate all custom mutation hook files
|
|
143
|
+
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
138
144
|
*/
|
|
139
145
|
function generateAllCustomMutationHooks(options) {
|
|
140
|
-
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
146
|
+
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true } = options;
|
|
141
147
|
return operations
|
|
142
148
|
.filter((op) => op.kind === 'mutation')
|
|
143
149
|
.map((operation) => generateCustomMutationHook({
|
|
@@ -145,5 +151,7 @@ function generateAllCustomMutationHooks(options) {
|
|
|
145
151
|
typeRegistry,
|
|
146
152
|
maxDepth,
|
|
147
153
|
skipQueryField,
|
|
148
|
-
|
|
154
|
+
reactQueryEnabled,
|
|
155
|
+
}))
|
|
156
|
+
.filter((result) => result !== null);
|
|
149
157
|
}
|
|
@@ -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
|
|
@@ -10,7 +10,7 @@ const utils_1 = require("./utils");
|
|
|
10
10
|
* Generate a custom query hook file
|
|
11
11
|
*/
|
|
12
12
|
function generateCustomQueryHook(options) {
|
|
13
|
-
const { operation, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
13
|
+
const { operation, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true } = options;
|
|
14
14
|
const project = (0, ts_ast_1.createProject)();
|
|
15
15
|
const hookName = (0, type_resolver_1.getOperationHookName)(operation.name, 'query');
|
|
16
16
|
const fileName = (0, type_resolver_1.getOperationFileName)(operation.name, 'query');
|
|
@@ -27,20 +27,25 @@ function generateCustomQueryHook(options) {
|
|
|
27
27
|
});
|
|
28
28
|
const sourceFile = (0, ts_ast_1.createSourceFile)(project, fileName);
|
|
29
29
|
// Add file header
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const headerText = reactQueryEnabled
|
|
31
|
+
? `Custom query hook for ${operation.name}`
|
|
32
|
+
: `Custom query functions for ${operation.name}`;
|
|
33
|
+
sourceFile.insertText(0, (0, ts_ast_1.createFileHeader)(headerText) + '\n\n');
|
|
34
|
+
// Add imports - conditionally include React Query imports
|
|
35
|
+
const imports = [];
|
|
36
|
+
if (reactQueryEnabled) {
|
|
37
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
34
38
|
moduleSpecifier: '@tanstack/react-query',
|
|
35
39
|
namedImports: ['useQuery'],
|
|
36
40
|
typeOnlyNamedImports: ['UseQueryOptions', 'QueryClient'],
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
44
|
+
moduleSpecifier: '../client',
|
|
45
|
+
namedImports: ['execute'],
|
|
46
|
+
typeOnlyNamedImports: ['ExecuteOptions'],
|
|
47
|
+
}));
|
|
48
|
+
sourceFile.addImportDeclarations(imports);
|
|
44
49
|
// Add query document constant
|
|
45
50
|
sourceFile.addVariableStatement((0, ts_ast_1.createConst)(documentConstName, '`\n' + queryDocument + '`', {
|
|
46
51
|
docs: ['GraphQL query document'],
|
|
@@ -66,17 +71,19 @@ function generateCustomQueryHook(options) {
|
|
|
66
71
|
docs: ['Query key factory for caching'],
|
|
67
72
|
}));
|
|
68
73
|
}
|
|
69
|
-
// Generate hook function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
// Generate hook function (only if React Query is enabled)
|
|
75
|
+
if (reactQueryEnabled) {
|
|
76
|
+
const hookParams = generateHookParameters(operation, variablesTypeName, resultTypeName);
|
|
77
|
+
const hookBody = generateHookBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
|
|
78
|
+
const hookDoc = generateHookDoc(operation, hookName);
|
|
79
|
+
sourceFile.addFunction({
|
|
80
|
+
name: hookName,
|
|
81
|
+
isExported: true,
|
|
82
|
+
parameters: hookParams,
|
|
83
|
+
statements: hookBody,
|
|
84
|
+
docs: [{ description: hookDoc }],
|
|
85
|
+
});
|
|
86
|
+
}
|
|
80
87
|
// Add standalone functions section
|
|
81
88
|
sourceFile.addStatements('\n// ============================================================================');
|
|
82
89
|
sourceFile.addStatements('// Standalone Functions (non-React)');
|
|
@@ -95,20 +102,22 @@ function generateCustomQueryHook(options) {
|
|
|
95
102
|
statements: fetchBody,
|
|
96
103
|
docs: [{ description: fetchDoc }],
|
|
97
104
|
});
|
|
98
|
-
// Generate prefetch function
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
// Generate prefetch function (only if React Query is enabled)
|
|
106
|
+
if (reactQueryEnabled) {
|
|
107
|
+
const prefetchFnName = `prefetch${(0, utils_1.ucFirst)(operation.name)}Query`;
|
|
108
|
+
const prefetchParams = generatePrefetchParameters(operation, variablesTypeName);
|
|
109
|
+
const prefetchBody = generatePrefetchBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
|
|
110
|
+
const prefetchDoc = generatePrefetchDoc(operation, prefetchFnName);
|
|
111
|
+
sourceFile.addFunction({
|
|
112
|
+
name: prefetchFnName,
|
|
113
|
+
isExported: true,
|
|
114
|
+
isAsync: true,
|
|
115
|
+
parameters: prefetchParams,
|
|
116
|
+
returnType: 'Promise<void>',
|
|
117
|
+
statements: prefetchBody,
|
|
118
|
+
docs: [{ description: prefetchDoc }],
|
|
119
|
+
});
|
|
120
|
+
}
|
|
112
121
|
return {
|
|
113
122
|
fileName,
|
|
114
123
|
content: (0, ts_ast_1.getFormattedOutput)(sourceFile),
|
|
@@ -346,7 +355,7 @@ await ${fnName}(queryClient);
|
|
|
346
355
|
* Generate all custom query hook files
|
|
347
356
|
*/
|
|
348
357
|
function generateAllCustomQueryHooks(options) {
|
|
349
|
-
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
358
|
+
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true } = options;
|
|
350
359
|
return operations
|
|
351
360
|
.filter((op) => op.kind === 'query')
|
|
352
361
|
.map((operation) => generateCustomQueryHook({
|
|
@@ -354,5 +363,6 @@ function generateAllCustomQueryHooks(options) {
|
|
|
354
363
|
typeRegistry,
|
|
355
364
|
maxDepth,
|
|
356
365
|
skipQueryField,
|
|
366
|
+
reactQueryEnabled,
|
|
357
367
|
}));
|
|
358
368
|
}
|
package/cli/codegen/index.js
CHANGED
|
@@ -28,6 +28,7 @@ function generate(options) {
|
|
|
28
28
|
// Extract codegen options
|
|
29
29
|
const maxDepth = config.codegen.maxFieldDepth;
|
|
30
30
|
const skipQueryField = config.codegen.skipQueryField;
|
|
31
|
+
const reactQueryEnabled = config.reactQuery.enabled;
|
|
31
32
|
// 1. Generate client.ts
|
|
32
33
|
files.push({
|
|
33
34
|
path: 'client.ts',
|
|
@@ -39,7 +40,7 @@ function generate(options) {
|
|
|
39
40
|
content: (0, types_1.generateTypesFile)(tables),
|
|
40
41
|
});
|
|
41
42
|
// 3. Generate table-based query hooks (queries/*.ts)
|
|
42
|
-
const queryHooks = (0, queries_1.generateAllQueryHooks)(tables);
|
|
43
|
+
const queryHooks = (0, queries_1.generateAllQueryHooks)(tables, { reactQueryEnabled });
|
|
43
44
|
for (const hook of queryHooks) {
|
|
44
45
|
files.push({
|
|
45
46
|
path: `queries/${hook.fileName}`,
|
|
@@ -54,6 +55,7 @@ function generate(options) {
|
|
|
54
55
|
typeRegistry: customOperations.typeRegistry,
|
|
55
56
|
maxDepth,
|
|
56
57
|
skipQueryField,
|
|
58
|
+
reactQueryEnabled,
|
|
57
59
|
});
|
|
58
60
|
for (const hook of customQueryHooks) {
|
|
59
61
|
files.push({
|
|
@@ -70,7 +72,7 @@ function generate(options) {
|
|
|
70
72
|
: (0, barrel_1.generateQueriesBarrel)(tables),
|
|
71
73
|
});
|
|
72
74
|
// 6. Generate table-based mutation hooks (mutations/*.ts)
|
|
73
|
-
const mutationHooks = (0, mutations_1.generateAllMutationHooks)(tables);
|
|
75
|
+
const mutationHooks = (0, mutations_1.generateAllMutationHooks)(tables, { reactQueryEnabled });
|
|
74
76
|
for (const hook of mutationHooks) {
|
|
75
77
|
files.push({
|
|
76
78
|
path: `mutations/${hook.fileName}`,
|
|
@@ -85,6 +87,7 @@ function generate(options) {
|
|
|
85
87
|
typeRegistry: customOperations.typeRegistry,
|
|
86
88
|
maxDepth,
|
|
87
89
|
skipQueryField,
|
|
90
|
+
reactQueryEnabled,
|
|
88
91
|
});
|
|
89
92
|
for (const hook of customMutationHooks) {
|
|
90
93
|
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[];
|