@constructive-io/graphql-codegen 2.23.3 → 2.24.1
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 +147 -2
- package/cli/codegen/babel-ast.d.ts +53 -0
- package/cli/codegen/babel-ast.js +160 -0
- package/cli/codegen/barrel.d.ts +7 -2
- package/cli/codegen/barrel.js +193 -102
- package/cli/codegen/client.js +61 -0
- package/cli/codegen/custom-mutations.d.ts +2 -12
- package/cli/codegen/custom-mutations.js +116 -124
- package/cli/codegen/custom-queries.d.ts +2 -10
- package/cli/codegen/custom-queries.js +236 -335
- package/cli/codegen/gql-ast.js +22 -1
- package/cli/codegen/index.d.ts +3 -0
- package/cli/codegen/index.js +73 -3
- package/cli/codegen/invalidation.d.ts +20 -0
- package/cli/codegen/invalidation.js +327 -0
- package/cli/codegen/mutation-keys.d.ts +24 -0
- package/cli/codegen/mutation-keys.js +247 -0
- package/cli/codegen/mutations.d.ts +5 -19
- package/cli/codegen/mutations.js +385 -383
- package/cli/codegen/orm/barrel.d.ts +1 -1
- package/cli/codegen/orm/barrel.js +42 -10
- package/cli/codegen/orm/client-generator.d.ts +1 -19
- package/cli/codegen/orm/client-generator.js +108 -77
- package/cli/codegen/orm/custom-ops-generator.d.ts +1 -12
- package/cli/codegen/orm/custom-ops-generator.js +192 -235
- package/cli/codegen/orm/input-types-generator.d.ts +13 -1
- package/cli/codegen/orm/input-types-generator.js +425 -147
- package/cli/codegen/orm/model-generator.d.ts +1 -19
- package/cli/codegen/orm/model-generator.js +229 -234
- package/cli/codegen/queries.d.ts +4 -12
- package/cli/codegen/queries.js +660 -390
- package/cli/codegen/query-keys.d.ts +15 -0
- package/cli/codegen/query-keys.js +477 -0
- package/cli/codegen/scalars.js +1 -0
- package/cli/codegen/schema-types-generator.d.ts +15 -10
- package/cli/codegen/schema-types-generator.js +87 -175
- package/cli/codegen/type-resolver.d.ts +1 -30
- package/cli/codegen/type-resolver.js +0 -53
- package/cli/codegen/types.d.ts +1 -1
- package/cli/codegen/types.js +76 -21
- package/cli/codegen/utils.d.ts +6 -0
- package/cli/codegen/utils.js +19 -0
- package/esm/cli/codegen/babel-ast.d.ts +53 -0
- package/esm/cli/codegen/babel-ast.js +111 -0
- package/esm/cli/codegen/barrel.d.ts +7 -2
- package/esm/cli/codegen/barrel.js +161 -103
- package/esm/cli/codegen/client.js +61 -0
- package/esm/cli/codegen/custom-mutations.d.ts +2 -12
- package/esm/cli/codegen/custom-mutations.js +83 -124
- package/esm/cli/codegen/custom-queries.d.ts +2 -10
- package/esm/cli/codegen/custom-queries.js +204 -336
- package/esm/cli/codegen/gql-ast.js +23 -2
- package/esm/cli/codegen/index.d.ts +3 -0
- package/esm/cli/codegen/index.js +69 -2
- package/esm/cli/codegen/invalidation.d.ts +20 -0
- package/esm/cli/codegen/invalidation.js +291 -0
- package/esm/cli/codegen/mutation-keys.d.ts +24 -0
- package/esm/cli/codegen/mutation-keys.js +211 -0
- package/esm/cli/codegen/mutations.d.ts +5 -19
- package/esm/cli/codegen/mutations.js +353 -384
- package/esm/cli/codegen/orm/barrel.d.ts +1 -1
- package/esm/cli/codegen/orm/barrel.js +10 -11
- package/esm/cli/codegen/orm/client-generator.d.ts +1 -19
- package/esm/cli/codegen/orm/client-generator.js +76 -78
- package/esm/cli/codegen/orm/custom-ops-generator.d.ts +1 -12
- package/esm/cli/codegen/orm/custom-ops-generator.js +160 -236
- package/esm/cli/codegen/orm/input-types-generator.d.ts +13 -1
- package/esm/cli/codegen/orm/input-types-generator.js +393 -148
- package/esm/cli/codegen/orm/model-generator.d.ts +1 -19
- package/esm/cli/codegen/orm/model-generator.js +197 -235
- package/esm/cli/codegen/queries.d.ts +4 -12
- package/esm/cli/codegen/queries.js +628 -391
- package/esm/cli/codegen/query-keys.d.ts +15 -0
- package/esm/cli/codegen/query-keys.js +441 -0
- package/esm/cli/codegen/scalars.js +1 -0
- package/esm/cli/codegen/schema-types-generator.d.ts +15 -10
- package/esm/cli/codegen/schema-types-generator.js +54 -175
- package/esm/cli/codegen/type-resolver.d.ts +1 -30
- package/esm/cli/codegen/type-resolver.js +0 -49
- package/esm/cli/codegen/types.d.ts +1 -1
- package/esm/cli/codegen/types.js +44 -22
- package/esm/cli/codegen/utils.d.ts +6 -0
- package/esm/cli/codegen/utils.js +18 -0
- package/esm/types/config.d.ts +75 -0
- package/esm/types/config.js +18 -0
- package/package.json +6 -4
- package/types/config.d.ts +75 -0
- package/types/config.js +19 -1
- package/cli/codegen/ts-ast.d.ts +0 -124
- package/cli/codegen/ts-ast.js +0 -280
- package/esm/cli/codegen/ts-ast.d.ts +0 -124
- package/esm/cli/codegen/ts-ast.js +0 -260
package/cli/codegen/mutations.js
CHANGED
|
@@ -1,481 +1,483 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.generateCreateMutationHook = generateCreateMutationHook;
|
|
4
37
|
exports.generateUpdateMutationHook = generateUpdateMutationHook;
|
|
5
38
|
exports.generateDeleteMutationHook = generateDeleteMutationHook;
|
|
6
39
|
exports.generateAllMutationHooks = generateAllMutationHooks;
|
|
7
|
-
const
|
|
40
|
+
const t = __importStar(require("@babel/types"));
|
|
41
|
+
const babel_ast_1 = require("./babel-ast");
|
|
8
42
|
const gql_ast_1 = require("./gql-ast");
|
|
9
43
|
const utils_1 = require("./utils");
|
|
10
|
-
/**
|
|
11
|
-
* Check if a field is auto-generated and should be excluded from create inputs
|
|
12
|
-
* Uses primary key from constraints and common timestamp patterns
|
|
13
|
-
*/
|
|
14
44
|
function isAutoGeneratedField(fieldName, pkFieldNames) {
|
|
15
45
|
const name = fieldName.toLowerCase();
|
|
16
|
-
// Exclude primary key fields (from constraints)
|
|
17
46
|
if (pkFieldNames.has(fieldName))
|
|
18
47
|
return true;
|
|
19
|
-
// Exclude common timestamp patterns (case-insensitive)
|
|
20
|
-
// These are typically auto-set by database triggers or defaults
|
|
21
48
|
const timestampPatterns = [
|
|
22
49
|
'createdat', 'created_at', 'createddate', 'created_date',
|
|
23
50
|
'updatedat', 'updated_at', 'updateddate', 'updated_date',
|
|
24
|
-
'deletedat', 'deleted_at',
|
|
51
|
+
'deletedat', 'deleted_at',
|
|
25
52
|
];
|
|
26
53
|
return timestampPatterns.includes(name);
|
|
27
54
|
}
|
|
28
|
-
// ============================================================================
|
|
29
|
-
// Create mutation hook generator
|
|
30
|
-
// ============================================================================
|
|
31
|
-
/**
|
|
32
|
-
* Generate create mutation hook file content using AST
|
|
33
|
-
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
34
|
-
*/
|
|
35
55
|
function generateCreateMutationHook(table, options = {}) {
|
|
36
|
-
const { reactQueryEnabled = true, enumsFromSchemaTypes = [] } = options;
|
|
37
|
-
// Mutations require React Query - skip generation when disabled
|
|
56
|
+
const { reactQueryEnabled = true, enumsFromSchemaTypes = [], useCentralizedKeys = true, hasRelationships = false, tableTypeNames = new Set(), } = options;
|
|
38
57
|
if (!reactQueryEnabled) {
|
|
39
58
|
return null;
|
|
40
59
|
}
|
|
41
60
|
const enumSet = new Set(enumsFromSchemaTypes);
|
|
42
|
-
const project = (0, ts_ast_1.createProject)();
|
|
43
61
|
const { typeName, singularName } = (0, utils_1.getTableNames)(table);
|
|
44
62
|
const hookName = (0, utils_1.getCreateMutationHookName)(table);
|
|
63
|
+
const keysName = `${(0, utils_1.lcFirst)(typeName)}Keys`;
|
|
64
|
+
const mutationKeysName = `${(0, utils_1.lcFirst)(typeName)}MutationKeys`;
|
|
65
|
+
const scopeTypeName = `${typeName}Scope`;
|
|
45
66
|
const mutationName = (0, utils_1.getCreateMutationName)(table);
|
|
46
67
|
const scalarFields = (0, utils_1.getScalarFields)(table);
|
|
47
|
-
|
|
48
|
-
const pkFieldNames = new Set((0, utils_1.getPrimaryKeyInfo)(table).map(pk => pk.name));
|
|
49
|
-
// Collect which enums are used by this table's fields
|
|
68
|
+
const pkFieldNames = new Set((0, utils_1.getPrimaryKeyInfo)(table).map((pk) => pk.name));
|
|
50
69
|
const usedEnums = new Set();
|
|
70
|
+
const usedTableTypes = new Set();
|
|
51
71
|
for (const field of scalarFields) {
|
|
52
72
|
const cleanType = field.type.gqlType.replace(/!/g, '');
|
|
53
73
|
if (enumSet.has(cleanType)) {
|
|
54
74
|
usedEnums.add(cleanType);
|
|
55
75
|
}
|
|
76
|
+
else if (tableTypeNames.has(cleanType) && cleanType !== typeName) {
|
|
77
|
+
// Track table types used in scalar fields (excluding the main type which is already imported)
|
|
78
|
+
usedTableTypes.add(cleanType);
|
|
79
|
+
}
|
|
56
80
|
}
|
|
57
|
-
// Generate GraphQL document via AST
|
|
58
81
|
const mutationAST = (0, gql_ast_1.buildCreateMutationAST)({ table });
|
|
59
82
|
const mutationDocument = (0, gql_ast_1.printGraphQL)(mutationAST);
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
typeOnlyNamedImports: [typeName],
|
|
77
|
-
}),
|
|
78
|
-
];
|
|
79
|
-
// Add import for enum types from schema-types if any are used
|
|
83
|
+
const statements = [];
|
|
84
|
+
const reactQueryImport = t.importDeclaration([
|
|
85
|
+
t.importSpecifier(t.identifier('useMutation'), t.identifier('useMutation')),
|
|
86
|
+
t.importSpecifier(t.identifier('useQueryClient'), t.identifier('useQueryClient')),
|
|
87
|
+
], t.stringLiteral('@tanstack/react-query'));
|
|
88
|
+
statements.push(reactQueryImport);
|
|
89
|
+
const reactQueryTypeImport = t.importDeclaration([t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], t.stringLiteral('@tanstack/react-query'));
|
|
90
|
+
reactQueryTypeImport.importKind = 'type';
|
|
91
|
+
statements.push(reactQueryTypeImport);
|
|
92
|
+
const clientImport = t.importDeclaration([t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], t.stringLiteral('../client'));
|
|
93
|
+
statements.push(clientImport);
|
|
94
|
+
// Import the main type and any other table types used in scalar fields
|
|
95
|
+
const allTypesToImport = [typeName, ...Array.from(usedTableTypes)].sort();
|
|
96
|
+
const typesImport = t.importDeclaration(allTypesToImport.map((t_) => t.importSpecifier(t.identifier(t_), t.identifier(t_))), t.stringLiteral('../types'));
|
|
97
|
+
typesImport.importKind = 'type';
|
|
98
|
+
statements.push(typesImport);
|
|
80
99
|
if (usedEnums.size > 0) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
100
|
+
const enumImport = t.importDeclaration(Array.from(usedEnums).sort().map((e) => t.importSpecifier(t.identifier(e), t.identifier(e))), t.stringLiteral('../schema-types'));
|
|
101
|
+
enumImport.importKind = 'type';
|
|
102
|
+
statements.push(enumImport);
|
|
103
|
+
}
|
|
104
|
+
if (useCentralizedKeys) {
|
|
105
|
+
const queryKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], t.stringLiteral('../query-keys'));
|
|
106
|
+
statements.push(queryKeyImport);
|
|
107
|
+
if (hasRelationships) {
|
|
108
|
+
const scopeTypeImport = t.importDeclaration([t.importSpecifier(t.identifier(scopeTypeName), t.identifier(scopeTypeName))], t.stringLiteral('../query-keys'));
|
|
109
|
+
scopeTypeImport.importKind = 'type';
|
|
110
|
+
statements.push(scopeTypeImport);
|
|
111
|
+
}
|
|
112
|
+
const mutationKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(mutationKeysName), t.identifier(mutationKeysName))], t.stringLiteral('../mutation-keys'));
|
|
113
|
+
statements.push(mutationKeyImport);
|
|
85
114
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
sourceFile.addStatements('// ============================================================================\n');
|
|
94
|
-
// Add mutation document constant
|
|
95
|
-
sourceFile.addVariableStatement((0, ts_ast_1.createConst)(`${mutationName}MutationDocument`, '`\n' + mutationDocument + '`'));
|
|
96
|
-
// Add section comment
|
|
97
|
-
sourceFile.addStatements('\n// ============================================================================');
|
|
98
|
-
sourceFile.addStatements('// Types');
|
|
99
|
-
sourceFile.addStatements('// ============================================================================\n');
|
|
100
|
-
// Generate CreateInput type - exclude auto-generated fields
|
|
101
|
-
// Note: Not exported to avoid conflicts with schema-types
|
|
115
|
+
const reExportDecl = t.exportNamedDeclaration(null, [t.exportSpecifier(t.identifier(typeName), t.identifier(typeName))], t.stringLiteral('../types'));
|
|
116
|
+
reExportDecl.exportKind = 'type';
|
|
117
|
+
statements.push(reExportDecl);
|
|
118
|
+
const mutationDocConst = t.variableDeclaration('const', [
|
|
119
|
+
t.variableDeclarator(t.identifier(`${mutationName}MutationDocument`), t.templateLiteral([t.templateElement({ raw: '\n' + mutationDocument, cooked: '\n' + mutationDocument }, true)], [])),
|
|
120
|
+
]);
|
|
121
|
+
statements.push(t.exportNamedDeclaration(mutationDocConst));
|
|
102
122
|
const inputFields = scalarFields
|
|
103
123
|
.filter((f) => !isAutoGeneratedField(f.name, pkFieldNames))
|
|
104
|
-
.map((f) =>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
isExported: false,
|
|
112
|
-
}));
|
|
113
|
-
// Variables interface
|
|
114
|
-
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`, [
|
|
115
|
-
{
|
|
116
|
-
name: 'input',
|
|
117
|
-
type: `{
|
|
118
|
-
${(0, utils_1.lcFirst)(typeName)}: ${typeName}CreateInput;
|
|
119
|
-
}`,
|
|
120
|
-
},
|
|
121
|
-
]));
|
|
122
|
-
// Result interface
|
|
123
|
-
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`, [
|
|
124
|
-
{
|
|
125
|
-
name: mutationName,
|
|
126
|
-
type: `{
|
|
127
|
-
${singularName}: ${typeName};
|
|
128
|
-
}`,
|
|
129
|
-
},
|
|
130
|
-
]));
|
|
131
|
-
// Add section comment
|
|
132
|
-
sourceFile.addStatements('\n// ============================================================================');
|
|
133
|
-
sourceFile.addStatements('// Hook');
|
|
134
|
-
sourceFile.addStatements('// ============================================================================\n');
|
|
135
|
-
// Hook function
|
|
136
|
-
sourceFile.addFunction({
|
|
137
|
-
name: hookName,
|
|
138
|
-
isExported: true,
|
|
139
|
-
parameters: [
|
|
140
|
-
{
|
|
141
|
-
name: 'options',
|
|
142
|
-
type: `Omit<UseMutationOptions<${(0, utils_1.ucFirst)(mutationName)}MutationResult, Error, ${(0, utils_1.ucFirst)(mutationName)}MutationVariables>, 'mutationFn'>`,
|
|
143
|
-
hasQuestionToken: true,
|
|
144
|
-
},
|
|
145
|
-
],
|
|
146
|
-
statements: `const queryClient = useQueryClient();
|
|
147
|
-
|
|
148
|
-
return useMutation({
|
|
149
|
-
mutationFn: (variables: ${(0, utils_1.ucFirst)(mutationName)}MutationVariables) =>
|
|
150
|
-
execute<${(0, utils_1.ucFirst)(mutationName)}MutationResult, ${(0, utils_1.ucFirst)(mutationName)}MutationVariables>(
|
|
151
|
-
${mutationName}MutationDocument,
|
|
152
|
-
variables
|
|
153
|
-
),
|
|
154
|
-
onSuccess: () => {
|
|
155
|
-
// Invalidate list queries to refetch
|
|
156
|
-
queryClient.invalidateQueries({ queryKey: ['${typeName.toLowerCase()}', 'list'] });
|
|
157
|
-
},
|
|
158
|
-
...options,
|
|
159
|
-
});`,
|
|
160
|
-
docs: [
|
|
161
|
-
{
|
|
162
|
-
description: `Mutation hook for creating a ${typeName}
|
|
163
|
-
|
|
164
|
-
@example
|
|
165
|
-
\`\`\`tsx
|
|
166
|
-
const { mutate, isPending } = ${hookName}();
|
|
167
|
-
|
|
168
|
-
mutate({
|
|
169
|
-
input: {
|
|
170
|
-
${(0, utils_1.lcFirst)(typeName)}: {
|
|
171
|
-
// ... fields
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
\`\`\``,
|
|
176
|
-
},
|
|
177
|
-
],
|
|
124
|
+
.map((f) => {
|
|
125
|
+
const prop = t.tsPropertySignature(t.identifier(f.name), t.tsTypeAnnotation(t.tsUnionType([
|
|
126
|
+
t.tsTypeReference(t.identifier((0, utils_1.fieldTypeToTs)(f.type))),
|
|
127
|
+
t.tsNullKeyword(),
|
|
128
|
+
])));
|
|
129
|
+
prop.optional = true;
|
|
130
|
+
return prop;
|
|
178
131
|
});
|
|
132
|
+
const createInputInterface = t.tsInterfaceDeclaration(t.identifier(`${typeName}CreateInput`), null, null, t.tsInterfaceBody(inputFields));
|
|
133
|
+
(0, babel_ast_1.addJSDocComment)(createInputInterface, [`Input type for creating a ${typeName}`]);
|
|
134
|
+
statements.push(createInputInterface);
|
|
135
|
+
const variablesInterfaceBody = t.tsInterfaceBody([
|
|
136
|
+
t.tsPropertySignature(t.identifier('input'), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
137
|
+
t.tsPropertySignature(t.identifier((0, utils_1.lcFirst)(typeName)), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${typeName}CreateInput`)))),
|
|
138
|
+
]))),
|
|
139
|
+
]);
|
|
140
|
+
const variablesInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`), null, null, variablesInterfaceBody);
|
|
141
|
+
statements.push(t.exportNamedDeclaration(variablesInterface));
|
|
142
|
+
const resultInterfaceBody = t.tsInterfaceBody([
|
|
143
|
+
t.tsPropertySignature(t.identifier(mutationName), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
144
|
+
t.tsPropertySignature(t.identifier(singularName), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(typeName)))),
|
|
145
|
+
]))),
|
|
146
|
+
]);
|
|
147
|
+
const resultInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`), null, null, resultInterfaceBody);
|
|
148
|
+
statements.push(t.exportNamedDeclaration(resultInterface));
|
|
149
|
+
const hookBodyStatements = [];
|
|
150
|
+
hookBodyStatements.push(t.variableDeclaration('const', [
|
|
151
|
+
t.variableDeclarator(t.identifier('queryClient'), t.callExpression(t.identifier('useQueryClient'), [])),
|
|
152
|
+
]));
|
|
153
|
+
const mutationOptions = [];
|
|
154
|
+
if (useCentralizedKeys) {
|
|
155
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.callExpression(t.memberExpression(t.identifier(mutationKeysName), t.identifier('create')), [])));
|
|
156
|
+
}
|
|
157
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('variables', t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`)))], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')], [
|
|
158
|
+
t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`)),
|
|
159
|
+
t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`)),
|
|
160
|
+
]))));
|
|
161
|
+
const invalidateQueryKey = useCentralizedKeys
|
|
162
|
+
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [])
|
|
163
|
+
: t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]);
|
|
164
|
+
mutationOptions.push(t.objectProperty(t.identifier('onSuccess'), t.arrowFunctionExpression([], t.blockStatement([
|
|
165
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), invalidateQueryKey)])])),
|
|
166
|
+
]))));
|
|
167
|
+
mutationOptions.push(t.spreadElement(t.identifier('options')));
|
|
168
|
+
hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)])));
|
|
169
|
+
const optionsTypeStr = `Omit<UseMutationOptions<${(0, utils_1.ucFirst)(mutationName)}MutationResult, Error, ${(0, utils_1.ucFirst)(mutationName)}MutationVariables>, 'mutationFn'>`;
|
|
170
|
+
const optionsParam = t.identifier('options');
|
|
171
|
+
optionsParam.optional = true;
|
|
172
|
+
optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr)));
|
|
173
|
+
const hookFunc = t.functionDeclaration(t.identifier(hookName), [optionsParam], t.blockStatement(hookBodyStatements));
|
|
174
|
+
const hookExport = t.exportNamedDeclaration(hookFunc);
|
|
175
|
+
(0, babel_ast_1.addJSDocComment)(hookExport, [
|
|
176
|
+
`Mutation hook for creating a ${typeName}`,
|
|
177
|
+
'',
|
|
178
|
+
'@example',
|
|
179
|
+
'```tsx',
|
|
180
|
+
`const { mutate, isPending } = ${hookName}();`,
|
|
181
|
+
'',
|
|
182
|
+
'mutate({',
|
|
183
|
+
' input: {',
|
|
184
|
+
` ${(0, utils_1.lcFirst)(typeName)}: {`,
|
|
185
|
+
' // ... fields',
|
|
186
|
+
' },',
|
|
187
|
+
' },',
|
|
188
|
+
'});',
|
|
189
|
+
'```',
|
|
190
|
+
]);
|
|
191
|
+
statements.push(hookExport);
|
|
192
|
+
const code = (0, babel_ast_1.generateCode)(statements);
|
|
193
|
+
const content = (0, utils_1.getGeneratedFileHeader)(`Create mutation hook for ${typeName}`) + '\n\n' + code;
|
|
179
194
|
return {
|
|
180
195
|
fileName: (0, utils_1.getCreateMutationFileName)(table),
|
|
181
|
-
content
|
|
196
|
+
content,
|
|
182
197
|
};
|
|
183
198
|
}
|
|
184
|
-
// ============================================================================
|
|
185
|
-
// Update mutation hook generator
|
|
186
|
-
// ============================================================================
|
|
187
|
-
/**
|
|
188
|
-
* Generate update mutation hook file content using AST
|
|
189
|
-
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
190
|
-
*/
|
|
191
199
|
function generateUpdateMutationHook(table, options = {}) {
|
|
192
|
-
const { reactQueryEnabled = true, enumsFromSchemaTypes = [] } = options;
|
|
193
|
-
// Mutations require React Query - skip generation when disabled
|
|
200
|
+
const { reactQueryEnabled = true, enumsFromSchemaTypes = [], useCentralizedKeys = true, hasRelationships = false, tableTypeNames = new Set(), } = options;
|
|
194
201
|
if (!reactQueryEnabled) {
|
|
195
202
|
return null;
|
|
196
203
|
}
|
|
197
|
-
// Check if update mutation exists
|
|
198
204
|
if (table.query?.update === null) {
|
|
199
205
|
return null;
|
|
200
206
|
}
|
|
201
207
|
const enumSet = new Set(enumsFromSchemaTypes);
|
|
202
|
-
const project = (0, ts_ast_1.createProject)();
|
|
203
208
|
const { typeName, singularName } = (0, utils_1.getTableNames)(table);
|
|
204
209
|
const hookName = (0, utils_1.getUpdateMutationHookName)(table);
|
|
205
210
|
const mutationName = (0, utils_1.getUpdateMutationName)(table);
|
|
206
211
|
const scalarFields = (0, utils_1.getScalarFields)(table);
|
|
207
|
-
|
|
212
|
+
const keysName = `${(0, utils_1.lcFirst)(typeName)}Keys`;
|
|
213
|
+
const mutationKeysName = `${(0, utils_1.lcFirst)(typeName)}MutationKeys`;
|
|
214
|
+
const scopeTypeName = `${typeName}Scope`;
|
|
208
215
|
const pkFields = (0, utils_1.getPrimaryKeyInfo)(table);
|
|
209
|
-
const pkField = pkFields[0];
|
|
210
|
-
const pkFieldNames = new Set(pkFields.map(pk => pk.name));
|
|
211
|
-
// Collect which enums are used by this table's fields
|
|
216
|
+
const pkField = pkFields[0];
|
|
217
|
+
const pkFieldNames = new Set(pkFields.map((pk) => pk.name));
|
|
212
218
|
const usedEnums = new Set();
|
|
219
|
+
const usedTableTypes = new Set();
|
|
213
220
|
for (const field of scalarFields) {
|
|
214
221
|
const cleanType = field.type.gqlType.replace(/!/g, '');
|
|
215
222
|
if (enumSet.has(cleanType)) {
|
|
216
223
|
usedEnums.add(cleanType);
|
|
217
224
|
}
|
|
225
|
+
else if (tableTypeNames.has(cleanType) && cleanType !== typeName) {
|
|
226
|
+
usedTableTypes.add(cleanType);
|
|
227
|
+
}
|
|
218
228
|
}
|
|
219
|
-
// Generate GraphQL document via AST
|
|
220
229
|
const mutationAST = (0, gql_ast_1.buildUpdateMutationAST)({ table });
|
|
221
230
|
const mutationDocument = (0, gql_ast_1.printGraphQL)(mutationAST);
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
typeOnlyNamedImports: [typeName],
|
|
239
|
-
}),
|
|
240
|
-
];
|
|
241
|
-
// Add import for enum types from schema-types if any are used
|
|
231
|
+
const statements = [];
|
|
232
|
+
const reactQueryImport = t.importDeclaration([
|
|
233
|
+
t.importSpecifier(t.identifier('useMutation'), t.identifier('useMutation')),
|
|
234
|
+
t.importSpecifier(t.identifier('useQueryClient'), t.identifier('useQueryClient')),
|
|
235
|
+
], t.stringLiteral('@tanstack/react-query'));
|
|
236
|
+
statements.push(reactQueryImport);
|
|
237
|
+
const reactQueryTypeImport = t.importDeclaration([t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], t.stringLiteral('@tanstack/react-query'));
|
|
238
|
+
reactQueryTypeImport.importKind = 'type';
|
|
239
|
+
statements.push(reactQueryTypeImport);
|
|
240
|
+
const clientImport = t.importDeclaration([t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], t.stringLiteral('../client'));
|
|
241
|
+
statements.push(clientImport);
|
|
242
|
+
// Import the main type and any other table types used in scalar fields
|
|
243
|
+
const allTypesToImportUpdate = [typeName, ...Array.from(usedTableTypes)].sort();
|
|
244
|
+
const typesImport = t.importDeclaration(allTypesToImportUpdate.map((t_) => t.importSpecifier(t.identifier(t_), t.identifier(t_))), t.stringLiteral('../types'));
|
|
245
|
+
typesImport.importKind = 'type';
|
|
246
|
+
statements.push(typesImport);
|
|
242
247
|
if (usedEnums.size > 0) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}));
|
|
248
|
+
const enumImport = t.importDeclaration(Array.from(usedEnums).sort().map((e) => t.importSpecifier(t.identifier(e), t.identifier(e))), t.stringLiteral('../schema-types'));
|
|
249
|
+
enumImport.importKind = 'type';
|
|
250
|
+
statements.push(enumImport);
|
|
247
251
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
252
|
+
if (useCentralizedKeys) {
|
|
253
|
+
const queryKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], t.stringLiteral('../query-keys'));
|
|
254
|
+
statements.push(queryKeyImport);
|
|
255
|
+
if (hasRelationships) {
|
|
256
|
+
const scopeTypeImport = t.importDeclaration([t.importSpecifier(t.identifier(scopeTypeName), t.identifier(scopeTypeName))], t.stringLiteral('../query-keys'));
|
|
257
|
+
scopeTypeImport.importKind = 'type';
|
|
258
|
+
statements.push(scopeTypeImport);
|
|
259
|
+
}
|
|
260
|
+
const mutationKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(mutationKeysName), t.identifier(mutationKeysName))], t.stringLiteral('../mutation-keys'));
|
|
261
|
+
statements.push(mutationKeyImport);
|
|
262
|
+
}
|
|
263
|
+
const reExportDecl = t.exportNamedDeclaration(null, [t.exportSpecifier(t.identifier(typeName), t.identifier(typeName))], t.stringLiteral('../types'));
|
|
264
|
+
reExportDecl.exportKind = 'type';
|
|
265
|
+
statements.push(reExportDecl);
|
|
266
|
+
const mutationDocConst = t.variableDeclaration('const', [
|
|
267
|
+
t.variableDeclarator(t.identifier(`${mutationName}MutationDocument`), t.templateLiteral([t.templateElement({ raw: '\n' + mutationDocument, cooked: '\n' + mutationDocument }, true)], [])),
|
|
268
|
+
]);
|
|
269
|
+
statements.push(t.exportNamedDeclaration(mutationDocConst));
|
|
264
270
|
const patchFields = scalarFields
|
|
265
271
|
.filter((f) => !pkFieldNames.has(f.name))
|
|
266
|
-
.map((f) =>
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
isExported: false,
|
|
274
|
-
}));
|
|
275
|
-
// Variables interface - use dynamic PK field name and type
|
|
276
|
-
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`, [
|
|
277
|
-
{
|
|
278
|
-
name: 'input',
|
|
279
|
-
type: `{
|
|
280
|
-
${pkField.name}: ${pkField.tsType};
|
|
281
|
-
patch: ${typeName}Patch;
|
|
282
|
-
}`,
|
|
283
|
-
},
|
|
284
|
-
]));
|
|
285
|
-
// Result interface
|
|
286
|
-
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`, [
|
|
287
|
-
{
|
|
288
|
-
name: mutationName,
|
|
289
|
-
type: `{
|
|
290
|
-
${singularName}: ${typeName};
|
|
291
|
-
}`,
|
|
292
|
-
},
|
|
293
|
-
]));
|
|
294
|
-
// Add section comment
|
|
295
|
-
sourceFile.addStatements('\n// ============================================================================');
|
|
296
|
-
sourceFile.addStatements('// Hook');
|
|
297
|
-
sourceFile.addStatements('// ============================================================================\n');
|
|
298
|
-
// Hook function
|
|
299
|
-
sourceFile.addFunction({
|
|
300
|
-
name: hookName,
|
|
301
|
-
isExported: true,
|
|
302
|
-
parameters: [
|
|
303
|
-
{
|
|
304
|
-
name: 'options',
|
|
305
|
-
type: `Omit<UseMutationOptions<${(0, utils_1.ucFirst)(mutationName)}MutationResult, Error, ${(0, utils_1.ucFirst)(mutationName)}MutationVariables>, 'mutationFn'>`,
|
|
306
|
-
hasQuestionToken: true,
|
|
307
|
-
},
|
|
308
|
-
],
|
|
309
|
-
statements: `const queryClient = useQueryClient();
|
|
310
|
-
|
|
311
|
-
return useMutation({
|
|
312
|
-
mutationFn: (variables: ${(0, utils_1.ucFirst)(mutationName)}MutationVariables) =>
|
|
313
|
-
execute<${(0, utils_1.ucFirst)(mutationName)}MutationResult, ${(0, utils_1.ucFirst)(mutationName)}MutationVariables>(
|
|
314
|
-
${mutationName}MutationDocument,
|
|
315
|
-
variables
|
|
316
|
-
),
|
|
317
|
-
onSuccess: (_, variables) => {
|
|
318
|
-
// Invalidate specific item and list queries
|
|
319
|
-
queryClient.invalidateQueries({ queryKey: ['${typeName.toLowerCase()}', 'detail', variables.input.${pkField.name}] });
|
|
320
|
-
queryClient.invalidateQueries({ queryKey: ['${typeName.toLowerCase()}', 'list'] });
|
|
321
|
-
},
|
|
322
|
-
...options,
|
|
323
|
-
});`,
|
|
324
|
-
docs: [
|
|
325
|
-
{
|
|
326
|
-
description: `Mutation hook for updating a ${typeName}
|
|
327
|
-
|
|
328
|
-
@example
|
|
329
|
-
\`\`\`tsx
|
|
330
|
-
const { mutate, isPending } = ${hookName}();
|
|
331
|
-
|
|
332
|
-
mutate({
|
|
333
|
-
input: {
|
|
334
|
-
${pkField.name}: ${pkField.tsType === 'string' ? "'value-here'" : '123'},
|
|
335
|
-
patch: {
|
|
336
|
-
// ... fields to update
|
|
337
|
-
},
|
|
338
|
-
},
|
|
339
|
-
});
|
|
340
|
-
\`\`\``,
|
|
341
|
-
},
|
|
342
|
-
],
|
|
272
|
+
.map((f) => {
|
|
273
|
+
const prop = t.tsPropertySignature(t.identifier(f.name), t.tsTypeAnnotation(t.tsUnionType([
|
|
274
|
+
t.tsTypeReference(t.identifier((0, utils_1.fieldTypeToTs)(f.type))),
|
|
275
|
+
t.tsNullKeyword(),
|
|
276
|
+
])));
|
|
277
|
+
prop.optional = true;
|
|
278
|
+
return prop;
|
|
343
279
|
});
|
|
280
|
+
const patchInterface = t.tsInterfaceDeclaration(t.identifier(`${typeName}Patch`), null, null, t.tsInterfaceBody(patchFields));
|
|
281
|
+
(0, babel_ast_1.addJSDocComment)(patchInterface, [`Patch type for updating a ${typeName} - all fields optional`]);
|
|
282
|
+
statements.push(patchInterface);
|
|
283
|
+
const pkTypeAnnotation = pkField.tsType === 'string'
|
|
284
|
+
? t.tsStringKeyword()
|
|
285
|
+
: pkField.tsType === 'number'
|
|
286
|
+
? t.tsNumberKeyword()
|
|
287
|
+
: t.tsTypeReference(t.identifier(pkField.tsType));
|
|
288
|
+
const variablesInterfaceBody = t.tsInterfaceBody([
|
|
289
|
+
t.tsPropertySignature(t.identifier('input'), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
290
|
+
t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTypeAnnotation)),
|
|
291
|
+
t.tsPropertySignature(t.identifier('patch'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${typeName}Patch`)))),
|
|
292
|
+
]))),
|
|
293
|
+
]);
|
|
294
|
+
const variablesInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`), null, null, variablesInterfaceBody);
|
|
295
|
+
statements.push(t.exportNamedDeclaration(variablesInterface));
|
|
296
|
+
const resultInterfaceBody = t.tsInterfaceBody([
|
|
297
|
+
t.tsPropertySignature(t.identifier(mutationName), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
298
|
+
t.tsPropertySignature(t.identifier(singularName), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(typeName)))),
|
|
299
|
+
]))),
|
|
300
|
+
]);
|
|
301
|
+
const resultInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`), null, null, resultInterfaceBody);
|
|
302
|
+
statements.push(t.exportNamedDeclaration(resultInterface));
|
|
303
|
+
const hookBodyStatements = [];
|
|
304
|
+
hookBodyStatements.push(t.variableDeclaration('const', [
|
|
305
|
+
t.variableDeclarator(t.identifier('queryClient'), t.callExpression(t.identifier('useQueryClient'), [])),
|
|
306
|
+
]));
|
|
307
|
+
const mutationOptions = [];
|
|
308
|
+
if (useCentralizedKeys) {
|
|
309
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.memberExpression(t.identifier(mutationKeysName), t.identifier('all'))));
|
|
310
|
+
}
|
|
311
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('variables', t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`)))], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')], [
|
|
312
|
+
t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`)),
|
|
313
|
+
t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`)),
|
|
314
|
+
]))));
|
|
315
|
+
const detailQueryKey = useCentralizedKeys
|
|
316
|
+
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name))])
|
|
317
|
+
: t.arrayExpression([
|
|
318
|
+
t.stringLiteral(typeName.toLowerCase()),
|
|
319
|
+
t.stringLiteral('detail'),
|
|
320
|
+
t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name)),
|
|
321
|
+
]);
|
|
322
|
+
const listQueryKey = useCentralizedKeys
|
|
323
|
+
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [])
|
|
324
|
+
: t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]);
|
|
325
|
+
mutationOptions.push(t.objectProperty(t.identifier('onSuccess'), t.arrowFunctionExpression([t.identifier('_'), t.identifier('variables')], t.blockStatement([
|
|
326
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), detailQueryKey)])])),
|
|
327
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), listQueryKey)])])),
|
|
328
|
+
]))));
|
|
329
|
+
mutationOptions.push(t.spreadElement(t.identifier('options')));
|
|
330
|
+
hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)])));
|
|
331
|
+
const optionsTypeStr = `Omit<UseMutationOptions<${(0, utils_1.ucFirst)(mutationName)}MutationResult, Error, ${(0, utils_1.ucFirst)(mutationName)}MutationVariables>, 'mutationFn'>`;
|
|
332
|
+
const optionsParam = t.identifier('options');
|
|
333
|
+
optionsParam.optional = true;
|
|
334
|
+
optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr)));
|
|
335
|
+
const hookFunc = t.functionDeclaration(t.identifier(hookName), [optionsParam], t.blockStatement(hookBodyStatements));
|
|
336
|
+
const hookExport = t.exportNamedDeclaration(hookFunc);
|
|
337
|
+
(0, babel_ast_1.addJSDocComment)(hookExport, [
|
|
338
|
+
`Mutation hook for updating a ${typeName}`,
|
|
339
|
+
'',
|
|
340
|
+
'@example',
|
|
341
|
+
'```tsx',
|
|
342
|
+
`const { mutate, isPending } = ${hookName}();`,
|
|
343
|
+
'',
|
|
344
|
+
'mutate({',
|
|
345
|
+
' input: {',
|
|
346
|
+
` ${pkField.name}: ${pkField.tsType === 'string' ? "'value-here'" : '123'},`,
|
|
347
|
+
' patch: {',
|
|
348
|
+
' // ... fields to update',
|
|
349
|
+
' },',
|
|
350
|
+
' },',
|
|
351
|
+
'});',
|
|
352
|
+
'```',
|
|
353
|
+
]);
|
|
354
|
+
statements.push(hookExport);
|
|
355
|
+
const code = (0, babel_ast_1.generateCode)(statements);
|
|
356
|
+
const content = (0, utils_1.getGeneratedFileHeader)(`Update mutation hook for ${typeName}`) + '\n\n' + code;
|
|
344
357
|
return {
|
|
345
358
|
fileName: (0, utils_1.getUpdateMutationFileName)(table),
|
|
346
|
-
content
|
|
359
|
+
content,
|
|
347
360
|
};
|
|
348
361
|
}
|
|
349
|
-
// ============================================================================
|
|
350
|
-
// Delete mutation hook generator
|
|
351
|
-
// ============================================================================
|
|
352
|
-
/**
|
|
353
|
-
* Generate delete mutation hook file content using AST
|
|
354
|
-
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
355
|
-
*/
|
|
356
362
|
function generateDeleteMutationHook(table, options = {}) {
|
|
357
|
-
const { reactQueryEnabled = true } = options;
|
|
358
|
-
// Mutations require React Query - skip generation when disabled
|
|
363
|
+
const { reactQueryEnabled = true, useCentralizedKeys = true, hasRelationships = false, } = options;
|
|
359
364
|
if (!reactQueryEnabled) {
|
|
360
365
|
return null;
|
|
361
366
|
}
|
|
362
|
-
// Check if delete mutation exists
|
|
363
367
|
if (table.query?.delete === null) {
|
|
364
368
|
return null;
|
|
365
369
|
}
|
|
366
|
-
const project = (0, ts_ast_1.createProject)();
|
|
367
370
|
const { typeName } = (0, utils_1.getTableNames)(table);
|
|
368
371
|
const hookName = (0, utils_1.getDeleteMutationHookName)(table);
|
|
369
372
|
const mutationName = (0, utils_1.getDeleteMutationName)(table);
|
|
370
|
-
|
|
373
|
+
const keysName = `${(0, utils_1.lcFirst)(typeName)}Keys`;
|
|
374
|
+
const mutationKeysName = `${(0, utils_1.lcFirst)(typeName)}MutationKeys`;
|
|
375
|
+
const scopeTypeName = `${typeName}Scope`;
|
|
371
376
|
const pkFields = (0, utils_1.getPrimaryKeyInfo)(table);
|
|
372
|
-
const pkField = pkFields[0];
|
|
373
|
-
// Generate GraphQL document via AST
|
|
377
|
+
const pkField = pkFields[0];
|
|
374
378
|
const mutationAST = (0, gql_ast_1.buildDeleteMutationAST)({ table });
|
|
375
379
|
const mutationDocument = (0, gql_ast_1.printGraphQL)(mutationAST);
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
380
|
+
const statements = [];
|
|
381
|
+
const reactQueryImport = t.importDeclaration([
|
|
382
|
+
t.importSpecifier(t.identifier('useMutation'), t.identifier('useMutation')),
|
|
383
|
+
t.importSpecifier(t.identifier('useQueryClient'), t.identifier('useQueryClient')),
|
|
384
|
+
], t.stringLiteral('@tanstack/react-query'));
|
|
385
|
+
statements.push(reactQueryImport);
|
|
386
|
+
const reactQueryTypeImport = t.importDeclaration([t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], t.stringLiteral('@tanstack/react-query'));
|
|
387
|
+
reactQueryTypeImport.importKind = 'type';
|
|
388
|
+
statements.push(reactQueryTypeImport);
|
|
389
|
+
const clientImport = t.importDeclaration([t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], t.stringLiteral('../client'));
|
|
390
|
+
statements.push(clientImport);
|
|
391
|
+
if (useCentralizedKeys) {
|
|
392
|
+
const queryKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], t.stringLiteral('../query-keys'));
|
|
393
|
+
statements.push(queryKeyImport);
|
|
394
|
+
if (hasRelationships) {
|
|
395
|
+
const scopeTypeImport = t.importDeclaration([t.importSpecifier(t.identifier(scopeTypeName), t.identifier(scopeTypeName))], t.stringLiteral('../query-keys'));
|
|
396
|
+
scopeTypeImport.importKind = 'type';
|
|
397
|
+
statements.push(scopeTypeImport);
|
|
398
|
+
}
|
|
399
|
+
const mutationKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(mutationKeysName), t.identifier(mutationKeysName))], t.stringLiteral('../mutation-keys'));
|
|
400
|
+
statements.push(mutationKeyImport);
|
|
401
|
+
}
|
|
402
|
+
const mutationDocConst = t.variableDeclaration('const', [
|
|
403
|
+
t.variableDeclarator(t.identifier(`${mutationName}MutationDocument`), t.templateLiteral([t.templateElement({ raw: '\n' + mutationDocument, cooked: '\n' + mutationDocument }, true)], [])),
|
|
390
404
|
]);
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
type: `{
|
|
415
|
-
clientMutationId: string | null;
|
|
416
|
-
deleted${(0, utils_1.ucFirst)(pkField.name)}: ${pkField.tsType} | null;
|
|
417
|
-
}`,
|
|
418
|
-
},
|
|
405
|
+
statements.push(t.exportNamedDeclaration(mutationDocConst));
|
|
406
|
+
const pkTypeAnnotation = pkField.tsType === 'string'
|
|
407
|
+
? t.tsStringKeyword()
|
|
408
|
+
: pkField.tsType === 'number'
|
|
409
|
+
? t.tsNumberKeyword()
|
|
410
|
+
: t.tsTypeReference(t.identifier(pkField.tsType));
|
|
411
|
+
const variablesInterfaceBody = t.tsInterfaceBody([
|
|
412
|
+
t.tsPropertySignature(t.identifier('input'), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
413
|
+
t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTypeAnnotation)),
|
|
414
|
+
]))),
|
|
415
|
+
]);
|
|
416
|
+
const variablesInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`), null, null, variablesInterfaceBody);
|
|
417
|
+
statements.push(t.exportNamedDeclaration(variablesInterface));
|
|
418
|
+
const deletedPkProp = t.tsPropertySignature(t.identifier(`deleted${(0, utils_1.ucFirst)(pkField.name)}`), t.tsTypeAnnotation(t.tsUnionType([pkTypeAnnotation, t.tsNullKeyword()])));
|
|
419
|
+
const clientMutationIdProp = t.tsPropertySignature(t.identifier('clientMutationId'), t.tsTypeAnnotation(t.tsUnionType([t.tsStringKeyword(), t.tsNullKeyword()])));
|
|
420
|
+
const resultInterfaceBody = t.tsInterfaceBody([
|
|
421
|
+
t.tsPropertySignature(t.identifier(mutationName), t.tsTypeAnnotation(t.tsTypeLiteral([clientMutationIdProp, deletedPkProp]))),
|
|
422
|
+
]);
|
|
423
|
+
const resultInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`), null, null, resultInterfaceBody);
|
|
424
|
+
statements.push(t.exportNamedDeclaration(resultInterface));
|
|
425
|
+
const hookBodyStatements = [];
|
|
426
|
+
hookBodyStatements.push(t.variableDeclaration('const', [
|
|
427
|
+
t.variableDeclarator(t.identifier('queryClient'), t.callExpression(t.identifier('useQueryClient'), [])),
|
|
419
428
|
]));
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
]
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
});
|
|
429
|
+
const mutationOptions = [];
|
|
430
|
+
if (useCentralizedKeys) {
|
|
431
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.memberExpression(t.identifier(mutationKeysName), t.identifier('all'))));
|
|
432
|
+
}
|
|
433
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('variables', t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`)))], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')], [
|
|
434
|
+
t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`)),
|
|
435
|
+
t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`)),
|
|
436
|
+
]))));
|
|
437
|
+
const detailQueryKey = useCentralizedKeys
|
|
438
|
+
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name))])
|
|
439
|
+
: t.arrayExpression([
|
|
440
|
+
t.stringLiteral(typeName.toLowerCase()),
|
|
441
|
+
t.stringLiteral('detail'),
|
|
442
|
+
t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name)),
|
|
443
|
+
]);
|
|
444
|
+
const listQueryKey = useCentralizedKeys
|
|
445
|
+
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [])
|
|
446
|
+
: t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]);
|
|
447
|
+
mutationOptions.push(t.objectProperty(t.identifier('onSuccess'), t.arrowFunctionExpression([t.identifier('_'), t.identifier('variables')], t.blockStatement([
|
|
448
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('removeQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), detailQueryKey)])])),
|
|
449
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), listQueryKey)])])),
|
|
450
|
+
]))));
|
|
451
|
+
mutationOptions.push(t.spreadElement(t.identifier('options')));
|
|
452
|
+
hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)])));
|
|
453
|
+
const optionsTypeStr = `Omit<UseMutationOptions<${(0, utils_1.ucFirst)(mutationName)}MutationResult, Error, ${(0, utils_1.ucFirst)(mutationName)}MutationVariables>, 'mutationFn'>`;
|
|
454
|
+
const optionsParam = t.identifier('options');
|
|
455
|
+
optionsParam.optional = true;
|
|
456
|
+
optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr)));
|
|
457
|
+
const hookFunc = t.functionDeclaration(t.identifier(hookName), [optionsParam], t.blockStatement(hookBodyStatements));
|
|
458
|
+
const hookExport = t.exportNamedDeclaration(hookFunc);
|
|
459
|
+
(0, babel_ast_1.addJSDocComment)(hookExport, [
|
|
460
|
+
`Mutation hook for deleting a ${typeName}`,
|
|
461
|
+
'',
|
|
462
|
+
'@example',
|
|
463
|
+
'```tsx',
|
|
464
|
+
`const { mutate, isPending } = ${hookName}();`,
|
|
465
|
+
'',
|
|
466
|
+
'mutate({',
|
|
467
|
+
' input: {',
|
|
468
|
+
` ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'},`,
|
|
469
|
+
' },',
|
|
470
|
+
'});',
|
|
471
|
+
'```',
|
|
472
|
+
]);
|
|
473
|
+
statements.push(hookExport);
|
|
474
|
+
const code = (0, babel_ast_1.generateCode)(statements);
|
|
475
|
+
const content = (0, utils_1.getGeneratedFileHeader)(`Delete mutation hook for ${typeName}`) + '\n\n' + code;
|
|
467
476
|
return {
|
|
468
477
|
fileName: (0, utils_1.getDeleteMutationFileName)(table),
|
|
469
|
-
content
|
|
478
|
+
content,
|
|
470
479
|
};
|
|
471
480
|
}
|
|
472
|
-
// ============================================================================
|
|
473
|
-
// Batch generator
|
|
474
|
-
// ============================================================================
|
|
475
|
-
/**
|
|
476
|
-
* Generate all mutation hook files for all tables
|
|
477
|
-
* When reactQueryEnabled is false, returns empty array since mutations require React Query
|
|
478
|
-
*/
|
|
479
481
|
function generateAllMutationHooks(tables, options = {}) {
|
|
480
482
|
const files = [];
|
|
481
483
|
for (const table of tables) {
|