@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
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',
|
package/cli/codegen/barrel.d.ts
CHANGED
|
@@ -15,8 +15,11 @@ export declare function generateQueriesBarrel(tables: CleanTable[]): string;
|
|
|
15
15
|
export declare function generateMutationsBarrel(tables: CleanTable[]): string;
|
|
16
16
|
/**
|
|
17
17
|
* Generate the main index.ts barrel file
|
|
18
|
+
*
|
|
19
|
+
* @param tables - The tables to include in the SDK
|
|
20
|
+
* @param hasSchemaTypes - Whether schema-types.ts was generated
|
|
18
21
|
*/
|
|
19
|
-
export declare function generateMainBarrel(tables: CleanTable[]): string;
|
|
22
|
+
export declare function generateMainBarrel(tables: CleanTable[], hasSchemaTypes?: boolean): string;
|
|
20
23
|
/**
|
|
21
24
|
* Generate queries barrel including custom query operations
|
|
22
25
|
*/
|
package/cli/codegen/barrel.js
CHANGED
|
@@ -12,10 +12,7 @@ const type_resolver_1 = require("./type-resolver");
|
|
|
12
12
|
* Generate the queries/index.ts barrel file
|
|
13
13
|
*/
|
|
14
14
|
function generateQueriesBarrel(tables) {
|
|
15
|
-
const lines = [
|
|
16
|
-
(0, ts_ast_1.createFileHeader)('Query hooks barrel export'),
|
|
17
|
-
'',
|
|
18
|
-
];
|
|
15
|
+
const lines = [(0, ts_ast_1.createFileHeader)('Query hooks barrel export'), ''];
|
|
19
16
|
// Export all query hooks
|
|
20
17
|
for (const table of tables) {
|
|
21
18
|
const listHookName = (0, utils_1.getListQueryHookName)(table);
|
|
@@ -51,31 +48,40 @@ function generateMutationsBarrel(tables) {
|
|
|
51
48
|
}
|
|
52
49
|
/**
|
|
53
50
|
* Generate the main index.ts barrel file
|
|
51
|
+
*
|
|
52
|
+
* @param tables - The tables to include in the SDK
|
|
53
|
+
* @param hasSchemaTypes - Whether schema-types.ts was generated
|
|
54
54
|
*/
|
|
55
|
-
function generateMainBarrel(tables) {
|
|
55
|
+
function generateMainBarrel(tables, hasSchemaTypes = false) {
|
|
56
56
|
const tableNames = tables.map((t) => t.name).join(', ');
|
|
57
|
+
const schemaTypesExport = hasSchemaTypes
|
|
58
|
+
? `
|
|
59
|
+
// Schema types (input, payload, enum types)
|
|
60
|
+
export * from './schema-types';
|
|
61
|
+
`
|
|
62
|
+
: '';
|
|
57
63
|
return `/**
|
|
58
64
|
* Auto-generated GraphQL SDK
|
|
59
65
|
* @generated by @constructive-io/graphql-codegen
|
|
60
|
-
*
|
|
66
|
+
*
|
|
61
67
|
* Tables: ${tableNames}
|
|
62
|
-
*
|
|
68
|
+
*
|
|
63
69
|
* Usage:
|
|
64
|
-
*
|
|
70
|
+
*
|
|
65
71
|
* 1. Configure the client:
|
|
66
72
|
* \`\`\`ts
|
|
67
73
|
* import { configure } from './generated';
|
|
68
|
-
*
|
|
74
|
+
*
|
|
69
75
|
* configure({
|
|
70
76
|
* endpoint: 'https://api.example.com/graphql',
|
|
71
77
|
* headers: { Authorization: 'Bearer <token>' },
|
|
72
78
|
* });
|
|
73
79
|
* \`\`\`
|
|
74
|
-
*
|
|
80
|
+
*
|
|
75
81
|
* 2. Use the hooks:
|
|
76
82
|
* \`\`\`tsx
|
|
77
83
|
* import { useCarsQuery, useCreateCarMutation } from './generated';
|
|
78
|
-
*
|
|
84
|
+
*
|
|
79
85
|
* function MyComponent() {
|
|
80
86
|
* const { data, isLoading } = useCarsQuery({ first: 10 });
|
|
81
87
|
* const { mutate } = useCreateCarMutation();
|
|
@@ -89,7 +95,7 @@ export * from './client';
|
|
|
89
95
|
|
|
90
96
|
// Entity and filter types
|
|
91
97
|
export * from './types';
|
|
92
|
-
|
|
98
|
+
${schemaTypesExport}
|
|
93
99
|
// Query hooks
|
|
94
100
|
export * from './queries';
|
|
95
101
|
|
package/cli/codegen/client.js
CHANGED
|
@@ -56,6 +56,39 @@ export function getConfig(): GraphQLClientConfig {
|
|
|
56
56
|
return globalConfig;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Set a single header value
|
|
61
|
+
* Useful for updating Authorization after login
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* \`\`\`ts
|
|
65
|
+
* setHeader('Authorization', 'Bearer <new-token>');
|
|
66
|
+
* \`\`\`
|
|
67
|
+
*/
|
|
68
|
+
export function setHeader(key: string, value: string): void {
|
|
69
|
+
const config = getConfig();
|
|
70
|
+
globalConfig = {
|
|
71
|
+
...config,
|
|
72
|
+
headers: { ...config.headers, [key]: value },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Merge multiple headers into the current configuration
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* \`\`\`ts
|
|
81
|
+
* setHeaders({ Authorization: 'Bearer <token>', 'X-Custom': 'value' });
|
|
82
|
+
* \`\`\`
|
|
83
|
+
*/
|
|
84
|
+
export function setHeaders(headers: Record<string, string>): void {
|
|
85
|
+
const config = getConfig();
|
|
86
|
+
globalConfig = {
|
|
87
|
+
...config,
|
|
88
|
+
headers: { ...config.headers, ...headers },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
59
92
|
// ============================================================================
|
|
60
93
|
// Error handling
|
|
61
94
|
// ============================================================================
|
|
@@ -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[];
|
|
@@ -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
|
}
|
|
@@ -20,13 +25,15 @@ function generateCustomMutationHook(options) {
|
|
|
20
25
|
}
|
|
21
26
|
}
|
|
22
27
|
function generateCustomMutationHookInternal(options) {
|
|
23
|
-
const { operation, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
28
|
+
const { operation, typeRegistry, maxDepth = 2, skipQueryField = true, tableTypeNames } = options;
|
|
24
29
|
const project = (0, ts_ast_1.createProject)();
|
|
25
30
|
const hookName = (0, type_resolver_1.getOperationHookName)(operation.name, 'mutation');
|
|
26
31
|
const fileName = (0, type_resolver_1.getOperationFileName)(operation.name, 'mutation');
|
|
27
32
|
const variablesTypeName = (0, type_resolver_1.getOperationVariablesTypeName)(operation.name, 'mutation');
|
|
28
33
|
const resultTypeName = (0, type_resolver_1.getOperationResultTypeName)(operation.name, 'mutation');
|
|
29
34
|
const documentConstName = (0, type_resolver_1.getDocumentConstName)(operation.name, 'mutation');
|
|
35
|
+
// Create type tracker to collect referenced types (with table type awareness)
|
|
36
|
+
const tracker = (0, type_resolver_1.createTypeTracker)({ tableTypeNames });
|
|
30
37
|
// Generate GraphQL document
|
|
31
38
|
const mutationDocument = (0, schema_gql_ast_1.buildCustomMutationString)({
|
|
32
39
|
operation,
|
|
@@ -37,8 +44,21 @@ function generateCustomMutationHookInternal(options) {
|
|
|
37
44
|
const sourceFile = (0, ts_ast_1.createSourceFile)(project, fileName);
|
|
38
45
|
// Add file header
|
|
39
46
|
sourceFile.insertText(0, (0, ts_ast_1.createFileHeader)(`Custom mutation hook for ${operation.name}`) + '\n\n');
|
|
47
|
+
// Generate variables interface if there are arguments (with tracking)
|
|
48
|
+
let variablesProps = [];
|
|
49
|
+
if (operation.args.length > 0) {
|
|
50
|
+
variablesProps = generateVariablesProperties(operation.args, tracker);
|
|
51
|
+
}
|
|
52
|
+
// Generate result interface (with tracking)
|
|
53
|
+
const resultType = (0, type_resolver_1.typeRefToTsType)(operation.returnType, tracker);
|
|
54
|
+
const resultProps = [
|
|
55
|
+
{ name: operation.name, type: resultType },
|
|
56
|
+
];
|
|
57
|
+
// Get importable types from tracker (separated by source)
|
|
58
|
+
const schemaTypes = tracker.getImportableTypes(); // From schema-types.ts
|
|
59
|
+
const tableTypes = tracker.getTableTypes(); // From types.ts
|
|
40
60
|
// Add imports
|
|
41
|
-
|
|
61
|
+
const imports = [
|
|
42
62
|
(0, ts_ast_1.createImport)({
|
|
43
63
|
moduleSpecifier: '@tanstack/react-query',
|
|
44
64
|
namedImports: ['useMutation'],
|
|
@@ -48,21 +68,31 @@ function generateCustomMutationHookInternal(options) {
|
|
|
48
68
|
moduleSpecifier: '../client',
|
|
49
69
|
namedImports: ['execute'],
|
|
50
70
|
}),
|
|
51
|
-
]
|
|
71
|
+
];
|
|
72
|
+
// Add types.ts import for table entity types
|
|
73
|
+
if (tableTypes.length > 0) {
|
|
74
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
75
|
+
moduleSpecifier: '../types',
|
|
76
|
+
typeOnlyNamedImports: tableTypes,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
// Add schema-types import for Input/Payload/Enum types
|
|
80
|
+
if (schemaTypes.length > 0) {
|
|
81
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
82
|
+
moduleSpecifier: '../schema-types',
|
|
83
|
+
typeOnlyNamedImports: schemaTypes,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
sourceFile.addImportDeclarations(imports);
|
|
52
87
|
// Add mutation document constant
|
|
53
88
|
sourceFile.addVariableStatement((0, ts_ast_1.createConst)(documentConstName, '`\n' + mutationDocument + '`', {
|
|
54
89
|
docs: ['GraphQL mutation document'],
|
|
55
90
|
}));
|
|
56
|
-
//
|
|
91
|
+
// Add variables interface
|
|
57
92
|
if (operation.args.length > 0) {
|
|
58
|
-
const variablesProps = generateVariablesProperties(operation.args);
|
|
59
93
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(variablesTypeName, variablesProps));
|
|
60
94
|
}
|
|
61
|
-
//
|
|
62
|
-
const resultType = (0, type_resolver_1.typeRefToTsType)(operation.returnType);
|
|
63
|
-
const resultProps = [
|
|
64
|
-
{ name: operation.name, type: resultType },
|
|
65
|
-
];
|
|
95
|
+
// Add result interface
|
|
66
96
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(resultTypeName, resultProps));
|
|
67
97
|
// Generate hook function
|
|
68
98
|
const hookParams = generateHookParameters(operation, variablesTypeName, resultTypeName);
|
|
@@ -86,10 +116,10 @@ function generateCustomMutationHookInternal(options) {
|
|
|
86
116
|
/**
|
|
87
117
|
* Generate interface properties from CleanArguments
|
|
88
118
|
*/
|
|
89
|
-
function generateVariablesProperties(args) {
|
|
119
|
+
function generateVariablesProperties(args, tracker) {
|
|
90
120
|
return args.map((arg) => ({
|
|
91
121
|
name: arg.name,
|
|
92
|
-
type: (0, type_resolver_1.typeRefToTsType)(arg.type),
|
|
122
|
+
type: (0, type_resolver_1.typeRefToTsType)(arg.type, tracker),
|
|
93
123
|
optional: !(0, type_resolver_1.isTypeRequired)(arg.type),
|
|
94
124
|
docs: arg.description ? [arg.description] : undefined,
|
|
95
125
|
}));
|
|
@@ -135,9 +165,10 @@ function generateHookBody(operation, documentConstName, variablesTypeName, resul
|
|
|
135
165
|
}
|
|
136
166
|
/**
|
|
137
167
|
* Generate all custom mutation hook files
|
|
168
|
+
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
138
169
|
*/
|
|
139
170
|
function generateAllCustomMutationHooks(options) {
|
|
140
|
-
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
171
|
+
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames } = options;
|
|
141
172
|
return operations
|
|
142
173
|
.filter((op) => op.kind === 'mutation')
|
|
143
174
|
.map((operation) => generateCustomMutationHook({
|
|
@@ -145,5 +176,8 @@ function generateAllCustomMutationHooks(options) {
|
|
|
145
176
|
typeRegistry,
|
|
146
177
|
maxDepth,
|
|
147
178
|
skipQueryField,
|
|
148
|
-
|
|
179
|
+
reactQueryEnabled,
|
|
180
|
+
tableTypeNames,
|
|
181
|
+
}))
|
|
182
|
+
.filter((result) => result !== null);
|
|
149
183
|
}
|
|
@@ -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
|
|
@@ -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, tableTypeNames } = 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');
|
|
@@ -18,6 +18,8 @@ function generateCustomQueryHook(options) {
|
|
|
18
18
|
const resultTypeName = (0, type_resolver_1.getOperationResultTypeName)(operation.name, 'query');
|
|
19
19
|
const documentConstName = (0, type_resolver_1.getDocumentConstName)(operation.name, 'query');
|
|
20
20
|
const queryKeyName = (0, type_resolver_1.getQueryKeyName)(operation.name);
|
|
21
|
+
// Create type tracker to collect referenced types (with table type awareness)
|
|
22
|
+
const tracker = (0, type_resolver_1.createTypeTracker)({ tableTypeNames });
|
|
21
23
|
// Generate GraphQL document
|
|
22
24
|
const queryDocument = (0, schema_gql_ast_1.buildCustomQueryString)({
|
|
23
25
|
operation,
|
|
@@ -27,34 +29,61 @@ function generateCustomQueryHook(options) {
|
|
|
27
29
|
});
|
|
28
30
|
const sourceFile = (0, ts_ast_1.createSourceFile)(project, fileName);
|
|
29
31
|
// Add file header
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const headerText = reactQueryEnabled
|
|
33
|
+
? `Custom query hook for ${operation.name}`
|
|
34
|
+
: `Custom query functions for ${operation.name}`;
|
|
35
|
+
sourceFile.insertText(0, (0, ts_ast_1.createFileHeader)(headerText) + '\n\n');
|
|
36
|
+
// Generate variables interface if there are arguments (with tracking)
|
|
37
|
+
let variablesProps = [];
|
|
38
|
+
if (operation.args.length > 0) {
|
|
39
|
+
variablesProps = generateVariablesProperties(operation.args, tracker);
|
|
40
|
+
}
|
|
41
|
+
// Generate result interface (with tracking)
|
|
42
|
+
const resultType = (0, type_resolver_1.typeRefToTsType)(operation.returnType, tracker);
|
|
43
|
+
const resultProps = [
|
|
44
|
+
{ name: operation.name, type: resultType },
|
|
45
|
+
];
|
|
46
|
+
// Get importable types from tracker (separated by source)
|
|
47
|
+
const schemaTypes = tracker.getImportableTypes(); // From schema-types.ts
|
|
48
|
+
const tableTypes = tracker.getTableTypes(); // From types.ts
|
|
49
|
+
// Add imports - conditionally include React Query imports
|
|
50
|
+
const imports = [];
|
|
51
|
+
if (reactQueryEnabled) {
|
|
52
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
34
53
|
moduleSpecifier: '@tanstack/react-query',
|
|
35
54
|
namedImports: ['useQuery'],
|
|
36
55
|
typeOnlyNamedImports: ['UseQueryOptions', 'QueryClient'],
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
59
|
+
moduleSpecifier: '../client',
|
|
60
|
+
namedImports: ['execute'],
|
|
61
|
+
typeOnlyNamedImports: ['ExecuteOptions'],
|
|
62
|
+
}));
|
|
63
|
+
// Add types.ts import for table entity types
|
|
64
|
+
if (tableTypes.length > 0) {
|
|
65
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
66
|
+
moduleSpecifier: '../types',
|
|
67
|
+
typeOnlyNamedImports: tableTypes,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
// Add schema-types import for Input/Payload/Enum types
|
|
71
|
+
if (schemaTypes.length > 0) {
|
|
72
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
73
|
+
moduleSpecifier: '../schema-types',
|
|
74
|
+
typeOnlyNamedImports: schemaTypes,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
sourceFile.addImportDeclarations(imports);
|
|
44
78
|
// Add query document constant
|
|
45
79
|
sourceFile.addVariableStatement((0, ts_ast_1.createConst)(documentConstName, '`\n' + queryDocument + '`', {
|
|
46
80
|
docs: ['GraphQL query document'],
|
|
47
81
|
}));
|
|
48
|
-
//
|
|
82
|
+
// Add variables interface
|
|
49
83
|
if (operation.args.length > 0) {
|
|
50
|
-
const variablesProps = generateVariablesProperties(operation.args);
|
|
51
84
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(variablesTypeName, variablesProps));
|
|
52
85
|
}
|
|
53
|
-
//
|
|
54
|
-
const resultType = (0, type_resolver_1.typeRefToTsType)(operation.returnType);
|
|
55
|
-
const resultProps = [
|
|
56
|
-
{ name: operation.name, type: resultType },
|
|
57
|
-
];
|
|
86
|
+
// Add result interface
|
|
58
87
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(resultTypeName, resultProps));
|
|
59
88
|
// Query key factory
|
|
60
89
|
if (operation.args.length > 0) {
|
|
@@ -66,17 +95,19 @@ function generateCustomQueryHook(options) {
|
|
|
66
95
|
docs: ['Query key factory for caching'],
|
|
67
96
|
}));
|
|
68
97
|
}
|
|
69
|
-
// Generate hook function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
98
|
+
// Generate hook function (only if React Query is enabled)
|
|
99
|
+
if (reactQueryEnabled) {
|
|
100
|
+
const hookParams = generateHookParameters(operation, variablesTypeName, resultTypeName);
|
|
101
|
+
const hookBody = generateHookBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
|
|
102
|
+
const hookDoc = generateHookDoc(operation, hookName);
|
|
103
|
+
sourceFile.addFunction({
|
|
104
|
+
name: hookName,
|
|
105
|
+
isExported: true,
|
|
106
|
+
parameters: hookParams,
|
|
107
|
+
statements: hookBody,
|
|
108
|
+
docs: [{ description: hookDoc }],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
80
111
|
// Add standalone functions section
|
|
81
112
|
sourceFile.addStatements('\n// ============================================================================');
|
|
82
113
|
sourceFile.addStatements('// Standalone Functions (non-React)');
|
|
@@ -95,20 +126,22 @@ function generateCustomQueryHook(options) {
|
|
|
95
126
|
statements: fetchBody,
|
|
96
127
|
docs: [{ description: fetchDoc }],
|
|
97
128
|
});
|
|
98
|
-
// Generate prefetch function
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
129
|
+
// Generate prefetch function (only if React Query is enabled)
|
|
130
|
+
if (reactQueryEnabled) {
|
|
131
|
+
const prefetchFnName = `prefetch${(0, utils_1.ucFirst)(operation.name)}Query`;
|
|
132
|
+
const prefetchParams = generatePrefetchParameters(operation, variablesTypeName);
|
|
133
|
+
const prefetchBody = generatePrefetchBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
|
|
134
|
+
const prefetchDoc = generatePrefetchDoc(operation, prefetchFnName);
|
|
135
|
+
sourceFile.addFunction({
|
|
136
|
+
name: prefetchFnName,
|
|
137
|
+
isExported: true,
|
|
138
|
+
isAsync: true,
|
|
139
|
+
parameters: prefetchParams,
|
|
140
|
+
returnType: 'Promise<void>',
|
|
141
|
+
statements: prefetchBody,
|
|
142
|
+
docs: [{ description: prefetchDoc }],
|
|
143
|
+
});
|
|
144
|
+
}
|
|
112
145
|
return {
|
|
113
146
|
fileName,
|
|
114
147
|
content: (0, ts_ast_1.getFormattedOutput)(sourceFile),
|
|
@@ -121,10 +154,10 @@ function generateCustomQueryHook(options) {
|
|
|
121
154
|
/**
|
|
122
155
|
* Generate interface properties from CleanArguments
|
|
123
156
|
*/
|
|
124
|
-
function generateVariablesProperties(args) {
|
|
157
|
+
function generateVariablesProperties(args, tracker) {
|
|
125
158
|
return args.map((arg) => ({
|
|
126
159
|
name: arg.name,
|
|
127
|
-
type: (0, type_resolver_1.typeRefToTsType)(arg.type),
|
|
160
|
+
type: (0, type_resolver_1.typeRefToTsType)(arg.type, tracker),
|
|
128
161
|
optional: !(0, type_resolver_1.isTypeRequired)(arg.type),
|
|
129
162
|
docs: arg.description ? [arg.description] : undefined,
|
|
130
163
|
}));
|
|
@@ -346,7 +379,7 @@ await ${fnName}(queryClient);
|
|
|
346
379
|
* Generate all custom query hook files
|
|
347
380
|
*/
|
|
348
381
|
function generateAllCustomQueryHooks(options) {
|
|
349
|
-
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
|
|
382
|
+
const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames } = options;
|
|
350
383
|
return operations
|
|
351
384
|
.filter((op) => op.kind === 'query')
|
|
352
385
|
.map((operation) => generateCustomQueryHook({
|
|
@@ -354,5 +387,7 @@ function generateAllCustomQueryHooks(options) {
|
|
|
354
387
|
typeRegistry,
|
|
355
388
|
maxDepth,
|
|
356
389
|
skipQueryField,
|
|
390
|
+
reactQueryEnabled,
|
|
391
|
+
tableTypeNames,
|
|
357
392
|
}));
|
|
358
393
|
}
|
package/cli/codegen/gql-ast.js
CHANGED
|
@@ -147,16 +147,20 @@ function buildSingleQueryAST(config) {
|
|
|
147
147
|
const { table } = config;
|
|
148
148
|
const queryName = (0, utils_1.getSingleRowQueryName)(table);
|
|
149
149
|
const scalarFields = (0, utils_1.getScalarFields)(table);
|
|
150
|
-
//
|
|
150
|
+
// Get primary key info dynamically from table constraints
|
|
151
|
+
const pkFields = (0, utils_1.getPrimaryKeyInfo)(table);
|
|
152
|
+
// For simplicity, use first PK field (most common case)
|
|
153
|
+
const pkField = pkFields[0];
|
|
154
|
+
// Variable definitions - use dynamic PK field name and type
|
|
151
155
|
const variableDefinitions = [
|
|
152
156
|
t.variableDefinition({
|
|
153
|
-
variable: t.variable({ name:
|
|
154
|
-
type: t.nonNullType({ type: t.namedType({ type:
|
|
157
|
+
variable: t.variable({ name: pkField.name }),
|
|
158
|
+
type: t.nonNullType({ type: t.namedType({ type: pkField.gqlType }) }),
|
|
155
159
|
}),
|
|
156
160
|
];
|
|
157
|
-
// Query arguments
|
|
161
|
+
// Query arguments - use dynamic PK field name
|
|
158
162
|
const args = [
|
|
159
|
-
t.argument({ name:
|
|
163
|
+
t.argument({ name: pkField.name, value: t.variable({ name: pkField.name }) }),
|
|
160
164
|
];
|
|
161
165
|
// Field selections
|
|
162
166
|
const fieldSelections = createFieldSelections(scalarFields);
|