@constructive-io/graphql-codegen 2.20.1 → 2.22.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/cli/codegen/barrel.d.ts +4 -1
- package/cli/codegen/barrel.js +18 -12
- package/cli/codegen/client.js +33 -0
- package/cli/codegen/custom-mutations.d.ts +11 -1
- package/cli/codegen/custom-mutations.js +49 -15
- package/cli/codegen/custom-queries.d.ts +8 -0
- package/cli/codegen/custom-queries.js +82 -47
- package/cli/codegen/gql-ast.js +9 -5
- package/cli/codegen/index.js +39 -8
- package/cli/codegen/mutations.d.ts +14 -4
- package/cli/codegen/mutations.js +114 -28
- package/cli/codegen/orm/barrel.js +4 -2
- package/cli/codegen/orm/index.js +17 -0
- package/cli/codegen/orm/input-types-generator.js +83 -29
- package/cli/codegen/orm/model-generator.js +6 -4
- package/cli/codegen/queries.d.ts +7 -3
- package/cli/codegen/queries.js +185 -158
- package/cli/codegen/scalars.d.ts +6 -4
- package/cli/codegen/scalars.js +17 -9
- package/cli/codegen/schema-types-generator.d.ts +26 -0
- package/cli/codegen/schema-types-generator.js +365 -0
- package/cli/codegen/ts-ast.d.ts +3 -1
- package/cli/codegen/ts-ast.js +2 -2
- package/cli/codegen/type-resolver.d.ts +52 -6
- package/cli/codegen/type-resolver.js +97 -19
- package/cli/codegen/types.d.ts +7 -4
- package/cli/codegen/types.js +94 -41
- package/cli/codegen/utils.d.ts +20 -2
- package/cli/codegen/utils.js +32 -7
- package/cli/commands/generate-orm.js +5 -5
- package/cli/commands/generate.d.ts +4 -1
- package/cli/commands/generate.js +27 -8
- package/cli/introspect/transform-schema.d.ts +33 -21
- package/cli/introspect/transform-schema.js +31 -21
- package/esm/cli/codegen/barrel.d.ts +4 -1
- package/esm/cli/codegen/barrel.js +18 -12
- package/esm/cli/codegen/client.js +33 -0
- package/esm/cli/codegen/custom-mutations.d.ts +11 -1
- package/esm/cli/codegen/custom-mutations.js +50 -16
- package/esm/cli/codegen/custom-queries.d.ts +8 -0
- package/esm/cli/codegen/custom-queries.js +83 -48
- package/esm/cli/codegen/gql-ast.js +10 -6
- package/esm/cli/codegen/index.js +39 -8
- package/esm/cli/codegen/mutations.d.ts +14 -4
- package/esm/cli/codegen/mutations.js +115 -29
- package/esm/cli/codegen/orm/barrel.js +4 -2
- package/esm/cli/codegen/orm/index.js +17 -0
- package/esm/cli/codegen/orm/input-types-generator.js +83 -29
- package/esm/cli/codegen/orm/model-generator.js +7 -5
- package/esm/cli/codegen/queries.d.ts +7 -3
- package/esm/cli/codegen/queries.js +186 -159
- package/esm/cli/codegen/scalars.d.ts +6 -4
- package/esm/cli/codegen/scalars.js +16 -8
- package/esm/cli/codegen/schema-types-generator.d.ts +26 -0
- package/esm/cli/codegen/schema-types-generator.js +362 -0
- package/esm/cli/codegen/ts-ast.d.ts +3 -1
- package/esm/cli/codegen/ts-ast.js +2 -2
- package/esm/cli/codegen/type-resolver.d.ts +52 -6
- package/esm/cli/codegen/type-resolver.js +97 -20
- package/esm/cli/codegen/types.d.ts +7 -4
- package/esm/cli/codegen/types.js +95 -41
- package/esm/cli/codegen/utils.d.ts +20 -2
- package/esm/cli/codegen/utils.js +31 -7
- package/esm/cli/commands/generate-orm.js +5 -5
- package/esm/cli/commands/generate.d.ts +4 -1
- package/esm/cli/commands/generate.js +27 -8
- package/esm/cli/introspect/transform-schema.d.ts +33 -21
- package/esm/cli/introspect/transform-schema.js +31 -21
- package/esm/types/config.d.ts +16 -1
- package/esm/types/config.js +6 -0
- package/esm/types/schema.d.ts +2 -0
- package/package.json +8 -6
- package/types/config.d.ts +16 -1
- package/types/config.js +6 -0
- package/types/schema.d.ts +2 -0
- package/__tests__/codegen/input-types-generator.test.d.ts +0 -1
- package/__tests__/codegen/input-types-generator.test.js +0 -635
- package/cli/codegen/filters.d.ts +0 -27
- package/cli/codegen/filters.js +0 -357
- package/cli/codegen/orm/input-types-generator.test.d.ts +0 -1
- package/cli/codegen/orm/input-types-generator.test.js +0 -75
- package/cli/codegen/orm/select-types.test.d.ts +0 -11
- package/cli/codegen/orm/select-types.test.js +0 -22
- package/cli/introspect/transform-schema.test.d.ts +0 -1
- package/cli/introspect/transform-schema.test.js +0 -67
- package/esm/__tests__/codegen/input-types-generator.test.d.ts +0 -1
- package/esm/__tests__/codegen/input-types-generator.test.js +0 -633
- package/esm/cli/codegen/filters.d.ts +0 -27
- package/esm/cli/codegen/filters.js +0 -351
- package/esm/cli/codegen/orm/input-types-generator.test.d.ts +0 -1
- package/esm/cli/codegen/orm/input-types-generator.test.js +0 -73
- package/esm/cli/codegen/orm/select-types.test.d.ts +0 -11
- package/esm/cli/codegen/orm/select-types.test.js +0 -21
- package/esm/cli/introspect/transform-schema.test.d.ts +0 -1
- package/esm/cli/introspect/transform-schema.test.js +0 -65
|
@@ -11,13 +11,15 @@ function generateModelFile(table, _useSharedTypes) {
|
|
|
11
11
|
const project = (0, ts_ast_1.createProject)();
|
|
12
12
|
const { typeName, singularName, pluralName } = (0, utils_1.getTableNames)(table);
|
|
13
13
|
const modelName = `${typeName}Model`;
|
|
14
|
-
|
|
14
|
+
// Avoid "index.ts" which clashes with barrel file
|
|
15
|
+
const baseFileName = (0, utils_1.lcFirst)(typeName);
|
|
16
|
+
const fileName = baseFileName === 'index' ? `${baseFileName}Model.ts` : `${baseFileName}.ts`;
|
|
15
17
|
const entityLower = singularName;
|
|
16
|
-
// Type names for this entity
|
|
18
|
+
// Type names for this entity - use inflection from table metadata
|
|
17
19
|
const selectTypeName = `${typeName}Select`;
|
|
18
20
|
const relationTypeName = `${typeName}WithRelations`;
|
|
19
|
-
const whereTypeName =
|
|
20
|
-
const orderByTypeName =
|
|
21
|
+
const whereTypeName = (0, utils_1.getFilterTypeName)(table);
|
|
22
|
+
const orderByTypeName = (0, utils_1.getOrderByTypeName)(table);
|
|
21
23
|
const createInputTypeName = `Create${typeName}Input`;
|
|
22
24
|
const updateInputTypeName = `Update${typeName}Input`;
|
|
23
25
|
const deleteInputTypeName = `Delete${typeName}Input`;
|
package/cli/codegen/queries.d.ts
CHANGED
|
@@ -11,15 +11,19 @@ export interface GeneratedQueryFile {
|
|
|
11
11
|
fileName: string;
|
|
12
12
|
content: string;
|
|
13
13
|
}
|
|
14
|
+
export interface QueryGeneratorOptions {
|
|
15
|
+
/** Whether to generate React Query hooks (default: true for backwards compatibility) */
|
|
16
|
+
reactQueryEnabled?: boolean;
|
|
17
|
+
}
|
|
14
18
|
/**
|
|
15
19
|
* Generate list query hook file content using AST
|
|
16
20
|
*/
|
|
17
|
-
export declare function generateListQueryHook(table: CleanTable): GeneratedQueryFile;
|
|
21
|
+
export declare function generateListQueryHook(table: CleanTable, options?: QueryGeneratorOptions): GeneratedQueryFile;
|
|
18
22
|
/**
|
|
19
23
|
* Generate single item query hook file content using AST
|
|
20
24
|
*/
|
|
21
|
-
export declare function generateSingleQueryHook(table: CleanTable): GeneratedQueryFile;
|
|
25
|
+
export declare function generateSingleQueryHook(table: CleanTable, options?: QueryGeneratorOptions): GeneratedQueryFile;
|
|
22
26
|
/**
|
|
23
27
|
* Generate all query hook files for all tables
|
|
24
28
|
*/
|
|
25
|
-
export declare function generateAllQueryHooks(tables: CleanTable[]): GeneratedQueryFile[];
|
|
29
|
+
export declare function generateAllQueryHooks(tables: CleanTable[], options?: QueryGeneratorOptions): GeneratedQueryFile[];
|
package/cli/codegen/queries.js
CHANGED
|
@@ -12,7 +12,8 @@ const utils_1 = require("./utils");
|
|
|
12
12
|
/**
|
|
13
13
|
* Generate list query hook file content using AST
|
|
14
14
|
*/
|
|
15
|
-
function generateListQueryHook(table) {
|
|
15
|
+
function generateListQueryHook(table, options = {}) {
|
|
16
|
+
const { reactQueryEnabled = true } = options;
|
|
16
17
|
const project = (0, ts_ast_1.createProject)();
|
|
17
18
|
const { typeName, pluralName } = (0, utils_1.getTableNames)(table);
|
|
18
19
|
const hookName = (0, utils_1.getListQueryHookName)(table);
|
|
@@ -25,32 +26,36 @@ function generateListQueryHook(table) {
|
|
|
25
26
|
const queryDocument = (0, gql_ast_1.printGraphQL)(queryAST);
|
|
26
27
|
const sourceFile = (0, ts_ast_1.createSourceFile)(project, (0, utils_1.getListQueryFileName)(table));
|
|
27
28
|
// Add file header as leading comment
|
|
28
|
-
|
|
29
|
+
const headerText = reactQueryEnabled
|
|
30
|
+
? `List query hook for ${typeName}`
|
|
31
|
+
: `List query functions for ${typeName}`;
|
|
32
|
+
sourceFile.insertText(0, (0, ts_ast_1.createFileHeader)(headerText) + '\n\n');
|
|
29
33
|
// Collect all filter types used by this table's fields
|
|
30
34
|
const filterTypesUsed = new Set();
|
|
31
35
|
for (const field of scalarFields) {
|
|
32
|
-
const filterType = (0, utils_1.getScalarFilterType)(field.type.gqlType);
|
|
36
|
+
const filterType = (0, utils_1.getScalarFilterType)(field.type.gqlType, field.type.isArray);
|
|
33
37
|
if (filterType) {
|
|
34
38
|
filterTypesUsed.add(filterType);
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
|
-
// Add imports
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
// Add imports - conditionally include React Query imports
|
|
42
|
+
const imports = [];
|
|
43
|
+
if (reactQueryEnabled) {
|
|
44
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
40
45
|
moduleSpecifier: '@tanstack/react-query',
|
|
41
46
|
namedImports: ['useQuery'],
|
|
42
47
|
typeOnlyNamedImports: ['UseQueryOptions', 'QueryClient'],
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
51
|
+
moduleSpecifier: '../client',
|
|
52
|
+
namedImports: ['execute'],
|
|
53
|
+
typeOnlyNamedImports: ['ExecuteOptions'],
|
|
54
|
+
}), (0, ts_ast_1.createImport)({
|
|
55
|
+
moduleSpecifier: '../types',
|
|
56
|
+
typeOnlyNamedImports: [typeName, ...Array.from(filterTypesUsed)],
|
|
57
|
+
}));
|
|
58
|
+
sourceFile.addImportDeclarations(imports);
|
|
54
59
|
// Re-export entity type
|
|
55
60
|
sourceFile.addStatements(`\n// Re-export entity type for convenience\nexport type { ${typeName} };\n`);
|
|
56
61
|
// Add section comment
|
|
@@ -66,12 +71,14 @@ function generateListQueryHook(table) {
|
|
|
66
71
|
// Generate filter interface
|
|
67
72
|
const fieldFilters = scalarFields
|
|
68
73
|
.map((field) => {
|
|
69
|
-
const filterType = (0, utils_1.getScalarFilterType)(field.type.gqlType);
|
|
74
|
+
const filterType = (0, utils_1.getScalarFilterType)(field.type.gqlType, field.type.isArray);
|
|
70
75
|
return filterType ? { fieldName: field.name, filterType } : null;
|
|
71
76
|
})
|
|
72
77
|
.filter((f) => f !== null);
|
|
73
|
-
|
|
78
|
+
// Note: Not exported to avoid conflicts with schema-types
|
|
79
|
+
sourceFile.addInterface((0, ts_ast_1.createFilterInterface)(filterTypeName, fieldFilters, { isExported: false }));
|
|
74
80
|
// Generate OrderBy type
|
|
81
|
+
// Note: Not exported to avoid conflicts with schema-types
|
|
75
82
|
const orderByValues = [
|
|
76
83
|
...scalarFields.flatMap((f) => [
|
|
77
84
|
`${(0, utils_1.toScreamingSnake)(f.name)}_ASC`,
|
|
@@ -81,7 +88,7 @@ function generateListQueryHook(table) {
|
|
|
81
88
|
'PRIMARY_KEY_ASC',
|
|
82
89
|
'PRIMARY_KEY_DESC',
|
|
83
90
|
];
|
|
84
|
-
sourceFile.addTypeAlias((0, ts_ast_1.createTypeAlias)(orderByTypeName, (0, ts_ast_1.createUnionType)(orderByValues)));
|
|
91
|
+
sourceFile.addTypeAlias((0, ts_ast_1.createTypeAlias)(orderByTypeName, (0, ts_ast_1.createUnionType)(orderByValues), { isExported: false }));
|
|
85
92
|
// Variables interface
|
|
86
93
|
const variablesProps = [
|
|
87
94
|
{ name: 'first', type: 'number', optional: true },
|
|
@@ -114,27 +121,28 @@ function generateListQueryHook(table) {
|
|
|
114
121
|
// Query key factory
|
|
115
122
|
sourceFile.addVariableStatement((0, ts_ast_1.createConst)(`${queryName}QueryKey`, `(variables?: ${(0, utils_1.ucFirst)(pluralName)}QueryVariables) =>
|
|
116
123
|
['${typeName.toLowerCase()}', 'list', variables] as const`));
|
|
117
|
-
// Add section
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
124
|
+
// Add React Query hook section (only if enabled)
|
|
125
|
+
if (reactQueryEnabled) {
|
|
126
|
+
sourceFile.addStatements('\n// ============================================================================');
|
|
127
|
+
sourceFile.addStatements('// Hook');
|
|
128
|
+
sourceFile.addStatements('// ============================================================================\n');
|
|
129
|
+
// Hook function
|
|
130
|
+
sourceFile.addFunction({
|
|
131
|
+
name: hookName,
|
|
132
|
+
isExported: true,
|
|
133
|
+
parameters: [
|
|
134
|
+
{
|
|
135
|
+
name: 'variables',
|
|
136
|
+
type: `${(0, utils_1.ucFirst)(pluralName)}QueryVariables`,
|
|
137
|
+
hasQuestionToken: true,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: 'options',
|
|
141
|
+
type: `Omit<UseQueryOptions<${(0, utils_1.ucFirst)(pluralName)}QueryResult, Error>, 'queryKey' | 'queryFn'>`,
|
|
142
|
+
hasQuestionToken: true,
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
statements: `return useQuery({
|
|
138
146
|
queryKey: ${queryName}QueryKey(variables),
|
|
139
147
|
queryFn: () => execute<${(0, utils_1.ucFirst)(pluralName)}QueryResult, ${(0, utils_1.ucFirst)(pluralName)}QueryVariables>(
|
|
140
148
|
${queryName}QueryDocument,
|
|
@@ -142,9 +150,9 @@ function generateListQueryHook(table) {
|
|
|
142
150
|
),
|
|
143
151
|
...options,
|
|
144
152
|
});`,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
153
|
+
docs: [
|
|
154
|
+
{
|
|
155
|
+
description: `Query hook for fetching ${typeName} list
|
|
148
156
|
|
|
149
157
|
@example
|
|
150
158
|
\`\`\`tsx
|
|
@@ -154,9 +162,10 @@ const { data, isLoading } = ${hookName}({
|
|
|
154
162
|
orderBy: ['CREATED_AT_DESC'],
|
|
155
163
|
});
|
|
156
164
|
\`\`\``,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
});
|
|
168
|
+
}
|
|
160
169
|
// Add section comment for standalone functions
|
|
161
170
|
sourceFile.addStatements('\n// ============================================================================');
|
|
162
171
|
sourceFile.addStatements('// Standalone Functions (non-React)');
|
|
@@ -202,29 +211,30 @@ const data = await queryClient.fetchQuery({
|
|
|
202
211
|
},
|
|
203
212
|
],
|
|
204
213
|
});
|
|
205
|
-
// Prefetch function (for SSR/QueryClient)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
214
|
+
// Prefetch function (for SSR/QueryClient) - only if React Query is enabled
|
|
215
|
+
if (reactQueryEnabled) {
|
|
216
|
+
sourceFile.addFunction({
|
|
217
|
+
name: `prefetch${(0, utils_1.ucFirst)(pluralName)}Query`,
|
|
218
|
+
isExported: true,
|
|
219
|
+
isAsync: true,
|
|
220
|
+
parameters: [
|
|
221
|
+
{
|
|
222
|
+
name: 'queryClient',
|
|
223
|
+
type: 'QueryClient',
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'variables',
|
|
227
|
+
type: `${(0, utils_1.ucFirst)(pluralName)}QueryVariables`,
|
|
228
|
+
hasQuestionToken: true,
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'options',
|
|
232
|
+
type: 'ExecuteOptions',
|
|
233
|
+
hasQuestionToken: true,
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
returnType: 'Promise<void>',
|
|
237
|
+
statements: `await queryClient.prefetchQuery({
|
|
228
238
|
queryKey: ${queryName}QueryKey(variables),
|
|
229
239
|
queryFn: () => execute<${(0, utils_1.ucFirst)(pluralName)}QueryResult, ${(0, utils_1.ucFirst)(pluralName)}QueryVariables>(
|
|
230
240
|
${queryName}QueryDocument,
|
|
@@ -232,17 +242,18 @@ const data = await queryClient.fetchQuery({
|
|
|
232
242
|
options
|
|
233
243
|
),
|
|
234
244
|
});`,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
245
|
+
docs: [
|
|
246
|
+
{
|
|
247
|
+
description: `Prefetch ${typeName} list for SSR or cache warming
|
|
238
248
|
|
|
239
249
|
@example
|
|
240
250
|
\`\`\`ts
|
|
241
251
|
await prefetch${(0, utils_1.ucFirst)(pluralName)}Query(queryClient, { first: 10 });
|
|
242
252
|
\`\`\``,
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
});
|
|
256
|
+
}
|
|
246
257
|
return {
|
|
247
258
|
fileName: (0, utils_1.getListQueryFileName)(table),
|
|
248
259
|
content: (0, ts_ast_1.getFormattedOutput)(sourceFile),
|
|
@@ -254,34 +265,46 @@ await prefetch${(0, utils_1.ucFirst)(pluralName)}Query(queryClient, { first: 10
|
|
|
254
265
|
/**
|
|
255
266
|
* Generate single item query hook file content using AST
|
|
256
267
|
*/
|
|
257
|
-
function generateSingleQueryHook(table) {
|
|
268
|
+
function generateSingleQueryHook(table, options = {}) {
|
|
269
|
+
const { reactQueryEnabled = true } = options;
|
|
258
270
|
const project = (0, ts_ast_1.createProject)();
|
|
259
271
|
const { typeName, singularName } = (0, utils_1.getTableNames)(table);
|
|
260
272
|
const hookName = (0, utils_1.getSingleQueryHookName)(table);
|
|
261
273
|
const queryName = (0, utils_1.getSingleRowQueryName)(table);
|
|
274
|
+
// Get primary key info dynamically from table constraints
|
|
275
|
+
const pkFields = (0, utils_1.getPrimaryKeyInfo)(table);
|
|
276
|
+
// For simplicity, use first PK field (most common case)
|
|
277
|
+
// Composite PKs would need more complex handling
|
|
278
|
+
const pkField = pkFields[0];
|
|
279
|
+
const pkName = pkField.name;
|
|
280
|
+
const pkTsType = pkField.tsType;
|
|
262
281
|
// Generate GraphQL document via AST
|
|
263
282
|
const queryAST = (0, gql_ast_1.buildSingleQueryAST)({ table });
|
|
264
283
|
const queryDocument = (0, gql_ast_1.printGraphQL)(queryAST);
|
|
265
284
|
const sourceFile = (0, ts_ast_1.createSourceFile)(project, (0, utils_1.getSingleQueryFileName)(table));
|
|
266
285
|
// Add file header
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
286
|
+
const headerText = reactQueryEnabled
|
|
287
|
+
? `Single item query hook for ${typeName}`
|
|
288
|
+
: `Single item query functions for ${typeName}`;
|
|
289
|
+
sourceFile.insertText(0, (0, ts_ast_1.createFileHeader)(headerText) + '\n\n');
|
|
290
|
+
// Add imports - conditionally include React Query imports
|
|
291
|
+
const imports = [];
|
|
292
|
+
if (reactQueryEnabled) {
|
|
293
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
271
294
|
moduleSpecifier: '@tanstack/react-query',
|
|
272
295
|
namedImports: ['useQuery'],
|
|
273
296
|
typeOnlyNamedImports: ['UseQueryOptions', 'QueryClient'],
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
300
|
+
moduleSpecifier: '../client',
|
|
301
|
+
namedImports: ['execute'],
|
|
302
|
+
typeOnlyNamedImports: ['ExecuteOptions'],
|
|
303
|
+
}), (0, ts_ast_1.createImport)({
|
|
304
|
+
moduleSpecifier: '../types',
|
|
305
|
+
typeOnlyNamedImports: [typeName],
|
|
306
|
+
}));
|
|
307
|
+
sourceFile.addImportDeclarations(imports);
|
|
285
308
|
// Re-export entity type
|
|
286
309
|
sourceFile.addStatements(`\n// Re-export entity type for convenience\nexport type { ${typeName} };\n`);
|
|
287
310
|
// Add section comment
|
|
@@ -294,9 +317,9 @@ function generateSingleQueryHook(table) {
|
|
|
294
317
|
sourceFile.addStatements('\n// ============================================================================');
|
|
295
318
|
sourceFile.addStatements('// Types');
|
|
296
319
|
sourceFile.addStatements('// ============================================================================\n');
|
|
297
|
-
// Variables interface
|
|
320
|
+
// Variables interface - use dynamic PK field name and type
|
|
298
321
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${(0, utils_1.ucFirst)(singularName)}QueryVariables`, [
|
|
299
|
-
{ name:
|
|
322
|
+
{ name: pkName, type: pkTsType },
|
|
300
323
|
]));
|
|
301
324
|
// Result interface
|
|
302
325
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${(0, utils_1.ucFirst)(singularName)}QueryResult`, [
|
|
@@ -306,60 +329,62 @@ function generateSingleQueryHook(table) {
|
|
|
306
329
|
sourceFile.addStatements('\n// ============================================================================');
|
|
307
330
|
sourceFile.addStatements('// Query Key');
|
|
308
331
|
sourceFile.addStatements('// ============================================================================\n');
|
|
309
|
-
// Query key factory
|
|
310
|
-
sourceFile.addVariableStatement((0, ts_ast_1.createConst)(`${queryName}QueryKey`, `(
|
|
311
|
-
['${typeName.toLowerCase()}', 'detail',
|
|
312
|
-
// Add section
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
332
|
+
// Query key factory - use dynamic PK field name and type
|
|
333
|
+
sourceFile.addVariableStatement((0, ts_ast_1.createConst)(`${queryName}QueryKey`, `(${pkName}: ${pkTsType}) =>
|
|
334
|
+
['${typeName.toLowerCase()}', 'detail', ${pkName}] as const`));
|
|
335
|
+
// Add React Query hook section (only if enabled)
|
|
336
|
+
if (reactQueryEnabled) {
|
|
337
|
+
sourceFile.addStatements('\n// ============================================================================');
|
|
338
|
+
sourceFile.addStatements('// Hook');
|
|
339
|
+
sourceFile.addStatements('// ============================================================================\n');
|
|
340
|
+
// Hook function - use dynamic PK field name and type
|
|
341
|
+
sourceFile.addFunction({
|
|
342
|
+
name: hookName,
|
|
343
|
+
isExported: true,
|
|
344
|
+
parameters: [
|
|
345
|
+
{ name: pkName, type: pkTsType },
|
|
346
|
+
{
|
|
347
|
+
name: 'options',
|
|
348
|
+
type: `Omit<UseQueryOptions<${(0, utils_1.ucFirst)(singularName)}QueryResult, Error>, 'queryKey' | 'queryFn'>`,
|
|
349
|
+
hasQuestionToken: true,
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
statements: `return useQuery({
|
|
353
|
+
queryKey: ${queryName}QueryKey(${pkName}),
|
|
330
354
|
queryFn: () => execute<${(0, utils_1.ucFirst)(singularName)}QueryResult, ${(0, utils_1.ucFirst)(singularName)}QueryVariables>(
|
|
331
355
|
${queryName}QueryDocument,
|
|
332
|
-
{
|
|
356
|
+
{ ${pkName} }
|
|
333
357
|
),
|
|
334
|
-
enabled:
|
|
358
|
+
enabled: !!${pkName} && (options?.enabled !== false),
|
|
335
359
|
...options,
|
|
336
360
|
});`,
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
361
|
+
docs: [
|
|
362
|
+
{
|
|
363
|
+
description: `Query hook for fetching a single ${typeName} by primary key
|
|
340
364
|
|
|
341
365
|
@example
|
|
342
366
|
\`\`\`tsx
|
|
343
|
-
const { data, isLoading } = ${hookName}('
|
|
367
|
+
const { data, isLoading } = ${hookName}(${pkTsType === 'string' ? "'value-here'" : '123'});
|
|
344
368
|
|
|
345
369
|
if (data?.${queryName}) {
|
|
346
|
-
console.log(data.${queryName}
|
|
370
|
+
console.log(data.${queryName}.${pkName});
|
|
347
371
|
}
|
|
348
372
|
\`\`\``,
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
});
|
|
376
|
+
}
|
|
352
377
|
// Add section comment for standalone functions
|
|
353
378
|
sourceFile.addStatements('\n// ============================================================================');
|
|
354
379
|
sourceFile.addStatements('// Standalone Functions (non-React)');
|
|
355
380
|
sourceFile.addStatements('// ============================================================================\n');
|
|
356
|
-
// Fetch function (standalone, no React)
|
|
381
|
+
// Fetch function (standalone, no React) - use dynamic PK
|
|
357
382
|
sourceFile.addFunction({
|
|
358
383
|
name: `fetch${(0, utils_1.ucFirst)(singularName)}Query`,
|
|
359
384
|
isExported: true,
|
|
360
385
|
isAsync: true,
|
|
361
386
|
parameters: [
|
|
362
|
-
{ name:
|
|
387
|
+
{ name: pkName, type: pkTsType },
|
|
363
388
|
{
|
|
364
389
|
name: 'options',
|
|
365
390
|
type: 'ExecuteOptions',
|
|
@@ -369,54 +394,56 @@ if (data?.${queryName}) {
|
|
|
369
394
|
returnType: `Promise<${(0, utils_1.ucFirst)(singularName)}QueryResult>`,
|
|
370
395
|
statements: `return execute<${(0, utils_1.ucFirst)(singularName)}QueryResult, ${(0, utils_1.ucFirst)(singularName)}QueryVariables>(
|
|
371
396
|
${queryName}QueryDocument,
|
|
372
|
-
{
|
|
397
|
+
{ ${pkName} },
|
|
373
398
|
options
|
|
374
399
|
);`,
|
|
375
400
|
docs: [
|
|
376
401
|
{
|
|
377
|
-
description: `Fetch a single ${typeName} by
|
|
402
|
+
description: `Fetch a single ${typeName} by primary key without React hooks
|
|
378
403
|
|
|
379
404
|
@example
|
|
380
405
|
\`\`\`ts
|
|
381
|
-
const data = await fetch${(0, utils_1.ucFirst)(singularName)}Query('
|
|
406
|
+
const data = await fetch${(0, utils_1.ucFirst)(singularName)}Query(${pkTsType === 'string' ? "'value-here'" : '123'});
|
|
382
407
|
\`\`\``,
|
|
383
408
|
},
|
|
384
409
|
],
|
|
385
410
|
});
|
|
386
|
-
// Prefetch function (for SSR/QueryClient)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
411
|
+
// Prefetch function (for SSR/QueryClient) - only if React Query is enabled, use dynamic PK
|
|
412
|
+
if (reactQueryEnabled) {
|
|
413
|
+
sourceFile.addFunction({
|
|
414
|
+
name: `prefetch${(0, utils_1.ucFirst)(singularName)}Query`,
|
|
415
|
+
isExported: true,
|
|
416
|
+
isAsync: true,
|
|
417
|
+
parameters: [
|
|
418
|
+
{ name: 'queryClient', type: 'QueryClient' },
|
|
419
|
+
{ name: pkName, type: pkTsType },
|
|
420
|
+
{
|
|
421
|
+
name: 'options',
|
|
422
|
+
type: 'ExecuteOptions',
|
|
423
|
+
hasQuestionToken: true,
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
returnType: 'Promise<void>',
|
|
427
|
+
statements: `await queryClient.prefetchQuery({
|
|
428
|
+
queryKey: ${queryName}QueryKey(${pkName}),
|
|
403
429
|
queryFn: () => execute<${(0, utils_1.ucFirst)(singularName)}QueryResult, ${(0, utils_1.ucFirst)(singularName)}QueryVariables>(
|
|
404
430
|
${queryName}QueryDocument,
|
|
405
|
-
{
|
|
431
|
+
{ ${pkName} },
|
|
406
432
|
options
|
|
407
433
|
),
|
|
408
434
|
});`,
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
435
|
+
docs: [
|
|
436
|
+
{
|
|
437
|
+
description: `Prefetch a single ${typeName} for SSR or cache warming
|
|
412
438
|
|
|
413
439
|
@example
|
|
414
440
|
\`\`\`ts
|
|
415
|
-
await prefetch${(0, utils_1.ucFirst)(singularName)}Query(queryClient, '
|
|
441
|
+
await prefetch${(0, utils_1.ucFirst)(singularName)}Query(queryClient, ${pkTsType === 'string' ? "'value-here'" : '123'});
|
|
416
442
|
\`\`\``,
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
443
|
+
},
|
|
444
|
+
],
|
|
445
|
+
});
|
|
446
|
+
}
|
|
420
447
|
return {
|
|
421
448
|
fileName: (0, utils_1.getSingleQueryFileName)(table),
|
|
422
449
|
content: (0, ts_ast_1.getFormattedOutput)(sourceFile),
|
|
@@ -428,11 +455,11 @@ await prefetch${(0, utils_1.ucFirst)(singularName)}Query(queryClient, 'uuid-here
|
|
|
428
455
|
/**
|
|
429
456
|
* Generate all query hook files for all tables
|
|
430
457
|
*/
|
|
431
|
-
function generateAllQueryHooks(tables) {
|
|
458
|
+
function generateAllQueryHooks(tables, options = {}) {
|
|
432
459
|
const files = [];
|
|
433
460
|
for (const table of tables) {
|
|
434
|
-
files.push(generateListQueryHook(table));
|
|
435
|
-
files.push(generateSingleQueryHook(table));
|
|
461
|
+
files.push(generateListQueryHook(table, options));
|
|
462
|
+
files.push(generateSingleQueryHook(table, options));
|
|
436
463
|
}
|
|
437
464
|
return files;
|
|
438
465
|
}
|
package/cli/codegen/scalars.d.ts
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
export declare const SCALAR_TS_MAP: Record<string, string>;
|
|
5
5
|
export declare const SCALAR_FILTER_MAP: Record<string, string>;
|
|
6
6
|
export declare const SCALAR_NAMES: Set<string>;
|
|
7
|
-
|
|
7
|
+
/** All base filter type names - skip these in schema-types.ts to avoid duplicates */
|
|
8
|
+
export declare const BASE_FILTER_TYPE_NAMES: Set<string>;
|
|
9
|
+
export declare function scalarToTsType(scalarName: string, options?: {
|
|
8
10
|
unknownScalar?: 'unknown' | 'name';
|
|
9
11
|
overrides?: Record<string, string>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export declare function scalarToFilterType(scalarName: string): string | null;
|
|
12
|
+
}): string;
|
|
13
|
+
/** Get the filter type for a scalar (handles both scalar and array types) */
|
|
14
|
+
export declare function scalarToFilterType(scalarName: string, isArray?: boolean): string | null;
|
package/cli/codegen/scalars.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Shared scalar mappings for code generation
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.SCALAR_NAMES = exports.SCALAR_FILTER_MAP = exports.SCALAR_TS_MAP = void 0;
|
|
6
|
+
exports.BASE_FILTER_TYPE_NAMES = exports.SCALAR_NAMES = exports.SCALAR_FILTER_MAP = exports.SCALAR_TS_MAP = void 0;
|
|
7
7
|
exports.scalarToTsType = scalarToTsType;
|
|
8
8
|
exports.scalarToFilterType = scalarToFilterType;
|
|
9
9
|
exports.SCALAR_TS_MAP = {
|
|
@@ -37,6 +37,8 @@ exports.SCALAR_TS_MAP = {
|
|
|
37
37
|
MacAddr: 'string',
|
|
38
38
|
TsVector: 'string',
|
|
39
39
|
TsQuery: 'string',
|
|
40
|
+
// File upload
|
|
41
|
+
Upload: 'File',
|
|
40
42
|
};
|
|
41
43
|
exports.SCALAR_FILTER_MAP = {
|
|
42
44
|
String: 'StringFilter',
|
|
@@ -57,15 +59,21 @@ exports.SCALAR_FILTER_MAP = {
|
|
|
57
59
|
Interval: 'StringFilter',
|
|
58
60
|
};
|
|
59
61
|
exports.SCALAR_NAMES = new Set(Object.keys(exports.SCALAR_TS_MAP));
|
|
62
|
+
/** Scalars that have list filter variants (e.g., StringListFilter) */
|
|
63
|
+
const LIST_FILTER_SCALARS = new Set(['String', 'Int', 'UUID']);
|
|
64
|
+
/** All base filter type names - skip these in schema-types.ts to avoid duplicates */
|
|
65
|
+
exports.BASE_FILTER_TYPE_NAMES = new Set([
|
|
66
|
+
...new Set(Object.values(exports.SCALAR_FILTER_MAP)),
|
|
67
|
+
...Array.from(LIST_FILTER_SCALARS).map((s) => `${s}ListFilter`),
|
|
68
|
+
]);
|
|
60
69
|
function scalarToTsType(scalarName, options = {}) {
|
|
61
|
-
|
|
62
|
-
if (override)
|
|
63
|
-
return override;
|
|
64
|
-
const mapped = exports.SCALAR_TS_MAP[scalarName];
|
|
65
|
-
if (mapped)
|
|
66
|
-
return mapped;
|
|
67
|
-
return options.unknownScalar === 'unknown' ? 'unknown' : scalarName;
|
|
70
|
+
return options.overrides?.[scalarName] ?? exports.SCALAR_TS_MAP[scalarName] ?? (options.unknownScalar === 'unknown' ? 'unknown' : scalarName);
|
|
68
71
|
}
|
|
69
|
-
|
|
72
|
+
/** Get the filter type for a scalar (handles both scalar and array types) */
|
|
73
|
+
function scalarToFilterType(scalarName, isArray = false) {
|
|
74
|
+
const baseName = scalarName === 'ID' ? 'UUID' : scalarName;
|
|
75
|
+
if (isArray) {
|
|
76
|
+
return LIST_FILTER_SCALARS.has(baseName) ? `${baseName}ListFilter` : null;
|
|
77
|
+
}
|
|
70
78
|
return exports.SCALAR_FILTER_MAP[scalarName] ?? null;
|
|
71
79
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TypeRegistry } from '../../types/schema';
|
|
2
|
+
export interface GeneratedSchemaTypesFile {
|
|
3
|
+
fileName: string;
|
|
4
|
+
content: string;
|
|
5
|
+
/** List of enum type names that were generated */
|
|
6
|
+
generatedEnums: string[];
|
|
7
|
+
/** List of table entity types that are referenced */
|
|
8
|
+
referencedTableTypes: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface GenerateSchemaTypesOptions {
|
|
11
|
+
/** The TypeRegistry containing all GraphQL types */
|
|
12
|
+
typeRegistry: TypeRegistry;
|
|
13
|
+
/** Type names that already exist in types.ts (table entity types) */
|
|
14
|
+
tableTypeNames: Set<string>;
|
|
15
|
+
}
|
|
16
|
+
export interface PayloadTypesResult {
|
|
17
|
+
generatedTypes: Set<string>;
|
|
18
|
+
referencedTableTypes: Set<string>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Generate comprehensive schema-types.ts file using ts-morph AST
|
|
22
|
+
*
|
|
23
|
+
* This generates all Input/Payload/Enum types from the TypeRegistry
|
|
24
|
+
* that are needed by custom mutation/query hooks.
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateSchemaTypesFile(options: GenerateSchemaTypesOptions): GeneratedSchemaTypesFile;
|