@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
|
@@ -53,6 +53,39 @@ export function getConfig(): GraphQLClientConfig {
|
|
|
53
53
|
return globalConfig;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Set a single header value
|
|
58
|
+
* Useful for updating Authorization after login
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* \`\`\`ts
|
|
62
|
+
* setHeader('Authorization', 'Bearer <new-token>');
|
|
63
|
+
* \`\`\`
|
|
64
|
+
*/
|
|
65
|
+
export function setHeader(key: string, value: string): void {
|
|
66
|
+
const config = getConfig();
|
|
67
|
+
globalConfig = {
|
|
68
|
+
...config,
|
|
69
|
+
headers: { ...config.headers, [key]: value },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Merge multiple headers into the current configuration
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* \`\`\`ts
|
|
78
|
+
* setHeaders({ Authorization: 'Bearer <token>', 'X-Custom': 'value' });
|
|
79
|
+
* \`\`\`
|
|
80
|
+
*/
|
|
81
|
+
export function setHeaders(headers: Record<string, string>): void {
|
|
82
|
+
const config = getConfig();
|
|
83
|
+
globalConfig = {
|
|
84
|
+
...config,
|
|
85
|
+
headers: { ...config.headers, ...headers },
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
56
89
|
// ============================================================================
|
|
57
90
|
// Error handling
|
|
58
91
|
// ============================================================================
|
|
@@ -21,18 +21,28 @@ 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;
|
|
26
|
+
/** Table entity type names (for import path resolution) */
|
|
27
|
+
tableTypeNames?: Set<string>;
|
|
24
28
|
}
|
|
25
29
|
/**
|
|
26
30
|
* Generate a custom mutation hook file
|
|
31
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
27
32
|
*/
|
|
28
|
-
export declare function generateCustomMutationHook(options: GenerateCustomMutationHookOptions): GeneratedCustomMutationFile;
|
|
33
|
+
export declare function generateCustomMutationHook(options: GenerateCustomMutationHookOptions): GeneratedCustomMutationFile | null;
|
|
29
34
|
export interface GenerateAllCustomMutationHooksOptions {
|
|
30
35
|
operations: CleanOperation[];
|
|
31
36
|
typeRegistry: TypeRegistry;
|
|
32
37
|
maxDepth?: number;
|
|
33
38
|
skipQueryField?: boolean;
|
|
39
|
+
/** Whether to generate React Query hooks (default: true for backwards compatibility) */
|
|
40
|
+
reactQueryEnabled?: boolean;
|
|
41
|
+
/** Table entity type names (for import path resolution) */
|
|
42
|
+
tableTypeNames?: Set<string>;
|
|
34
43
|
}
|
|
35
44
|
/**
|
|
36
45
|
* Generate all custom mutation hook files
|
|
46
|
+
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
37
47
|
*/
|
|
38
48
|
export declare function generateAllCustomMutationHooks(options: GenerateAllCustomMutationHooksOptions): GeneratedCustomMutationFile[];
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { createProject, createSourceFile, getFormattedOutput, createFileHeader, createImport, createInterface, createConst, } from './ts-ast';
|
|
2
2
|
import { buildCustomMutationString } from './schema-gql-ast';
|
|
3
|
-
import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, } from './type-resolver';
|
|
3
|
+
import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, createTypeTracker, } 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
|
}
|
|
@@ -16,13 +21,15 @@ export function generateCustomMutationHook(options) {
|
|
|
16
21
|
}
|
|
17
22
|
}
|
|
18
23
|
function generateCustomMutationHookInternal(options) {
|
|
19
|
-
const { operation, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
24
|
+
const { operation, typeRegistry, maxDepth = 2, skipQueryField = true, tableTypeNames } = options;
|
|
20
25
|
const project = createProject();
|
|
21
26
|
const hookName = getOperationHookName(operation.name, 'mutation');
|
|
22
27
|
const fileName = getOperationFileName(operation.name, 'mutation');
|
|
23
28
|
const variablesTypeName = getOperationVariablesTypeName(operation.name, 'mutation');
|
|
24
29
|
const resultTypeName = getOperationResultTypeName(operation.name, 'mutation');
|
|
25
30
|
const documentConstName = getDocumentConstName(operation.name, 'mutation');
|
|
31
|
+
// Create type tracker to collect referenced types (with table type awareness)
|
|
32
|
+
const tracker = createTypeTracker({ tableTypeNames });
|
|
26
33
|
// Generate GraphQL document
|
|
27
34
|
const mutationDocument = buildCustomMutationString({
|
|
28
35
|
operation,
|
|
@@ -33,8 +40,21 @@ function generateCustomMutationHookInternal(options) {
|
|
|
33
40
|
const sourceFile = createSourceFile(project, fileName);
|
|
34
41
|
// Add file header
|
|
35
42
|
sourceFile.insertText(0, createFileHeader(`Custom mutation hook for ${operation.name}`) + '\n\n');
|
|
43
|
+
// Generate variables interface if there are arguments (with tracking)
|
|
44
|
+
let variablesProps = [];
|
|
45
|
+
if (operation.args.length > 0) {
|
|
46
|
+
variablesProps = generateVariablesProperties(operation.args, tracker);
|
|
47
|
+
}
|
|
48
|
+
// Generate result interface (with tracking)
|
|
49
|
+
const resultType = typeRefToTsType(operation.returnType, tracker);
|
|
50
|
+
const resultProps = [
|
|
51
|
+
{ name: operation.name, type: resultType },
|
|
52
|
+
];
|
|
53
|
+
// Get importable types from tracker (separated by source)
|
|
54
|
+
const schemaTypes = tracker.getImportableTypes(); // From schema-types.ts
|
|
55
|
+
const tableTypes = tracker.getTableTypes(); // From types.ts
|
|
36
56
|
// Add imports
|
|
37
|
-
|
|
57
|
+
const imports = [
|
|
38
58
|
createImport({
|
|
39
59
|
moduleSpecifier: '@tanstack/react-query',
|
|
40
60
|
namedImports: ['useMutation'],
|
|
@@ -44,21 +64,31 @@ function generateCustomMutationHookInternal(options) {
|
|
|
44
64
|
moduleSpecifier: '../client',
|
|
45
65
|
namedImports: ['execute'],
|
|
46
66
|
}),
|
|
47
|
-
]
|
|
67
|
+
];
|
|
68
|
+
// Add types.ts import for table entity types
|
|
69
|
+
if (tableTypes.length > 0) {
|
|
70
|
+
imports.push(createImport({
|
|
71
|
+
moduleSpecifier: '../types',
|
|
72
|
+
typeOnlyNamedImports: tableTypes,
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
// Add schema-types import for Input/Payload/Enum types
|
|
76
|
+
if (schemaTypes.length > 0) {
|
|
77
|
+
imports.push(createImport({
|
|
78
|
+
moduleSpecifier: '../schema-types',
|
|
79
|
+
typeOnlyNamedImports: schemaTypes,
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
sourceFile.addImportDeclarations(imports);
|
|
48
83
|
// Add mutation document constant
|
|
49
84
|
sourceFile.addVariableStatement(createConst(documentConstName, '`\n' + mutationDocument + '`', {
|
|
50
85
|
docs: ['GraphQL mutation document'],
|
|
51
86
|
}));
|
|
52
|
-
//
|
|
87
|
+
// Add variables interface
|
|
53
88
|
if (operation.args.length > 0) {
|
|
54
|
-
const variablesProps = generateVariablesProperties(operation.args);
|
|
55
89
|
sourceFile.addInterface(createInterface(variablesTypeName, variablesProps));
|
|
56
90
|
}
|
|
57
|
-
//
|
|
58
|
-
const resultType = typeRefToTsType(operation.returnType);
|
|
59
|
-
const resultProps = [
|
|
60
|
-
{ name: operation.name, type: resultType },
|
|
61
|
-
];
|
|
91
|
+
// Add result interface
|
|
62
92
|
sourceFile.addInterface(createInterface(resultTypeName, resultProps));
|
|
63
93
|
// Generate hook function
|
|
64
94
|
const hookParams = generateHookParameters(operation, variablesTypeName, resultTypeName);
|
|
@@ -82,10 +112,10 @@ function generateCustomMutationHookInternal(options) {
|
|
|
82
112
|
/**
|
|
83
113
|
* Generate interface properties from CleanArguments
|
|
84
114
|
*/
|
|
85
|
-
function generateVariablesProperties(args) {
|
|
115
|
+
function generateVariablesProperties(args, tracker) {
|
|
86
116
|
return args.map((arg) => ({
|
|
87
117
|
name: arg.name,
|
|
88
|
-
type: typeRefToTsType(arg.type),
|
|
118
|
+
type: typeRefToTsType(arg.type, tracker),
|
|
89
119
|
optional: !isTypeRequired(arg.type),
|
|
90
120
|
docs: arg.description ? [arg.description] : undefined,
|
|
91
121
|
}));
|
|
@@ -131,9 +161,10 @@ function generateHookBody(operation, documentConstName, variablesTypeName, resul
|
|
|
131
161
|
}
|
|
132
162
|
/**
|
|
133
163
|
* Generate all custom mutation hook files
|
|
164
|
+
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
134
165
|
*/
|
|
135
166
|
export function generateAllCustomMutationHooks(options) {
|
|
136
|
-
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
167
|
+
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames } = options;
|
|
137
168
|
return operations
|
|
138
169
|
.filter((op) => op.kind === 'mutation')
|
|
139
170
|
.map((operation) => generateCustomMutationHook({
|
|
@@ -141,5 +172,8 @@ export function generateAllCustomMutationHooks(options) {
|
|
|
141
172
|
typeRegistry,
|
|
142
173
|
maxDepth,
|
|
143
174
|
skipQueryField,
|
|
144
|
-
|
|
175
|
+
reactQueryEnabled,
|
|
176
|
+
tableTypeNames,
|
|
177
|
+
}))
|
|
178
|
+
.filter((result) => result !== null);
|
|
145
179
|
}
|
|
@@ -21,6 +21,10 @@ 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;
|
|
26
|
+
/** Table entity type names (for import path resolution) */
|
|
27
|
+
tableTypeNames?: Set<string>;
|
|
24
28
|
}
|
|
25
29
|
/**
|
|
26
30
|
* Generate a custom query hook file
|
|
@@ -31,6 +35,10 @@ export interface GenerateAllCustomQueryHooksOptions {
|
|
|
31
35
|
typeRegistry: TypeRegistry;
|
|
32
36
|
maxDepth?: number;
|
|
33
37
|
skipQueryField?: boolean;
|
|
38
|
+
/** Whether to generate React Query hooks (default: true for backwards compatibility) */
|
|
39
|
+
reactQueryEnabled?: boolean;
|
|
40
|
+
/** Table entity type names (for import path resolution) */
|
|
41
|
+
tableTypeNames?: Set<string>;
|
|
34
42
|
}
|
|
35
43
|
/**
|
|
36
44
|
* Generate all custom query hook files
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createProject, createSourceFile, getFormattedOutput, createFileHeader, createImport, createInterface, createConst, } from './ts-ast';
|
|
2
2
|
import { buildCustomQueryString } from './schema-gql-ast';
|
|
3
|
-
import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, getQueryKeyName, } from './type-resolver';
|
|
3
|
+
import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, getQueryKeyName, createTypeTracker, } from './type-resolver';
|
|
4
4
|
import { ucFirst } from './utils';
|
|
5
5
|
/**
|
|
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, tableTypeNames } = options;
|
|
10
10
|
const project = createProject();
|
|
11
11
|
const hookName = getOperationHookName(operation.name, 'query');
|
|
12
12
|
const fileName = getOperationFileName(operation.name, 'query');
|
|
@@ -14,6 +14,8 @@ export function generateCustomQueryHook(options) {
|
|
|
14
14
|
const resultTypeName = getOperationResultTypeName(operation.name, 'query');
|
|
15
15
|
const documentConstName = getDocumentConstName(operation.name, 'query');
|
|
16
16
|
const queryKeyName = getQueryKeyName(operation.name);
|
|
17
|
+
// Create type tracker to collect referenced types (with table type awareness)
|
|
18
|
+
const tracker = createTypeTracker({ tableTypeNames });
|
|
17
19
|
// Generate GraphQL document
|
|
18
20
|
const queryDocument = buildCustomQueryString({
|
|
19
21
|
operation,
|
|
@@ -23,34 +25,61 @@ export function generateCustomQueryHook(options) {
|
|
|
23
25
|
});
|
|
24
26
|
const sourceFile = createSourceFile(project, fileName);
|
|
25
27
|
// Add file header
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const headerText = reactQueryEnabled
|
|
29
|
+
? `Custom query hook for ${operation.name}`
|
|
30
|
+
: `Custom query functions for ${operation.name}`;
|
|
31
|
+
sourceFile.insertText(0, createFileHeader(headerText) + '\n\n');
|
|
32
|
+
// Generate variables interface if there are arguments (with tracking)
|
|
33
|
+
let variablesProps = [];
|
|
34
|
+
if (operation.args.length > 0) {
|
|
35
|
+
variablesProps = generateVariablesProperties(operation.args, tracker);
|
|
36
|
+
}
|
|
37
|
+
// Generate result interface (with tracking)
|
|
38
|
+
const resultType = typeRefToTsType(operation.returnType, tracker);
|
|
39
|
+
const resultProps = [
|
|
40
|
+
{ name: operation.name, type: resultType },
|
|
41
|
+
];
|
|
42
|
+
// Get importable types from tracker (separated by source)
|
|
43
|
+
const schemaTypes = tracker.getImportableTypes(); // From schema-types.ts
|
|
44
|
+
const tableTypes = tracker.getTableTypes(); // From types.ts
|
|
45
|
+
// Add imports - conditionally include React Query imports
|
|
46
|
+
const imports = [];
|
|
47
|
+
if (reactQueryEnabled) {
|
|
48
|
+
imports.push(createImport({
|
|
30
49
|
moduleSpecifier: '@tanstack/react-query',
|
|
31
50
|
namedImports: ['useQuery'],
|
|
32
51
|
typeOnlyNamedImports: ['UseQueryOptions', 'QueryClient'],
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
imports.push(createImport({
|
|
55
|
+
moduleSpecifier: '../client',
|
|
56
|
+
namedImports: ['execute'],
|
|
57
|
+
typeOnlyNamedImports: ['ExecuteOptions'],
|
|
58
|
+
}));
|
|
59
|
+
// Add types.ts import for table entity types
|
|
60
|
+
if (tableTypes.length > 0) {
|
|
61
|
+
imports.push(createImport({
|
|
62
|
+
moduleSpecifier: '../types',
|
|
63
|
+
typeOnlyNamedImports: tableTypes,
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
// Add schema-types import for Input/Payload/Enum types
|
|
67
|
+
if (schemaTypes.length > 0) {
|
|
68
|
+
imports.push(createImport({
|
|
69
|
+
moduleSpecifier: '../schema-types',
|
|
70
|
+
typeOnlyNamedImports: schemaTypes,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
sourceFile.addImportDeclarations(imports);
|
|
40
74
|
// Add query document constant
|
|
41
75
|
sourceFile.addVariableStatement(createConst(documentConstName, '`\n' + queryDocument + '`', {
|
|
42
76
|
docs: ['GraphQL query document'],
|
|
43
77
|
}));
|
|
44
|
-
//
|
|
78
|
+
// Add variables interface
|
|
45
79
|
if (operation.args.length > 0) {
|
|
46
|
-
const variablesProps = generateVariablesProperties(operation.args);
|
|
47
80
|
sourceFile.addInterface(createInterface(variablesTypeName, variablesProps));
|
|
48
81
|
}
|
|
49
|
-
//
|
|
50
|
-
const resultType = typeRefToTsType(operation.returnType);
|
|
51
|
-
const resultProps = [
|
|
52
|
-
{ name: operation.name, type: resultType },
|
|
53
|
-
];
|
|
82
|
+
// Add result interface
|
|
54
83
|
sourceFile.addInterface(createInterface(resultTypeName, resultProps));
|
|
55
84
|
// Query key factory
|
|
56
85
|
if (operation.args.length > 0) {
|
|
@@ -62,17 +91,19 @@ export function generateCustomQueryHook(options) {
|
|
|
62
91
|
docs: ['Query key factory for caching'],
|
|
63
92
|
}));
|
|
64
93
|
}
|
|
65
|
-
// Generate hook function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
94
|
+
// Generate hook function (only if React Query is enabled)
|
|
95
|
+
if (reactQueryEnabled) {
|
|
96
|
+
const hookParams = generateHookParameters(operation, variablesTypeName, resultTypeName);
|
|
97
|
+
const hookBody = generateHookBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
|
|
98
|
+
const hookDoc = generateHookDoc(operation, hookName);
|
|
99
|
+
sourceFile.addFunction({
|
|
100
|
+
name: hookName,
|
|
101
|
+
isExported: true,
|
|
102
|
+
parameters: hookParams,
|
|
103
|
+
statements: hookBody,
|
|
104
|
+
docs: [{ description: hookDoc }],
|
|
105
|
+
});
|
|
106
|
+
}
|
|
76
107
|
// Add standalone functions section
|
|
77
108
|
sourceFile.addStatements('\n// ============================================================================');
|
|
78
109
|
sourceFile.addStatements('// Standalone Functions (non-React)');
|
|
@@ -91,20 +122,22 @@ export function generateCustomQueryHook(options) {
|
|
|
91
122
|
statements: fetchBody,
|
|
92
123
|
docs: [{ description: fetchDoc }],
|
|
93
124
|
});
|
|
94
|
-
// Generate prefetch function
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
125
|
+
// Generate prefetch function (only if React Query is enabled)
|
|
126
|
+
if (reactQueryEnabled) {
|
|
127
|
+
const prefetchFnName = `prefetch${ucFirst(operation.name)}Query`;
|
|
128
|
+
const prefetchParams = generatePrefetchParameters(operation, variablesTypeName);
|
|
129
|
+
const prefetchBody = generatePrefetchBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
|
|
130
|
+
const prefetchDoc = generatePrefetchDoc(operation, prefetchFnName);
|
|
131
|
+
sourceFile.addFunction({
|
|
132
|
+
name: prefetchFnName,
|
|
133
|
+
isExported: true,
|
|
134
|
+
isAsync: true,
|
|
135
|
+
parameters: prefetchParams,
|
|
136
|
+
returnType: 'Promise<void>',
|
|
137
|
+
statements: prefetchBody,
|
|
138
|
+
docs: [{ description: prefetchDoc }],
|
|
139
|
+
});
|
|
140
|
+
}
|
|
108
141
|
return {
|
|
109
142
|
fileName,
|
|
110
143
|
content: getFormattedOutput(sourceFile),
|
|
@@ -117,10 +150,10 @@ export function generateCustomQueryHook(options) {
|
|
|
117
150
|
/**
|
|
118
151
|
* Generate interface properties from CleanArguments
|
|
119
152
|
*/
|
|
120
|
-
function generateVariablesProperties(args) {
|
|
153
|
+
function generateVariablesProperties(args, tracker) {
|
|
121
154
|
return args.map((arg) => ({
|
|
122
155
|
name: arg.name,
|
|
123
|
-
type: typeRefToTsType(arg.type),
|
|
156
|
+
type: typeRefToTsType(arg.type, tracker),
|
|
124
157
|
optional: !isTypeRequired(arg.type),
|
|
125
158
|
docs: arg.description ? [arg.description] : undefined,
|
|
126
159
|
}));
|
|
@@ -342,7 +375,7 @@ await ${fnName}(queryClient);
|
|
|
342
375
|
* Generate all custom query hook files
|
|
343
376
|
*/
|
|
344
377
|
export function generateAllCustomQueryHooks(options) {
|
|
345
|
-
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
378
|
+
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames } = options;
|
|
346
379
|
return operations
|
|
347
380
|
.filter((op) => op.kind === 'query')
|
|
348
381
|
.map((operation) => generateCustomQueryHook({
|
|
@@ -350,5 +383,7 @@ export function generateAllCustomQueryHooks(options) {
|
|
|
350
383
|
typeRegistry,
|
|
351
384
|
maxDepth,
|
|
352
385
|
skipQueryField,
|
|
386
|
+
reactQueryEnabled,
|
|
387
|
+
tableTypeNames,
|
|
353
388
|
}));
|
|
354
389
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as t from 'gql-ast';
|
|
8
8
|
import { print } from 'graphql';
|
|
9
|
-
import { getTableNames, getAllRowsQueryName, getSingleRowQueryName, getCreateMutationName, getUpdateMutationName, getDeleteMutationName, getFilterTypeName, getOrderByTypeName, getScalarFields, ucFirst, } from './utils';
|
|
9
|
+
import { getTableNames, getAllRowsQueryName, getSingleRowQueryName, getCreateMutationName, getUpdateMutationName, getDeleteMutationName, getFilterTypeName, getOrderByTypeName, getScalarFields, getPrimaryKeyInfo, ucFirst, } from './utils';
|
|
10
10
|
// ============================================================================
|
|
11
11
|
// Field selection builders
|
|
12
12
|
// ============================================================================
|
|
@@ -106,16 +106,20 @@ export function buildSingleQueryAST(config) {
|
|
|
106
106
|
const { table } = config;
|
|
107
107
|
const queryName = getSingleRowQueryName(table);
|
|
108
108
|
const scalarFields = getScalarFields(table);
|
|
109
|
-
//
|
|
109
|
+
// Get primary key info dynamically from table constraints
|
|
110
|
+
const pkFields = getPrimaryKeyInfo(table);
|
|
111
|
+
// For simplicity, use first PK field (most common case)
|
|
112
|
+
const pkField = pkFields[0];
|
|
113
|
+
// Variable definitions - use dynamic PK field name and type
|
|
110
114
|
const variableDefinitions = [
|
|
111
115
|
t.variableDefinition({
|
|
112
|
-
variable: t.variable({ name:
|
|
113
|
-
type: t.nonNullType({ type: t.namedType({ type:
|
|
116
|
+
variable: t.variable({ name: pkField.name }),
|
|
117
|
+
type: t.nonNullType({ type: t.namedType({ type: pkField.gqlType }) }),
|
|
114
118
|
}),
|
|
115
119
|
];
|
|
116
|
-
// Query arguments
|
|
120
|
+
// Query arguments - use dynamic PK field name
|
|
117
121
|
const args = [
|
|
118
|
-
t.argument({ name:
|
|
122
|
+
t.argument({ name: pkField.name, value: t.variable({ name: pkField.name }) }),
|
|
119
123
|
];
|
|
120
124
|
// Field selections
|
|
121
125
|
const fieldSelections = createFieldSelections(scalarFields);
|
package/esm/cli/codegen/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { generateClientFile } from './client';
|
|
2
2
|
import { generateTypesFile } from './types';
|
|
3
|
+
import { generateSchemaTypesFile } from './schema-types-generator';
|
|
3
4
|
import { generateAllQueryHooks } from './queries';
|
|
4
5
|
import { generateAllMutationHooks } from './mutations';
|
|
5
6
|
import { generateAllCustomQueryHooks } from './custom-queries';
|
|
6
7
|
import { generateAllCustomMutationHooks } from './custom-mutations';
|
|
7
8
|
import { generateQueriesBarrel, generateMutationsBarrel, generateMainBarrel, generateCustomQueriesBarrel, generateCustomMutationsBarrel, } from './barrel';
|
|
9
|
+
import { getTableNames } from './utils';
|
|
8
10
|
// ============================================================================
|
|
9
11
|
// Main orchestrator
|
|
10
12
|
// ============================================================================
|
|
@@ -23,25 +25,47 @@ export function generate(options) {
|
|
|
23
25
|
// Extract codegen options
|
|
24
26
|
const maxDepth = config.codegen.maxFieldDepth;
|
|
25
27
|
const skipQueryField = config.codegen.skipQueryField;
|
|
28
|
+
const reactQueryEnabled = config.reactQuery.enabled;
|
|
26
29
|
// 1. Generate client.ts
|
|
27
30
|
files.push({
|
|
28
31
|
path: 'client.ts',
|
|
29
32
|
content: generateClientFile(),
|
|
30
33
|
});
|
|
31
|
-
//
|
|
34
|
+
// Collect table type names for import path resolution
|
|
35
|
+
const tableTypeNames = new Set(tables.map((t) => getTableNames(t).typeName));
|
|
36
|
+
// 2. Generate schema-types.ts for custom operations (if any)
|
|
37
|
+
// NOTE: This must come BEFORE types.ts so that types.ts can import enum types
|
|
38
|
+
let hasSchemaTypes = false;
|
|
39
|
+
let generatedEnumNames = [];
|
|
40
|
+
if (customOperations && customOperations.typeRegistry) {
|
|
41
|
+
const schemaTypesResult = generateSchemaTypesFile({
|
|
42
|
+
typeRegistry: customOperations.typeRegistry,
|
|
43
|
+
tableTypeNames,
|
|
44
|
+
});
|
|
45
|
+
// Only include if there's meaningful content
|
|
46
|
+
if (schemaTypesResult.content.split('\n').length > 10) {
|
|
47
|
+
files.push({
|
|
48
|
+
path: 'schema-types.ts',
|
|
49
|
+
content: schemaTypesResult.content,
|
|
50
|
+
});
|
|
51
|
+
hasSchemaTypes = true;
|
|
52
|
+
generatedEnumNames = schemaTypesResult.generatedEnums || [];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// 3. Generate types.ts (can now import enums from schema-types)
|
|
32
56
|
files.push({
|
|
33
57
|
path: 'types.ts',
|
|
34
|
-
content: generateTypesFile(tables),
|
|
58
|
+
content: generateTypesFile(tables, { enumsFromSchemaTypes: generatedEnumNames }),
|
|
35
59
|
});
|
|
36
|
-
//
|
|
37
|
-
const queryHooks = generateAllQueryHooks(tables);
|
|
60
|
+
// 4. Generate table-based query hooks (queries/*.ts)
|
|
61
|
+
const queryHooks = generateAllQueryHooks(tables, { reactQueryEnabled });
|
|
38
62
|
for (const hook of queryHooks) {
|
|
39
63
|
files.push({
|
|
40
64
|
path: `queries/${hook.fileName}`,
|
|
41
65
|
content: hook.content,
|
|
42
66
|
});
|
|
43
67
|
}
|
|
44
|
-
//
|
|
68
|
+
// 5. Generate custom query hooks if available
|
|
45
69
|
let customQueryHooks = [];
|
|
46
70
|
if (customOperations && customOperations.queries.length > 0) {
|
|
47
71
|
customQueryHooks = generateAllCustomQueryHooks({
|
|
@@ -49,6 +73,8 @@ export function generate(options) {
|
|
|
49
73
|
typeRegistry: customOperations.typeRegistry,
|
|
50
74
|
maxDepth,
|
|
51
75
|
skipQueryField,
|
|
76
|
+
reactQueryEnabled,
|
|
77
|
+
tableTypeNames,
|
|
52
78
|
});
|
|
53
79
|
for (const hook of customQueryHooks) {
|
|
54
80
|
files.push({
|
|
@@ -65,7 +91,10 @@ export function generate(options) {
|
|
|
65
91
|
: generateQueriesBarrel(tables),
|
|
66
92
|
});
|
|
67
93
|
// 6. Generate table-based mutation hooks (mutations/*.ts)
|
|
68
|
-
const mutationHooks = generateAllMutationHooks(tables
|
|
94
|
+
const mutationHooks = generateAllMutationHooks(tables, {
|
|
95
|
+
reactQueryEnabled,
|
|
96
|
+
enumsFromSchemaTypes: generatedEnumNames,
|
|
97
|
+
});
|
|
69
98
|
for (const hook of mutationHooks) {
|
|
70
99
|
files.push({
|
|
71
100
|
path: `mutations/${hook.fileName}`,
|
|
@@ -80,6 +109,8 @@ export function generate(options) {
|
|
|
80
109
|
typeRegistry: customOperations.typeRegistry,
|
|
81
110
|
maxDepth,
|
|
82
111
|
skipQueryField,
|
|
112
|
+
reactQueryEnabled,
|
|
113
|
+
tableTypeNames,
|
|
83
114
|
});
|
|
84
115
|
for (const hook of customMutationHooks) {
|
|
85
116
|
files.push({
|
|
@@ -95,10 +126,10 @@ export function generate(options) {
|
|
|
95
126
|
? generateCustomMutationsBarrel(tables, customMutationHooks.map((h) => h.operationName))
|
|
96
127
|
: generateMutationsBarrel(tables),
|
|
97
128
|
});
|
|
98
|
-
// 9. Generate main index.ts barrel
|
|
129
|
+
// 9. Generate main index.ts barrel (with schema-types if present)
|
|
99
130
|
files.push({
|
|
100
131
|
path: 'index.ts',
|
|
101
|
-
content: generateMainBarrel(tables),
|
|
132
|
+
content: generateMainBarrel(tables, hasSchemaTypes),
|
|
102
133
|
});
|
|
103
134
|
return {
|
|
104
135
|
files,
|
|
@@ -12,19 +12,29 @@ 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
|
+
/** Enum type names that are available from schema-types.ts */
|
|
19
|
+
enumsFromSchemaTypes?: string[];
|
|
20
|
+
}
|
|
15
21
|
/**
|
|
16
22
|
* Generate create mutation hook file content using AST
|
|
23
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
17
24
|
*/
|
|
18
|
-
export declare function generateCreateMutationHook(table: CleanTable): GeneratedMutationFile;
|
|
25
|
+
export declare function generateCreateMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
|
|
19
26
|
/**
|
|
20
27
|
* Generate update mutation hook file content using AST
|
|
28
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
21
29
|
*/
|
|
22
|
-
export declare function generateUpdateMutationHook(table: CleanTable): GeneratedMutationFile | null;
|
|
30
|
+
export declare function generateUpdateMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
|
|
23
31
|
/**
|
|
24
32
|
* Generate delete mutation hook file content using AST
|
|
33
|
+
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
25
34
|
*/
|
|
26
|
-
export declare function generateDeleteMutationHook(table: CleanTable): GeneratedMutationFile | null;
|
|
35
|
+
export declare function generateDeleteMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
|
|
27
36
|
/**
|
|
28
37
|
* Generate all mutation hook files for all tables
|
|
38
|
+
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
29
39
|
*/
|
|
30
|
-
export declare function generateAllMutationHooks(tables: CleanTable[]): GeneratedMutationFile[];
|
|
40
|
+
export declare function generateAllMutationHooks(tables: CleanTable[], options?: MutationGeneratorOptions): GeneratedMutationFile[];
|