@constructive-io/graphql-codegen 3.2.0 → 3.3.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 +25 -30
- package/cli/index.js +36 -41
- package/cli/shared.d.ts +15 -17
- package/cli/shared.js +113 -21
- package/client/error.js +31 -9
- package/client/execute.js +2 -2
- package/client/index.d.ts +3 -3
- package/client/index.js +6 -6
- package/core/ast.d.ts +1 -1
- package/core/ast.js +1 -1
- package/core/codegen/babel-ast.d.ts +1 -1
- package/core/codegen/babel-ast.js +2 -2
- package/core/codegen/barrel.d.ts +0 -6
- package/core/codegen/barrel.js +22 -19
- package/core/codegen/client.d.ts +2 -12
- package/core/codegen/client.js +7 -21
- package/core/codegen/custom-mutations.d.ts +0 -14
- package/core/codegen/custom-mutations.js +139 -88
- package/core/codegen/custom-queries.d.ts +0 -14
- package/core/codegen/custom-queries.js +483 -193
- package/core/codegen/hooks-ast.d.ts +75 -0
- package/core/codegen/hooks-ast.js +522 -0
- package/core/codegen/index.d.ts +16 -18
- package/core/codegen/index.js +42 -88
- package/core/codegen/invalidation.d.ts +1 -7
- package/core/codegen/invalidation.js +50 -16
- package/core/codegen/mutation-keys.d.ts +1 -10
- package/core/codegen/mutation-keys.js +22 -8
- package/core/codegen/mutations.d.ts +0 -13
- package/core/codegen/mutations.js +301 -366
- package/core/codegen/orm/barrel.d.ts +0 -5
- package/core/codegen/orm/barrel.js +5 -0
- package/core/codegen/orm/client-generator.d.ts +0 -5
- package/core/codegen/orm/client-generator.js +7 -2
- package/core/codegen/orm/client.js +3 -1
- package/core/codegen/orm/custom-ops-generator.d.ts +0 -6
- package/core/codegen/orm/custom-ops-generator.js +104 -51
- package/core/codegen/orm/index.d.ts +4 -4
- package/core/codegen/orm/index.js +28 -15
- package/core/codegen/orm/input-types-generator.d.ts +1 -13
- package/core/codegen/orm/input-types-generator.js +85 -23
- package/core/codegen/orm/model-generator.d.ts +0 -5
- package/core/codegen/orm/model-generator.js +309 -131
- package/core/codegen/orm/select-types.d.ts +19 -14
- package/core/codegen/queries.d.ts +0 -8
- package/core/codegen/queries.js +360 -559
- package/core/codegen/query-keys.d.ts +1 -1
- package/core/codegen/query-keys.js +37 -23
- package/core/codegen/scalars.js +3 -1
- package/core/codegen/schema-types-generator.d.ts +1 -1
- package/core/codegen/schema-types-generator.js +17 -2
- package/core/codegen/select-helpers.d.ts +19 -0
- package/core/codegen/select-helpers.js +40 -0
- package/core/codegen/selection.d.ts +4 -0
- package/core/codegen/selection.js +65 -0
- package/core/codegen/shared/index.d.ts +2 -15
- package/core/codegen/shared/index.js +17 -4
- package/core/codegen/templates/hooks-client.ts +49 -0
- package/core/codegen/templates/hooks-selection.ts +58 -0
- package/core/codegen/templates/orm-client.ts +8 -6
- package/core/codegen/templates/query-builder.ts +250 -46
- package/core/codegen/templates/select-types.ts +31 -14
- package/core/codegen/type-resolver.d.ts +1 -5
- package/core/codegen/type-resolver.js +0 -22
- package/core/codegen/types.d.ts +0 -3
- package/core/codegen/types.js +71 -14
- package/core/codegen/utils.d.ts +1 -4
- package/core/codegen/utils.js +4 -1
- package/core/config/index.d.ts +1 -1
- package/core/config/resolver.js +1 -3
- package/core/generate.js +38 -50
- package/core/index.d.ts +3 -3
- package/core/index.js +3 -4
- package/core/introspect/index.d.ts +6 -6
- package/core/introspect/index.js +5 -8
- package/core/introspect/infer-tables.d.ts +0 -14
- package/core/introspect/infer-tables.js +15 -1
- package/core/introspect/source/database.js +1 -1
- package/core/introspect/source/endpoint.d.ts +0 -6
- package/core/introspect/source/endpoint.js +7 -1
- package/core/introspect/source/index.d.ts +4 -4
- package/core/introspect/source/index.js +5 -9
- package/core/introspect/source/pgpm-module.js +3 -3
- package/core/introspect/transform-schema.d.ts +2 -2
- package/core/introspect/transform-schema.js +2 -2
- package/core/output/index.d.ts +1 -1
- package/core/output/index.js +2 -2
- package/core/output/writer.d.ts +3 -0
- package/core/output/writer.js +20 -1
- package/core/pipeline/index.d.ts +2 -2
- package/core/query-builder.d.ts +2 -2
- package/core/query-builder.js +1 -1
- package/core/watch/index.d.ts +4 -4
- package/core/watch/index.js +9 -9
- package/core/watch/orchestrator.js +5 -3
- package/esm/cli/index.js +37 -42
- package/esm/cli/shared.d.ts +15 -17
- package/esm/cli/shared.js +103 -20
- package/esm/client/error.js +31 -9
- package/esm/client/execute.js +2 -2
- package/esm/client/index.d.ts +3 -3
- package/esm/client/index.js +3 -3
- package/esm/core/ast.d.ts +1 -1
- package/esm/core/ast.js +1 -1
- package/esm/core/codegen/babel-ast.d.ts +1 -1
- package/esm/core/codegen/babel-ast.js +2 -2
- package/esm/core/codegen/barrel.d.ts +0 -6
- package/esm/core/codegen/barrel.js +23 -20
- package/esm/core/codegen/client.d.ts +2 -12
- package/esm/core/codegen/client.js +7 -21
- package/esm/core/codegen/custom-mutations.d.ts +0 -14
- package/esm/core/codegen/custom-mutations.js +141 -90
- package/esm/core/codegen/custom-queries.d.ts +0 -14
- package/esm/core/codegen/custom-queries.js +486 -196
- package/esm/core/codegen/hooks-ast.d.ts +75 -0
- package/esm/core/codegen/hooks-ast.js +424 -0
- package/esm/core/codegen/index.d.ts +16 -18
- package/esm/core/codegen/index.js +26 -71
- package/esm/core/codegen/invalidation.d.ts +1 -7
- package/esm/core/codegen/invalidation.js +51 -17
- package/esm/core/codegen/mutation-keys.d.ts +1 -10
- package/esm/core/codegen/mutation-keys.js +23 -9
- package/esm/core/codegen/mutations.d.ts +0 -13
- package/esm/core/codegen/mutations.js +302 -367
- package/esm/core/codegen/orm/barrel.d.ts +0 -5
- package/esm/core/codegen/orm/barrel.js +6 -1
- package/esm/core/codegen/orm/client-generator.d.ts +0 -5
- package/esm/core/codegen/orm/client-generator.js +7 -2
- package/esm/core/codegen/orm/client.js +3 -1
- package/esm/core/codegen/orm/custom-ops-generator.d.ts +0 -6
- package/esm/core/codegen/orm/custom-ops-generator.js +103 -50
- package/esm/core/codegen/orm/index.d.ts +4 -4
- package/esm/core/codegen/orm/index.js +25 -12
- package/esm/core/codegen/orm/input-types-generator.d.ts +1 -13
- package/esm/core/codegen/orm/input-types-generator.js +85 -23
- package/esm/core/codegen/orm/model-generator.d.ts +0 -5
- package/esm/core/codegen/orm/model-generator.js +310 -132
- package/esm/core/codegen/orm/select-types.d.ts +19 -14
- package/esm/core/codegen/queries.d.ts +0 -8
- package/esm/core/codegen/queries.js +362 -561
- package/esm/core/codegen/query-keys.d.ts +1 -1
- package/esm/core/codegen/query-keys.js +38 -24
- package/esm/core/codegen/scalars.js +3 -1
- package/esm/core/codegen/schema-types-generator.d.ts +1 -1
- package/esm/core/codegen/schema-types-generator.js +17 -2
- package/esm/core/codegen/select-helpers.d.ts +19 -0
- package/esm/core/codegen/select-helpers.js +35 -0
- package/esm/core/codegen/selection.d.ts +4 -0
- package/esm/core/codegen/selection.js +29 -0
- package/esm/core/codegen/shared/index.d.ts +2 -15
- package/esm/core/codegen/shared/index.js +16 -3
- package/esm/core/codegen/type-resolver.d.ts +1 -5
- package/esm/core/codegen/type-resolver.js +1 -22
- package/esm/core/codegen/types.d.ts +0 -3
- package/esm/core/codegen/types.js +72 -15
- package/esm/core/codegen/utils.d.ts +1 -4
- package/esm/core/codegen/utils.js +4 -1
- package/esm/core/config/index.d.ts +1 -1
- package/esm/core/config/resolver.js +2 -4
- package/esm/core/generate.js +38 -50
- package/esm/core/index.d.ts +3 -3
- package/esm/core/index.js +2 -3
- package/esm/core/introspect/index.d.ts +6 -6
- package/esm/core/introspect/index.js +3 -6
- package/esm/core/introspect/infer-tables.d.ts +0 -14
- package/esm/core/introspect/infer-tables.js +16 -2
- package/esm/core/introspect/source/database.js +2 -2
- package/esm/core/introspect/source/endpoint.d.ts +0 -6
- package/esm/core/introspect/source/endpoint.js +7 -1
- package/esm/core/introspect/source/index.d.ts +4 -4
- package/esm/core/introspect/source/index.js +6 -10
- package/esm/core/introspect/source/pgpm-module.js +3 -3
- package/esm/core/introspect/transform-schema.d.ts +2 -2
- package/esm/core/introspect/transform-schema.js +2 -2
- package/esm/core/output/index.d.ts +1 -1
- package/esm/core/output/index.js +1 -1
- package/esm/core/output/writer.d.ts +3 -0
- package/esm/core/output/writer.js +20 -1
- package/esm/core/pipeline/index.d.ts +2 -2
- package/esm/core/pipeline/index.js +2 -2
- package/esm/core/query-builder.d.ts +2 -2
- package/esm/core/query-builder.js +2 -2
- package/esm/core/watch/index.d.ts +4 -4
- package/esm/core/watch/index.js +3 -3
- package/esm/core/watch/orchestrator.js +5 -3
- package/esm/generators/index.d.ts +3 -3
- package/esm/generators/index.js +3 -3
- package/esm/generators/mutations.d.ts +1 -1
- package/esm/generators/select.d.ts +1 -1
- package/esm/index.d.ts +3 -3
- package/esm/index.js +1 -4
- package/esm/types/config.d.ts +0 -10
- package/esm/types/config.js +0 -2
- package/esm/types/index.d.ts +6 -6
- package/esm/types/index.js +1 -1
- package/generators/index.d.ts +3 -3
- package/generators/index.js +8 -8
- package/generators/mutations.d.ts +1 -1
- package/generators/select.d.ts +1 -1
- package/index.d.ts +3 -3
- package/index.js +11 -5
- package/package.json +11 -11
- package/types/config.d.ts +0 -10
- package/types/config.js +0 -2
- package/types/index.d.ts +6 -6
- package/types/index.js +2 -2
- package/core/codegen/gql-ast.d.ts +0 -41
- package/core/codegen/gql-ast.js +0 -353
- package/core/codegen/schema-gql-ast.d.ts +0 -51
- package/core/codegen/schema-gql-ast.js +0 -385
- package/core/codegen/templates/client.browser.ts +0 -271
- package/core/codegen/templates/client.node.ts +0 -337
- package/esm/core/codegen/gql-ast.d.ts +0 -41
- package/esm/core/codegen/gql-ast.js +0 -312
- package/esm/core/codegen/schema-gql-ast.d.ts +0 -51
- package/esm/core/codegen/schema-gql-ast.js +0 -343
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import generate from '@babel/generator';
|
|
9
9
|
import * as t from '@babel/types';
|
|
10
10
|
// Re-export for convenience
|
|
11
|
-
export {
|
|
11
|
+
export { generate, t };
|
|
12
12
|
/**
|
|
13
13
|
* Generate code from an array of statements
|
|
14
14
|
*/
|
|
@@ -47,7 +47,7 @@ export const commentLine = (value) => {
|
|
|
47
47
|
export function addJSDocComment(node, lines) {
|
|
48
48
|
const commentText = lines.length === 1
|
|
49
49
|
? `* ${lines[0]} `
|
|
50
|
-
: `*\n${lines.map(line => ` * ${line}`).join('\n')}\n `;
|
|
50
|
+
: `*\n${lines.map((line) => ` * ${line}`).join('\n')}\n `;
|
|
51
51
|
if (!node.leadingComments) {
|
|
52
52
|
node.leadingComments = [];
|
|
53
53
|
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Barrel file generators - creates index.ts files for exports
|
|
3
|
-
*
|
|
4
|
-
* Using Babel AST for generating barrel (index.ts) files with re-exports.
|
|
5
|
-
*/
|
|
6
1
|
import type { CleanTable } from '../../types/schema';
|
|
7
2
|
/**
|
|
8
3
|
* Generate the queries/index.ts barrel file
|
|
@@ -19,7 +14,6 @@ export declare function generateMutationsBarrel(tables: CleanTable[]): string;
|
|
|
19
14
|
* @param hasSchemaTypes - Whether schema-types.ts was generated
|
|
20
15
|
*/
|
|
21
16
|
export interface MainBarrelOptions {
|
|
22
|
-
hasSchemaTypes?: boolean;
|
|
23
17
|
hasMutations?: boolean;
|
|
24
18
|
/** Whether query-keys.ts was generated */
|
|
25
19
|
hasQueryKeys?: boolean;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel file generators - creates index.ts files for exports
|
|
3
|
+
*
|
|
4
|
+
* Using Babel AST for generating barrel (index.ts) files with re-exports.
|
|
5
|
+
*/
|
|
1
6
|
import * as t from '@babel/types';
|
|
2
|
-
import {
|
|
3
|
-
import { getListQueryHookName, getSingleQueryHookName, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, hasValidPrimaryKey, } from './utils';
|
|
7
|
+
import { addJSDocComment, generateCode } from './babel-ast';
|
|
4
8
|
import { getOperationHookName } from './type-resolver';
|
|
9
|
+
import { getCreateMutationHookName, getDeleteMutationHookName, getListQueryHookName, getSingleQueryHookName, getUpdateMutationHookName, hasValidPrimaryKey, } from './utils';
|
|
5
10
|
/**
|
|
6
11
|
* Helper to create export * from './module' statement
|
|
7
12
|
*/
|
|
@@ -41,14 +46,14 @@ export function generateMutationsBarrel(tables) {
|
|
|
41
46
|
// Export all mutation hooks
|
|
42
47
|
for (const table of tables) {
|
|
43
48
|
const createHookName = getCreateMutationHookName(table);
|
|
44
|
-
const updateHookName = getUpdateMutationHookName(table);
|
|
45
|
-
const deleteHookName = getDeleteMutationHookName(table);
|
|
46
49
|
statements.push(exportAllFrom(`./${createHookName}`));
|
|
47
|
-
// Only add update/delete if they exist
|
|
48
|
-
if (table.query?.update !== null) {
|
|
50
|
+
// Only add update/delete if they exist AND table has valid PK
|
|
51
|
+
if (table.query?.update !== null && hasValidPrimaryKey(table)) {
|
|
52
|
+
const updateHookName = getUpdateMutationHookName(table);
|
|
49
53
|
statements.push(exportAllFrom(`./${updateHookName}`));
|
|
50
54
|
}
|
|
51
|
-
if (table.query?.delete !== null) {
|
|
55
|
+
if (table.query?.delete !== null && hasValidPrimaryKey(table)) {
|
|
56
|
+
const deleteHookName = getDeleteMutationHookName(table);
|
|
52
57
|
statements.push(exportAllFrom(`./${deleteHookName}`));
|
|
53
58
|
}
|
|
54
59
|
}
|
|
@@ -64,17 +69,11 @@ export function generateMutationsBarrel(tables) {
|
|
|
64
69
|
}
|
|
65
70
|
export function generateMainBarrel(tables, options = {}) {
|
|
66
71
|
const opts = options;
|
|
67
|
-
const {
|
|
72
|
+
const { hasMutations = true, hasQueryKeys = false, hasMutationKeys = false, hasInvalidation = false, } = opts;
|
|
68
73
|
const tableNames = tables.map((tbl) => tbl.name).join(', ');
|
|
69
74
|
const statements = [];
|
|
70
|
-
// Client configuration
|
|
75
|
+
// Client configuration (ORM wrapper with configure/getClient)
|
|
71
76
|
statements.push(exportAllFrom('./client'));
|
|
72
|
-
// Entity and filter types
|
|
73
|
-
statements.push(exportAllFrom('./types'));
|
|
74
|
-
// Schema types (input, payload, enum types)
|
|
75
|
-
if (hasSchemaTypes) {
|
|
76
|
-
statements.push(exportAllFrom('./schema-types'));
|
|
77
|
-
}
|
|
78
77
|
// Centralized query keys (for cache management)
|
|
79
78
|
if (hasQueryKeys) {
|
|
80
79
|
statements.push(exportAllFrom('./query-keys'));
|
|
@@ -118,8 +117,12 @@ export function generateMainBarrel(tables, options = {}) {
|
|
|
118
117
|
"import { useCarsQuery, useCreateCarMutation } from './generated';",
|
|
119
118
|
'',
|
|
120
119
|
'function MyComponent() {',
|
|
121
|
-
' const { data, isLoading } = useCarsQuery({
|
|
122
|
-
'
|
|
120
|
+
' const { data, isLoading } = useCarsQuery({',
|
|
121
|
+
' selection: { fields: { id: true }, first: 10 },',
|
|
122
|
+
' });',
|
|
123
|
+
' const { mutate } = useCreateCarMutation({',
|
|
124
|
+
' selection: { fields: { id: true } },',
|
|
125
|
+
' });',
|
|
123
126
|
' // ...',
|
|
124
127
|
'}',
|
|
125
128
|
'```',
|
|
@@ -208,15 +211,15 @@ export function generateCustomMutationsBarrel(tables, customMutationNames) {
|
|
|
208
211
|
statements.push(exportAllFrom(`./${createHookName}`));
|
|
209
212
|
exportedHooks.add(createHookName);
|
|
210
213
|
}
|
|
211
|
-
// Only add update/delete if they exist
|
|
212
|
-
if (table.query?.update !== null) {
|
|
214
|
+
// Only add update/delete if they exist AND table has valid PK
|
|
215
|
+
if (table.query?.update !== null && hasValidPrimaryKey(table)) {
|
|
213
216
|
const updateHookName = getUpdateMutationHookName(table);
|
|
214
217
|
if (!exportedHooks.has(updateHookName)) {
|
|
215
218
|
statements.push(exportAllFrom(`./${updateHookName}`));
|
|
216
219
|
exportedHooks.add(updateHookName);
|
|
217
220
|
}
|
|
218
221
|
}
|
|
219
|
-
if (table.query?.delete !== null) {
|
|
222
|
+
if (table.query?.delete !== null && hasValidPrimaryKey(table)) {
|
|
220
223
|
const deleteHookName = getDeleteMutationHookName(table);
|
|
221
224
|
if (!exportedHooks.has(deleteHookName)) {
|
|
222
225
|
statements.push(exportAllFrom(`./${deleteHookName}`));
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
export interface GenerateClientFileOptions {
|
|
2
|
-
/**
|
|
3
|
-
* Generate browser-compatible code using native fetch
|
|
4
|
-
* When true (default), uses native W3C fetch API
|
|
5
|
-
* When false, uses undici fetch with dispatcher support for localhost DNS resolution
|
|
6
|
-
* @default true
|
|
7
|
-
*/
|
|
8
|
-
browserCompatible?: boolean;
|
|
9
|
-
}
|
|
10
1
|
/**
|
|
11
|
-
* Generate client.ts content
|
|
12
|
-
* @param options - Generation options
|
|
2
|
+
* Generate client.ts content - ORM client wrapper with configure/getClient
|
|
13
3
|
*/
|
|
14
|
-
export declare function generateClientFile(
|
|
4
|
+
export declare function generateClientFile(): string;
|
|
@@ -1,43 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Client generator - generates client.ts
|
|
2
|
+
* Client generator - generates client.ts as ORM client wrapper
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Uses template-copy pattern: reads hooks-client.ts from templates/
|
|
5
|
+
* and writes it to the output directory with a generated file header.
|
|
5
6
|
*/
|
|
6
7
|
import * as fs from 'fs';
|
|
7
8
|
import * as path from 'path';
|
|
8
9
|
import { getGeneratedFileHeader } from './utils';
|
|
9
|
-
/**
|
|
10
|
-
* Find a template file path.
|
|
11
|
-
* Templates are at ./templates/ relative to this file in both src/ and dist/.
|
|
12
|
-
*/
|
|
13
10
|
function findTemplateFile(templateName) {
|
|
14
11
|
const templatePath = path.join(__dirname, 'templates', templateName);
|
|
15
12
|
if (fs.existsSync(templatePath)) {
|
|
16
13
|
return templatePath;
|
|
17
14
|
}
|
|
18
|
-
throw new Error(`Could not find template file: ${templateName}. `
|
|
19
|
-
`Searched in: ${templatePath}`);
|
|
15
|
+
throw new Error(`Could not find template file: ${templateName}. Searched in: ${templatePath}`);
|
|
20
16
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Read a template file and replace the header with generated file header
|
|
23
|
-
*/
|
|
24
17
|
function readTemplateFile(templateName, description) {
|
|
25
18
|
const templatePath = findTemplateFile(templateName);
|
|
26
19
|
let content = fs.readFileSync(templatePath, 'utf-8');
|
|
27
|
-
// Replace the source file header comment with the generated file header
|
|
28
|
-
// Match the header pattern used in template files
|
|
29
20
|
const headerPattern = /\/\*\*[\s\S]*?\* NOTE: This file is read at codegen time and written to output\.[\s\S]*?\*\/\n*/;
|
|
30
21
|
content = content.replace(headerPattern, getGeneratedFileHeader(description) + '\n');
|
|
31
22
|
return content;
|
|
32
23
|
}
|
|
33
24
|
/**
|
|
34
|
-
* Generate client.ts content
|
|
35
|
-
* @param options - Generation options
|
|
25
|
+
* Generate client.ts content - ORM client wrapper with configure/getClient
|
|
36
26
|
*/
|
|
37
|
-
export function generateClientFile(
|
|
38
|
-
|
|
39
|
-
const templateName = browserCompatible
|
|
40
|
-
? 'client.browser.ts'
|
|
41
|
-
: 'client.node.ts';
|
|
42
|
-
return readTemplateFile(templateName, 'GraphQL client configuration and execution');
|
|
27
|
+
export function generateClientFile() {
|
|
28
|
+
return readTemplateFile('hooks-client.ts', 'ORM client wrapper for React Query hooks');
|
|
43
29
|
}
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom mutation hook generators for non-table operations
|
|
3
|
-
*
|
|
4
|
-
* Generates hooks for operations discovered via schema introspection
|
|
5
|
-
* that are NOT table CRUD operations (e.g., login, register, etc.)
|
|
6
|
-
*
|
|
7
|
-
* Output structure:
|
|
8
|
-
* mutations/
|
|
9
|
-
* useLoginMutation.ts
|
|
10
|
-
* useRegisterMutation.ts
|
|
11
|
-
* ...
|
|
12
|
-
*/
|
|
13
1
|
import type { CleanOperation, TypeRegistry } from '../../types/schema';
|
|
14
2
|
export interface GeneratedCustomMutationFile {
|
|
15
3
|
fileName: string;
|
|
@@ -19,7 +7,6 @@ export interface GeneratedCustomMutationFile {
|
|
|
19
7
|
export interface GenerateCustomMutationHookOptions {
|
|
20
8
|
operation: CleanOperation;
|
|
21
9
|
typeRegistry: TypeRegistry;
|
|
22
|
-
maxDepth?: number;
|
|
23
10
|
skipQueryField?: boolean;
|
|
24
11
|
reactQueryEnabled?: boolean;
|
|
25
12
|
tableTypeNames?: Set<string>;
|
|
@@ -29,7 +16,6 @@ export declare function generateCustomMutationHook(options: GenerateCustomMutati
|
|
|
29
16
|
export interface GenerateAllCustomMutationHooksOptions {
|
|
30
17
|
operations: CleanOperation[];
|
|
31
18
|
typeRegistry: TypeRegistry;
|
|
32
|
-
maxDepth?: number;
|
|
33
19
|
skipQueryField?: boolean;
|
|
34
20
|
reactQueryEnabled?: boolean;
|
|
35
21
|
tableTypeNames?: Set<string>;
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom mutation hook generators for non-table operations (Babel AST-based)
|
|
3
|
+
*
|
|
4
|
+
* Generates hooks for operations discovered via schema introspection
|
|
5
|
+
* that are NOT table CRUD operations (e.g., login, register, etc.)
|
|
6
|
+
*
|
|
7
|
+
* Delegates to ORM custom mutation operations:
|
|
8
|
+
* getClient().mutation.operationName(args, { select }).unwrap()
|
|
9
|
+
*
|
|
10
|
+
* Output structure:
|
|
11
|
+
* mutations/
|
|
12
|
+
* useLoginMutation.ts
|
|
13
|
+
* useRegisterMutation.ts
|
|
14
|
+
* ...
|
|
15
|
+
*/
|
|
1
16
|
import * as t from '@babel/types';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
function generateVariablesProperties(args, tracker) {
|
|
7
|
-
return args.map((arg) => ({
|
|
8
|
-
name: arg.name,
|
|
9
|
-
type: typeRefToTsType(arg.type, tracker),
|
|
10
|
-
optional: !isTypeRequired(arg.type),
|
|
11
|
-
docs: arg.description ? [arg.description] : undefined,
|
|
12
|
-
}));
|
|
13
|
-
}
|
|
17
|
+
import { buildSelectionArgsCall, callExpr, constDecl, createFunctionParam, createImportDeclaration, createSTypeParam, createTypeReExport, customSelectResultTypeLiteral, destructureParamsWithSelection, exportDeclareFunction, exportFunction, generateHookFileCode, getClientCustomCallUnwrap, objectProp, omitType, returnUseMutation, selectionConfigType, spreadObj, sRef, typeRef, useMutationOptionsType, useMutationResultType, voidStatement, } from './hooks-ast';
|
|
18
|
+
import { getSelectTypeName } from './select-helpers';
|
|
19
|
+
import { createTypeTracker, getOperationFileName, getOperationHookName, getTypeBaseName, typeRefToTsType, } from './type-resolver';
|
|
20
|
+
import { ucFirst } from './utils';
|
|
14
21
|
export function generateCustomMutationHook(options) {
|
|
15
22
|
const { operation, reactQueryEnabled = true } = options;
|
|
16
23
|
if (!reactQueryEnabled) {
|
|
@@ -26,95 +33,140 @@ export function generateCustomMutationHook(options) {
|
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
35
|
function generateCustomMutationHookInternal(options) {
|
|
29
|
-
const { operation, typeRegistry,
|
|
36
|
+
const { operation, typeRegistry, tableTypeNames, useCentralizedKeys = true, } = options;
|
|
30
37
|
const hookName = getOperationHookName(operation.name, 'mutation');
|
|
31
38
|
const fileName = getOperationFileName(operation.name, 'mutation');
|
|
32
|
-
const
|
|
33
|
-
const resultTypeName = getOperationResultTypeName(operation.name, 'mutation');
|
|
34
|
-
const documentConstName = getDocumentConstName(operation.name, 'mutation');
|
|
39
|
+
const varTypeName = `${ucFirst(operation.name)}Variables`;
|
|
35
40
|
const tracker = createTypeTracker({ tableTypeNames });
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
const hasArgs = operation.args.length > 0;
|
|
42
|
+
typeRefToTsType(operation.returnType, tracker);
|
|
43
|
+
for (const arg of operation.args) {
|
|
44
|
+
typeRefToTsType(arg.type, tracker);
|
|
45
|
+
}
|
|
46
|
+
const selectTypeName = getSelectTypeName(operation.returnType);
|
|
47
|
+
const payloadTypeName = getTypeBaseName(operation.returnType);
|
|
48
|
+
const hasSelect = !!selectTypeName && !!payloadTypeName;
|
|
42
49
|
const statements = [];
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const reactQueryTypeImport = t.importDeclaration([t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], t.stringLiteral('@tanstack/react-query'));
|
|
52
|
-
reactQueryTypeImport.importKind = 'type';
|
|
53
|
-
statements.push(reactQueryTypeImport);
|
|
54
|
-
const clientImport = t.importDeclaration([t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], t.stringLiteral('../client'));
|
|
55
|
-
statements.push(clientImport);
|
|
56
|
-
if (tableTypes.length > 0) {
|
|
57
|
-
const typesImport = t.importDeclaration(tableTypes.map((tt) => t.importSpecifier(t.identifier(tt), t.identifier(tt))), t.stringLiteral('../types'));
|
|
58
|
-
typesImport.importKind = 'type';
|
|
59
|
-
statements.push(typesImport);
|
|
50
|
+
// Imports
|
|
51
|
+
statements.push(createImportDeclaration('@tanstack/react-query', ['useMutation']));
|
|
52
|
+
statements.push(createImportDeclaration('@tanstack/react-query', ['UseMutationOptions', 'UseMutationResult'], true));
|
|
53
|
+
statements.push(createImportDeclaration('../client', ['getClient']));
|
|
54
|
+
statements.push(createImportDeclaration('../selection', ['buildSelectionArgs']));
|
|
55
|
+
statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true));
|
|
56
|
+
if (useCentralizedKeys) {
|
|
57
|
+
statements.push(createImportDeclaration('../mutation-keys', ['customMutationKeys']));
|
|
60
58
|
}
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
schemaTypesImport.importKind = 'type';
|
|
64
|
-
statements.push(schemaTypesImport);
|
|
59
|
+
if (hasArgs) {
|
|
60
|
+
statements.push(createImportDeclaration('../../orm/mutation', [varTypeName], true));
|
|
65
61
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
const inputTypeImports = [];
|
|
63
|
+
if (hasSelect) {
|
|
64
|
+
inputTypeImports.push(selectTypeName);
|
|
65
|
+
inputTypeImports.push(payloadTypeName);
|
|
69
66
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (operation.args.length > 0) {
|
|
77
|
-
const variablesInterfaceProps = variablesProps.map((vp) => {
|
|
78
|
-
const prop = t.tsPropertySignature(t.identifier(vp.name), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(vp.type))));
|
|
79
|
-
prop.optional = vp.optional;
|
|
80
|
-
return prop;
|
|
81
|
-
});
|
|
82
|
-
const variablesInterface = t.tsInterfaceDeclaration(t.identifier(variablesTypeName), null, null, t.tsInterfaceBody(variablesInterfaceProps));
|
|
83
|
-
statements.push(t.exportNamedDeclaration(variablesInterface));
|
|
67
|
+
else {
|
|
68
|
+
for (const refType of tracker.referencedTypes) {
|
|
69
|
+
if (!inputTypeImports.includes(refType)) {
|
|
70
|
+
inputTypeImports.push(refType);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
84
73
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
]);
|
|
88
|
-
const resultInterface = t.tsInterfaceDeclaration(t.identifier(resultTypeName), null, null, resultInterfaceBody);
|
|
89
|
-
statements.push(t.exportNamedDeclaration(resultInterface));
|
|
90
|
-
const hasArgs = operation.args.length > 0;
|
|
91
|
-
const hookBodyStatements = [];
|
|
92
|
-
const mutationOptions = [];
|
|
93
|
-
if (useCentralizedKeys) {
|
|
94
|
-
mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.callExpression(t.memberExpression(t.identifier('customMutationKeys'), t.identifier(operation.name)), [])));
|
|
74
|
+
if (inputTypeImports.length > 0) {
|
|
75
|
+
statements.push(createImportDeclaration('../../orm/input-types', inputTypeImports, true));
|
|
95
76
|
}
|
|
77
|
+
if (hasSelect) {
|
|
78
|
+
statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true));
|
|
79
|
+
}
|
|
80
|
+
// Re-exports
|
|
96
81
|
if (hasArgs) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
]))
|
|
82
|
+
statements.push(createTypeReExport([varTypeName], '../../orm/mutation'));
|
|
83
|
+
}
|
|
84
|
+
if (hasSelect) {
|
|
85
|
+
statements.push(createTypeReExport([selectTypeName], '../../orm/input-types'));
|
|
86
|
+
}
|
|
87
|
+
// Hook
|
|
88
|
+
if (hasSelect) {
|
|
89
|
+
const mutationVarType = hasArgs
|
|
90
|
+
? typeRef(varTypeName)
|
|
91
|
+
: t.tsVoidKeyword();
|
|
92
|
+
const selectedResultType = (sel) => customSelectResultTypeLiteral(operation.name, operation.returnType, payloadTypeName, sel);
|
|
93
|
+
// Overload 1: with selection.fields
|
|
94
|
+
const o1ParamType = t.tsIntersectionType([
|
|
95
|
+
t.tsTypeLiteral([
|
|
96
|
+
t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(t.tsParenthesizedType(t.tsIntersectionType([
|
|
97
|
+
t.tsTypeLiteral([
|
|
98
|
+
t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(sRef())),
|
|
99
|
+
]),
|
|
100
|
+
typeRef('StrictSelect', [sRef(), typeRef(selectTypeName)]),
|
|
101
|
+
])))),
|
|
102
|
+
]),
|
|
103
|
+
useMutationOptionsType(selectedResultType(sRef()), mutationVarType),
|
|
104
|
+
]);
|
|
105
|
+
statements.push(exportDeclareFunction(hookName, createSTypeParam(selectTypeName), [createFunctionParam('params', o1ParamType)], useMutationResultType(selectedResultType(sRef()), mutationVarType)));
|
|
106
|
+
// Implementation
|
|
107
|
+
const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))));
|
|
108
|
+
const implParamType = t.tsIntersectionType([
|
|
109
|
+
t.tsTypeLiteral([implSelProp]),
|
|
110
|
+
omitType(typeRef('UseMutationOptions', [
|
|
111
|
+
t.tsAnyKeyword(),
|
|
112
|
+
typeRef('Error'),
|
|
113
|
+
mutationVarType,
|
|
114
|
+
]), ['mutationFn']),
|
|
115
|
+
]);
|
|
116
|
+
const body = [];
|
|
117
|
+
body.push(buildSelectionArgsCall(selectTypeName));
|
|
118
|
+
body.push(destructureParamsWithSelection('mutationOptions'));
|
|
119
|
+
body.push(voidStatement('_selection'));
|
|
120
|
+
const mutationKeyExpr = useCentralizedKeys
|
|
121
|
+
? callExpr(t.memberExpression(t.identifier('customMutationKeys'), t.identifier(operation.name)), [])
|
|
122
|
+
: undefined;
|
|
123
|
+
const selectArgExpr = t.objectExpression([
|
|
124
|
+
objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
|
|
125
|
+
]);
|
|
126
|
+
let mutationFnExpr;
|
|
127
|
+
if (hasArgs) {
|
|
128
|
+
const variablesParam = createFunctionParam('variables', typeRef(varTypeName));
|
|
129
|
+
mutationFnExpr = t.arrowFunctionExpression([variablesParam], getClientCustomCallUnwrap('mutation', operation.name, [t.identifier('variables')], selectArgExpr));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
mutationFnExpr = t.arrowFunctionExpression([], getClientCustomCallUnwrap('mutation', operation.name, [], selectArgExpr));
|
|
133
|
+
}
|
|
134
|
+
body.push(returnUseMutation(mutationFnExpr, [spreadObj(t.identifier('mutationOptions'))], mutationKeyExpr));
|
|
135
|
+
statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body));
|
|
101
136
|
}
|
|
102
137
|
else {
|
|
103
|
-
|
|
138
|
+
// Without select: simple hook (scalar return type)
|
|
139
|
+
const resultTypeStr = typeRefToTsType(operation.returnType, tracker);
|
|
140
|
+
const resultTypeLiteral = t.tsTypeLiteral([
|
|
141
|
+
t.tsPropertySignature(t.identifier(operation.name), t.tsTypeAnnotation(typeRef(resultTypeStr))),
|
|
142
|
+
]);
|
|
143
|
+
const mutationVarType = hasArgs
|
|
144
|
+
? typeRef(varTypeName)
|
|
145
|
+
: t.tsVoidKeyword();
|
|
146
|
+
const optionsType = omitType(typeRef('UseMutationOptions', [
|
|
147
|
+
resultTypeLiteral,
|
|
148
|
+
typeRef('Error'),
|
|
149
|
+
mutationVarType,
|
|
150
|
+
]), ['mutationFn']);
|
|
151
|
+
const body = [];
|
|
152
|
+
body.push(constDecl('mutationOptions', t.logicalExpression('??', t.identifier('params'), t.objectExpression([]))));
|
|
153
|
+
const mutationKeyExpr = useCentralizedKeys
|
|
154
|
+
? callExpr(t.memberExpression(t.identifier('customMutationKeys'), t.identifier(operation.name)), [])
|
|
155
|
+
: undefined;
|
|
156
|
+
let mutationFnExpr;
|
|
157
|
+
if (hasArgs) {
|
|
158
|
+
const variablesParam = createFunctionParam('variables', typeRef(varTypeName));
|
|
159
|
+
mutationFnExpr = t.arrowFunctionExpression([variablesParam], getClientCustomCallUnwrap('mutation', operation.name, [
|
|
160
|
+
t.identifier('variables'),
|
|
161
|
+
]));
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
mutationFnExpr = t.arrowFunctionExpression([], getClientCustomCallUnwrap('mutation', operation.name, []));
|
|
165
|
+
}
|
|
166
|
+
body.push(returnUseMutation(mutationFnExpr, [spreadObj(t.identifier('mutationOptions'))], mutationKeyExpr));
|
|
167
|
+
statements.push(exportFunction(hookName, null, [createFunctionParam('params', optionsType, true)], body));
|
|
104
168
|
}
|
|
105
|
-
|
|
106
|
-
hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)])));
|
|
107
|
-
const optionsType = hasArgs
|
|
108
|
-
? `Omit<UseMutationOptions<${resultTypeName}, Error, ${variablesTypeName}>, 'mutationFn'>`
|
|
109
|
-
: `Omit<UseMutationOptions<${resultTypeName}, Error, void>, 'mutationFn'>`;
|
|
110
|
-
const optionsParam = t.identifier('options');
|
|
111
|
-
optionsParam.optional = true;
|
|
112
|
-
optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsType)));
|
|
113
|
-
const hookFunc = t.functionDeclaration(t.identifier(hookName), [optionsParam], t.blockStatement(hookBodyStatements));
|
|
114
|
-
const hookExport = t.exportNamedDeclaration(hookFunc);
|
|
115
|
-
statements.push(hookExport);
|
|
116
|
-
const code = generateCode(statements);
|
|
117
|
-
const content = getGeneratedFileHeader(`Custom mutation hook for ${operation.name}`) + '\n\n' + code;
|
|
169
|
+
const content = generateHookFileCode(`Custom mutation hook for ${operation.name}`, statements);
|
|
118
170
|
return {
|
|
119
171
|
fileName,
|
|
120
172
|
content,
|
|
@@ -122,13 +174,12 @@ function generateCustomMutationHookInternal(options) {
|
|
|
122
174
|
};
|
|
123
175
|
}
|
|
124
176
|
export function generateAllCustomMutationHooks(options) {
|
|
125
|
-
const { operations, typeRegistry,
|
|
177
|
+
const { operations, typeRegistry, skipQueryField = true, reactQueryEnabled = true, tableTypeNames, useCentralizedKeys = true, } = options;
|
|
126
178
|
return operations
|
|
127
179
|
.filter((op) => op.kind === 'mutation')
|
|
128
180
|
.map((operation) => generateCustomMutationHook({
|
|
129
181
|
operation,
|
|
130
182
|
typeRegistry,
|
|
131
|
-
maxDepth,
|
|
132
183
|
skipQueryField,
|
|
133
184
|
reactQueryEnabled,
|
|
134
185
|
tableTypeNames,
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom query hook generators for non-table operations
|
|
3
|
-
*
|
|
4
|
-
* Generates hooks for operations discovered via schema introspection
|
|
5
|
-
* that are NOT table CRUD operations (e.g., currentUser, nodeById, etc.)
|
|
6
|
-
*
|
|
7
|
-
* Output structure:
|
|
8
|
-
* queries/
|
|
9
|
-
* useCurrentUserQuery.ts
|
|
10
|
-
* useNodeQuery.ts
|
|
11
|
-
* ...
|
|
12
|
-
*/
|
|
13
1
|
import type { CleanOperation, TypeRegistry } from '../../types/schema';
|
|
14
2
|
export interface GeneratedCustomQueryFile {
|
|
15
3
|
fileName: string;
|
|
@@ -19,7 +7,6 @@ export interface GeneratedCustomQueryFile {
|
|
|
19
7
|
export interface GenerateCustomQueryHookOptions {
|
|
20
8
|
operation: CleanOperation;
|
|
21
9
|
typeRegistry: TypeRegistry;
|
|
22
|
-
maxDepth?: number;
|
|
23
10
|
skipQueryField?: boolean;
|
|
24
11
|
reactQueryEnabled?: boolean;
|
|
25
12
|
tableTypeNames?: Set<string>;
|
|
@@ -29,7 +16,6 @@ export declare function generateCustomQueryHook(options: GenerateCustomQueryHook
|
|
|
29
16
|
export interface GenerateAllCustomQueryHooksOptions {
|
|
30
17
|
operations: CleanOperation[];
|
|
31
18
|
typeRegistry: TypeRegistry;
|
|
32
|
-
maxDepth?: number;
|
|
33
19
|
skipQueryField?: boolean;
|
|
34
20
|
reactQueryEnabled?: boolean;
|
|
35
21
|
tableTypeNames?: Set<string>;
|