@constructive-io/graphql-codegen 2.23.3 → 2.24.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 +147 -2
- package/cli/codegen/babel-ast.d.ts +46 -0
- package/cli/codegen/babel-ast.js +145 -0
- package/cli/codegen/barrel.d.ts +7 -2
- package/cli/codegen/barrel.js +159 -97
- 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 +246 -335
- package/cli/codegen/index.d.ts +3 -0
- package/cli/codegen/index.js +72 -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 +3 -19
- package/cli/codegen/mutations.js +372 -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 +403 -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 +3 -11
- package/cli/codegen/queries.js +582 -389
- 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/esm/cli/codegen/babel-ast.d.ts +46 -0
- package/esm/cli/codegen/babel-ast.js +97 -0
- package/esm/cli/codegen/barrel.d.ts +7 -2
- package/esm/cli/codegen/barrel.js +126 -97
- 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 +214 -336
- package/esm/cli/codegen/index.d.ts +3 -0
- package/esm/cli/codegen/index.js +68 -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 +3 -19
- package/esm/cli/codegen/mutations.js +340 -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 +371 -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 +3 -11
- package/esm/cli/codegen/queries.js +550 -390
- 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/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,52 +1,71 @@
|
|
|
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, } = 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();
|
|
51
70
|
for (const field of scalarFields) {
|
|
52
71
|
const cleanType = field.type.gqlType.replace(/!/g, '');
|
|
@@ -54,161 +73,141 @@ function generateCreateMutationHook(table, options = {}) {
|
|
|
54
73
|
usedEnums.add(cleanType);
|
|
55
74
|
}
|
|
56
75
|
}
|
|
57
|
-
// Generate GraphQL document via AST
|
|
58
76
|
const mutationAST = (0, gql_ast_1.buildCreateMutationAST)({ table });
|
|
59
77
|
const mutationDocument = (0, gql_ast_1.printGraphQL)(mutationAST);
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
(0, ts_ast_1.createImport)({
|
|
75
|
-
moduleSpecifier: '../types',
|
|
76
|
-
typeOnlyNamedImports: [typeName],
|
|
77
|
-
}),
|
|
78
|
-
];
|
|
79
|
-
// Add import for enum types from schema-types if any are used
|
|
78
|
+
const statements = [];
|
|
79
|
+
const reactQueryImport = t.importDeclaration([
|
|
80
|
+
t.importSpecifier(t.identifier('useMutation'), t.identifier('useMutation')),
|
|
81
|
+
t.importSpecifier(t.identifier('useQueryClient'), t.identifier('useQueryClient')),
|
|
82
|
+
], t.stringLiteral('@tanstack/react-query'));
|
|
83
|
+
statements.push(reactQueryImport);
|
|
84
|
+
const reactQueryTypeImport = t.importDeclaration([t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], t.stringLiteral('@tanstack/react-query'));
|
|
85
|
+
reactQueryTypeImport.importKind = 'type';
|
|
86
|
+
statements.push(reactQueryTypeImport);
|
|
87
|
+
const clientImport = t.importDeclaration([t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], t.stringLiteral('../client'));
|
|
88
|
+
statements.push(clientImport);
|
|
89
|
+
const typesImport = t.importDeclaration([t.importSpecifier(t.identifier(typeName), t.identifier(typeName))], t.stringLiteral('../types'));
|
|
90
|
+
typesImport.importKind = 'type';
|
|
91
|
+
statements.push(typesImport);
|
|
80
92
|
if (usedEnums.size > 0) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}));
|
|
93
|
+
const enumImport = t.importDeclaration(Array.from(usedEnums).sort().map((e) => t.importSpecifier(t.identifier(e), t.identifier(e))), t.stringLiteral('../schema-types'));
|
|
94
|
+
enumImport.importKind = 'type';
|
|
95
|
+
statements.push(enumImport);
|
|
85
96
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
if (useCentralizedKeys) {
|
|
98
|
+
const queryKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], t.stringLiteral('../query-keys'));
|
|
99
|
+
statements.push(queryKeyImport);
|
|
100
|
+
if (hasRelationships) {
|
|
101
|
+
const scopeTypeImport = t.importDeclaration([t.importSpecifier(t.identifier(scopeTypeName), t.identifier(scopeTypeName))], t.stringLiteral('../query-keys'));
|
|
102
|
+
scopeTypeImport.importKind = 'type';
|
|
103
|
+
statements.push(scopeTypeImport);
|
|
104
|
+
}
|
|
105
|
+
const mutationKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(mutationKeysName), t.identifier(mutationKeysName))], t.stringLiteral('../mutation-keys'));
|
|
106
|
+
statements.push(mutationKeyImport);
|
|
107
|
+
}
|
|
108
|
+
const reExportDecl = t.exportNamedDeclaration(null, [t.exportSpecifier(t.identifier(typeName), t.identifier(typeName))], t.stringLiteral('../types'));
|
|
109
|
+
reExportDecl.exportKind = 'type';
|
|
110
|
+
statements.push(reExportDecl);
|
|
111
|
+
const mutationDocConst = t.variableDeclaration('const', [
|
|
112
|
+
t.variableDeclarator(t.identifier(`${mutationName}MutationDocument`), t.templateLiteral([t.templateElement({ raw: '\n' + mutationDocument, cooked: '\n' + mutationDocument }, true)], [])),
|
|
113
|
+
]);
|
|
114
|
+
statements.push(t.exportNamedDeclaration(mutationDocConst));
|
|
102
115
|
const inputFields = scalarFields
|
|
103
116
|
.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
|
-
],
|
|
117
|
+
.map((f) => {
|
|
118
|
+
const prop = t.tsPropertySignature(t.identifier(f.name), t.tsTypeAnnotation(t.tsUnionType([
|
|
119
|
+
t.tsTypeReference(t.identifier((0, utils_1.fieldTypeToTs)(f.type))),
|
|
120
|
+
t.tsNullKeyword(),
|
|
121
|
+
])));
|
|
122
|
+
prop.optional = true;
|
|
123
|
+
return prop;
|
|
178
124
|
});
|
|
125
|
+
const createInputInterface = t.tsInterfaceDeclaration(t.identifier(`${typeName}CreateInput`), null, null, t.tsInterfaceBody(inputFields));
|
|
126
|
+
(0, babel_ast_1.addJSDocComment)(createInputInterface, [`Input type for creating a ${typeName}`]);
|
|
127
|
+
statements.push(createInputInterface);
|
|
128
|
+
const variablesInterfaceBody = t.tsInterfaceBody([
|
|
129
|
+
t.tsPropertySignature(t.identifier('input'), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
130
|
+
t.tsPropertySignature(t.identifier((0, utils_1.lcFirst)(typeName)), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${typeName}CreateInput`)))),
|
|
131
|
+
]))),
|
|
132
|
+
]);
|
|
133
|
+
const variablesInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`), null, null, variablesInterfaceBody);
|
|
134
|
+
statements.push(t.exportNamedDeclaration(variablesInterface));
|
|
135
|
+
const resultInterfaceBody = t.tsInterfaceBody([
|
|
136
|
+
t.tsPropertySignature(t.identifier(mutationName), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
137
|
+
t.tsPropertySignature(t.identifier(singularName), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(typeName)))),
|
|
138
|
+
]))),
|
|
139
|
+
]);
|
|
140
|
+
const resultInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`), null, null, resultInterfaceBody);
|
|
141
|
+
statements.push(t.exportNamedDeclaration(resultInterface));
|
|
142
|
+
const hookBodyStatements = [];
|
|
143
|
+
hookBodyStatements.push(t.variableDeclaration('const', [
|
|
144
|
+
t.variableDeclarator(t.identifier('queryClient'), t.callExpression(t.identifier('useQueryClient'), [])),
|
|
145
|
+
]));
|
|
146
|
+
const mutationOptions = [];
|
|
147
|
+
if (useCentralizedKeys) {
|
|
148
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.callExpression(t.memberExpression(t.identifier(mutationKeysName), t.identifier('create')), [])));
|
|
149
|
+
}
|
|
150
|
+
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`)))], t.callExpression(t.identifier('execute'), [
|
|
151
|
+
t.identifier(`${mutationName}MutationDocument`),
|
|
152
|
+
t.identifier('variables'),
|
|
153
|
+
]))));
|
|
154
|
+
const invalidateQueryKey = useCentralizedKeys
|
|
155
|
+
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [])
|
|
156
|
+
: t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]);
|
|
157
|
+
mutationOptions.push(t.objectProperty(t.identifier('onSuccess'), t.arrowFunctionExpression([], t.blockStatement([
|
|
158
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), invalidateQueryKey)])])),
|
|
159
|
+
]))));
|
|
160
|
+
mutationOptions.push(t.spreadElement(t.identifier('options')));
|
|
161
|
+
hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)])));
|
|
162
|
+
const optionsTypeStr = `Omit<UseMutationOptions<${(0, utils_1.ucFirst)(mutationName)}MutationResult, Error, ${(0, utils_1.ucFirst)(mutationName)}MutationVariables>, 'mutationFn'>`;
|
|
163
|
+
const optionsParam = t.identifier('options');
|
|
164
|
+
optionsParam.optional = true;
|
|
165
|
+
optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr)));
|
|
166
|
+
const hookFunc = t.functionDeclaration(t.identifier(hookName), [optionsParam], t.blockStatement(hookBodyStatements));
|
|
167
|
+
const hookExport = t.exportNamedDeclaration(hookFunc);
|
|
168
|
+
(0, babel_ast_1.addJSDocComment)(hookExport, [
|
|
169
|
+
`Mutation hook for creating a ${typeName}`,
|
|
170
|
+
'',
|
|
171
|
+
'@example',
|
|
172
|
+
'```tsx',
|
|
173
|
+
`const { mutate, isPending } = ${hookName}();`,
|
|
174
|
+
'',
|
|
175
|
+
'mutate({',
|
|
176
|
+
' input: {',
|
|
177
|
+
` ${(0, utils_1.lcFirst)(typeName)}: {`,
|
|
178
|
+
' // ... fields',
|
|
179
|
+
' },',
|
|
180
|
+
' },',
|
|
181
|
+
'});',
|
|
182
|
+
'```',
|
|
183
|
+
]);
|
|
184
|
+
statements.push(hookExport);
|
|
185
|
+
const code = (0, babel_ast_1.generateCode)(statements);
|
|
186
|
+
const content = (0, utils_1.getGeneratedFileHeader)(`Create mutation hook for ${typeName}`) + '\n\n' + code;
|
|
179
187
|
return {
|
|
180
188
|
fileName: (0, utils_1.getCreateMutationFileName)(table),
|
|
181
|
-
content
|
|
189
|
+
content,
|
|
182
190
|
};
|
|
183
191
|
}
|
|
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
192
|
function generateUpdateMutationHook(table, options = {}) {
|
|
192
|
-
const { reactQueryEnabled = true, enumsFromSchemaTypes = [] } = options;
|
|
193
|
-
// Mutations require React Query - skip generation when disabled
|
|
193
|
+
const { reactQueryEnabled = true, enumsFromSchemaTypes = [], useCentralizedKeys = true, hasRelationships = false, } = options;
|
|
194
194
|
if (!reactQueryEnabled) {
|
|
195
195
|
return null;
|
|
196
196
|
}
|
|
197
|
-
// Check if update mutation exists
|
|
198
197
|
if (table.query?.update === null) {
|
|
199
198
|
return null;
|
|
200
199
|
}
|
|
201
200
|
const enumSet = new Set(enumsFromSchemaTypes);
|
|
202
|
-
const project = (0, ts_ast_1.createProject)();
|
|
203
201
|
const { typeName, singularName } = (0, utils_1.getTableNames)(table);
|
|
204
202
|
const hookName = (0, utils_1.getUpdateMutationHookName)(table);
|
|
205
203
|
const mutationName = (0, utils_1.getUpdateMutationName)(table);
|
|
206
204
|
const scalarFields = (0, utils_1.getScalarFields)(table);
|
|
207
|
-
|
|
205
|
+
const keysName = `${(0, utils_1.lcFirst)(typeName)}Keys`;
|
|
206
|
+
const mutationKeysName = `${(0, utils_1.lcFirst)(typeName)}MutationKeys`;
|
|
207
|
+
const scopeTypeName = `${typeName}Scope`;
|
|
208
208
|
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
|
|
209
|
+
const pkField = pkFields[0];
|
|
210
|
+
const pkFieldNames = new Set(pkFields.map((pk) => pk.name));
|
|
212
211
|
const usedEnums = new Set();
|
|
213
212
|
for (const field of scalarFields) {
|
|
214
213
|
const cleanType = field.type.gqlType.replace(/!/g, '');
|
|
@@ -216,266 +215,256 @@ function generateUpdateMutationHook(table, options = {}) {
|
|
|
216
215
|
usedEnums.add(cleanType);
|
|
217
216
|
}
|
|
218
217
|
}
|
|
219
|
-
// Generate GraphQL document via AST
|
|
220
218
|
const mutationAST = (0, gql_ast_1.buildUpdateMutationAST)({ table });
|
|
221
219
|
const mutationDocument = (0, gql_ast_1.printGraphQL)(mutationAST);
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
(0, ts_ast_1.createImport)({
|
|
237
|
-
moduleSpecifier: '../types',
|
|
238
|
-
typeOnlyNamedImports: [typeName],
|
|
239
|
-
}),
|
|
240
|
-
];
|
|
241
|
-
// Add import for enum types from schema-types if any are used
|
|
220
|
+
const statements = [];
|
|
221
|
+
const reactQueryImport = t.importDeclaration([
|
|
222
|
+
t.importSpecifier(t.identifier('useMutation'), t.identifier('useMutation')),
|
|
223
|
+
t.importSpecifier(t.identifier('useQueryClient'), t.identifier('useQueryClient')),
|
|
224
|
+
], t.stringLiteral('@tanstack/react-query'));
|
|
225
|
+
statements.push(reactQueryImport);
|
|
226
|
+
const reactQueryTypeImport = t.importDeclaration([t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], t.stringLiteral('@tanstack/react-query'));
|
|
227
|
+
reactQueryTypeImport.importKind = 'type';
|
|
228
|
+
statements.push(reactQueryTypeImport);
|
|
229
|
+
const clientImport = t.importDeclaration([t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], t.stringLiteral('../client'));
|
|
230
|
+
statements.push(clientImport);
|
|
231
|
+
const typesImport = t.importDeclaration([t.importSpecifier(t.identifier(typeName), t.identifier(typeName))], t.stringLiteral('../types'));
|
|
232
|
+
typesImport.importKind = 'type';
|
|
233
|
+
statements.push(typesImport);
|
|
242
234
|
if (usedEnums.size > 0) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
235
|
+
const enumImport = t.importDeclaration(Array.from(usedEnums).sort().map((e) => t.importSpecifier(t.identifier(e), t.identifier(e))), t.stringLiteral('../schema-types'));
|
|
236
|
+
enumImport.importKind = 'type';
|
|
237
|
+
statements.push(enumImport);
|
|
238
|
+
}
|
|
239
|
+
if (useCentralizedKeys) {
|
|
240
|
+
const queryKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], t.stringLiteral('../query-keys'));
|
|
241
|
+
statements.push(queryKeyImport);
|
|
242
|
+
if (hasRelationships) {
|
|
243
|
+
const scopeTypeImport = t.importDeclaration([t.importSpecifier(t.identifier(scopeTypeName), t.identifier(scopeTypeName))], t.stringLiteral('../query-keys'));
|
|
244
|
+
scopeTypeImport.importKind = 'type';
|
|
245
|
+
statements.push(scopeTypeImport);
|
|
246
|
+
}
|
|
247
|
+
const mutationKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(mutationKeysName), t.identifier(mutationKeysName))], t.stringLiteral('../mutation-keys'));
|
|
248
|
+
statements.push(mutationKeyImport);
|
|
247
249
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
sourceFile.addStatements('// ============================================================================\n');
|
|
256
|
-
// Add mutation document constant
|
|
257
|
-
sourceFile.addVariableStatement((0, ts_ast_1.createConst)(`${mutationName}MutationDocument`, '`\n' + mutationDocument + '`'));
|
|
258
|
-
// Add section comment
|
|
259
|
-
sourceFile.addStatements('\n// ============================================================================');
|
|
260
|
-
sourceFile.addStatements('// Types');
|
|
261
|
-
sourceFile.addStatements('// ============================================================================\n');
|
|
262
|
-
// Generate Patch type - all fields optional, exclude primary key
|
|
263
|
-
// Note: Not exported to avoid conflicts with schema-types
|
|
250
|
+
const reExportDecl = t.exportNamedDeclaration(null, [t.exportSpecifier(t.identifier(typeName), t.identifier(typeName))], t.stringLiteral('../types'));
|
|
251
|
+
reExportDecl.exportKind = 'type';
|
|
252
|
+
statements.push(reExportDecl);
|
|
253
|
+
const mutationDocConst = t.variableDeclaration('const', [
|
|
254
|
+
t.variableDeclarator(t.identifier(`${mutationName}MutationDocument`), t.templateLiteral([t.templateElement({ raw: '\n' + mutationDocument, cooked: '\n' + mutationDocument }, true)], [])),
|
|
255
|
+
]);
|
|
256
|
+
statements.push(t.exportNamedDeclaration(mutationDocConst));
|
|
264
257
|
const patchFields = scalarFields
|
|
265
258
|
.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
|
-
],
|
|
259
|
+
.map((f) => {
|
|
260
|
+
const prop = t.tsPropertySignature(t.identifier(f.name), t.tsTypeAnnotation(t.tsUnionType([
|
|
261
|
+
t.tsTypeReference(t.identifier((0, utils_1.fieldTypeToTs)(f.type))),
|
|
262
|
+
t.tsNullKeyword(),
|
|
263
|
+
])));
|
|
264
|
+
prop.optional = true;
|
|
265
|
+
return prop;
|
|
343
266
|
});
|
|
267
|
+
const patchInterface = t.tsInterfaceDeclaration(t.identifier(`${typeName}Patch`), null, null, t.tsInterfaceBody(patchFields));
|
|
268
|
+
(0, babel_ast_1.addJSDocComment)(patchInterface, [`Patch type for updating a ${typeName} - all fields optional`]);
|
|
269
|
+
statements.push(patchInterface);
|
|
270
|
+
const pkTypeAnnotation = pkField.tsType === 'string'
|
|
271
|
+
? t.tsStringKeyword()
|
|
272
|
+
: pkField.tsType === 'number'
|
|
273
|
+
? t.tsNumberKeyword()
|
|
274
|
+
: t.tsTypeReference(t.identifier(pkField.tsType));
|
|
275
|
+
const variablesInterfaceBody = t.tsInterfaceBody([
|
|
276
|
+
t.tsPropertySignature(t.identifier('input'), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
277
|
+
t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTypeAnnotation)),
|
|
278
|
+
t.tsPropertySignature(t.identifier('patch'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${typeName}Patch`)))),
|
|
279
|
+
]))),
|
|
280
|
+
]);
|
|
281
|
+
const variablesInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`), null, null, variablesInterfaceBody);
|
|
282
|
+
statements.push(t.exportNamedDeclaration(variablesInterface));
|
|
283
|
+
const resultInterfaceBody = t.tsInterfaceBody([
|
|
284
|
+
t.tsPropertySignature(t.identifier(mutationName), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
285
|
+
t.tsPropertySignature(t.identifier(singularName), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(typeName)))),
|
|
286
|
+
]))),
|
|
287
|
+
]);
|
|
288
|
+
const resultInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`), null, null, resultInterfaceBody);
|
|
289
|
+
statements.push(t.exportNamedDeclaration(resultInterface));
|
|
290
|
+
const hookBodyStatements = [];
|
|
291
|
+
hookBodyStatements.push(t.variableDeclaration('const', [
|
|
292
|
+
t.variableDeclarator(t.identifier('queryClient'), t.callExpression(t.identifier('useQueryClient'), [])),
|
|
293
|
+
]));
|
|
294
|
+
const mutationOptions = [];
|
|
295
|
+
if (useCentralizedKeys) {
|
|
296
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.memberExpression(t.identifier(mutationKeysName), t.identifier('all'))));
|
|
297
|
+
}
|
|
298
|
+
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`)))], t.callExpression(t.identifier('execute'), [
|
|
299
|
+
t.identifier(`${mutationName}MutationDocument`),
|
|
300
|
+
t.identifier('variables'),
|
|
301
|
+
]))));
|
|
302
|
+
const detailQueryKey = useCentralizedKeys
|
|
303
|
+
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name))])
|
|
304
|
+
: t.arrayExpression([
|
|
305
|
+
t.stringLiteral(typeName.toLowerCase()),
|
|
306
|
+
t.stringLiteral('detail'),
|
|
307
|
+
t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name)),
|
|
308
|
+
]);
|
|
309
|
+
const listQueryKey = useCentralizedKeys
|
|
310
|
+
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [])
|
|
311
|
+
: t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]);
|
|
312
|
+
mutationOptions.push(t.objectProperty(t.identifier('onSuccess'), t.arrowFunctionExpression([t.identifier('_'), t.identifier('variables')], t.blockStatement([
|
|
313
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), detailQueryKey)])])),
|
|
314
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), listQueryKey)])])),
|
|
315
|
+
]))));
|
|
316
|
+
mutationOptions.push(t.spreadElement(t.identifier('options')));
|
|
317
|
+
hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)])));
|
|
318
|
+
const optionsTypeStr = `Omit<UseMutationOptions<${(0, utils_1.ucFirst)(mutationName)}MutationResult, Error, ${(0, utils_1.ucFirst)(mutationName)}MutationVariables>, 'mutationFn'>`;
|
|
319
|
+
const optionsParam = t.identifier('options');
|
|
320
|
+
optionsParam.optional = true;
|
|
321
|
+
optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr)));
|
|
322
|
+
const hookFunc = t.functionDeclaration(t.identifier(hookName), [optionsParam], t.blockStatement(hookBodyStatements));
|
|
323
|
+
const hookExport = t.exportNamedDeclaration(hookFunc);
|
|
324
|
+
(0, babel_ast_1.addJSDocComment)(hookExport, [
|
|
325
|
+
`Mutation hook for updating a ${typeName}`,
|
|
326
|
+
'',
|
|
327
|
+
'@example',
|
|
328
|
+
'```tsx',
|
|
329
|
+
`const { mutate, isPending } = ${hookName}();`,
|
|
330
|
+
'',
|
|
331
|
+
'mutate({',
|
|
332
|
+
' input: {',
|
|
333
|
+
` ${pkField.name}: ${pkField.tsType === 'string' ? "'value-here'" : '123'},`,
|
|
334
|
+
' patch: {',
|
|
335
|
+
' // ... fields to update',
|
|
336
|
+
' },',
|
|
337
|
+
' },',
|
|
338
|
+
'});',
|
|
339
|
+
'```',
|
|
340
|
+
]);
|
|
341
|
+
statements.push(hookExport);
|
|
342
|
+
const code = (0, babel_ast_1.generateCode)(statements);
|
|
343
|
+
const content = (0, utils_1.getGeneratedFileHeader)(`Update mutation hook for ${typeName}`) + '\n\n' + code;
|
|
344
344
|
return {
|
|
345
345
|
fileName: (0, utils_1.getUpdateMutationFileName)(table),
|
|
346
|
-
content
|
|
346
|
+
content,
|
|
347
347
|
};
|
|
348
348
|
}
|
|
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
349
|
function generateDeleteMutationHook(table, options = {}) {
|
|
357
|
-
const { reactQueryEnabled = true } = options;
|
|
358
|
-
// Mutations require React Query - skip generation when disabled
|
|
350
|
+
const { reactQueryEnabled = true, useCentralizedKeys = true, hasRelationships = false, } = options;
|
|
359
351
|
if (!reactQueryEnabled) {
|
|
360
352
|
return null;
|
|
361
353
|
}
|
|
362
|
-
// Check if delete mutation exists
|
|
363
354
|
if (table.query?.delete === null) {
|
|
364
355
|
return null;
|
|
365
356
|
}
|
|
366
|
-
const project = (0, ts_ast_1.createProject)();
|
|
367
357
|
const { typeName } = (0, utils_1.getTableNames)(table);
|
|
368
358
|
const hookName = (0, utils_1.getDeleteMutationHookName)(table);
|
|
369
359
|
const mutationName = (0, utils_1.getDeleteMutationName)(table);
|
|
370
|
-
|
|
360
|
+
const keysName = `${(0, utils_1.lcFirst)(typeName)}Keys`;
|
|
361
|
+
const mutationKeysName = `${(0, utils_1.lcFirst)(typeName)}MutationKeys`;
|
|
362
|
+
const scopeTypeName = `${typeName}Scope`;
|
|
371
363
|
const pkFields = (0, utils_1.getPrimaryKeyInfo)(table);
|
|
372
|
-
const pkField = pkFields[0];
|
|
373
|
-
// Generate GraphQL document via AST
|
|
364
|
+
const pkField = pkFields[0];
|
|
374
365
|
const mutationAST = (0, gql_ast_1.buildDeleteMutationAST)({ table });
|
|
375
366
|
const mutationDocument = (0, gql_ast_1.printGraphQL)(mutationAST);
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
367
|
+
const statements = [];
|
|
368
|
+
const reactQueryImport = t.importDeclaration([
|
|
369
|
+
t.importSpecifier(t.identifier('useMutation'), t.identifier('useMutation')),
|
|
370
|
+
t.importSpecifier(t.identifier('useQueryClient'), t.identifier('useQueryClient')),
|
|
371
|
+
], t.stringLiteral('@tanstack/react-query'));
|
|
372
|
+
statements.push(reactQueryImport);
|
|
373
|
+
const reactQueryTypeImport = t.importDeclaration([t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], t.stringLiteral('@tanstack/react-query'));
|
|
374
|
+
reactQueryTypeImport.importKind = 'type';
|
|
375
|
+
statements.push(reactQueryTypeImport);
|
|
376
|
+
const clientImport = t.importDeclaration([t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], t.stringLiteral('../client'));
|
|
377
|
+
statements.push(clientImport);
|
|
378
|
+
if (useCentralizedKeys) {
|
|
379
|
+
const queryKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], t.stringLiteral('../query-keys'));
|
|
380
|
+
statements.push(queryKeyImport);
|
|
381
|
+
if (hasRelationships) {
|
|
382
|
+
const scopeTypeImport = t.importDeclaration([t.importSpecifier(t.identifier(scopeTypeName), t.identifier(scopeTypeName))], t.stringLiteral('../query-keys'));
|
|
383
|
+
scopeTypeImport.importKind = 'type';
|
|
384
|
+
statements.push(scopeTypeImport);
|
|
385
|
+
}
|
|
386
|
+
const mutationKeyImport = t.importDeclaration([t.importSpecifier(t.identifier(mutationKeysName), t.identifier(mutationKeysName))], t.stringLiteral('../mutation-keys'));
|
|
387
|
+
statements.push(mutationKeyImport);
|
|
388
|
+
}
|
|
389
|
+
const mutationDocConst = t.variableDeclaration('const', [
|
|
390
|
+
t.variableDeclarator(t.identifier(`${mutationName}MutationDocument`), t.templateLiteral([t.templateElement({ raw: '\n' + mutationDocument, cooked: '\n' + mutationDocument }, true)], [])),
|
|
390
391
|
]);
|
|
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
|
-
},
|
|
392
|
+
statements.push(t.exportNamedDeclaration(mutationDocConst));
|
|
393
|
+
const pkTypeAnnotation = pkField.tsType === 'string'
|
|
394
|
+
? t.tsStringKeyword()
|
|
395
|
+
: pkField.tsType === 'number'
|
|
396
|
+
? t.tsNumberKeyword()
|
|
397
|
+
: t.tsTypeReference(t.identifier(pkField.tsType));
|
|
398
|
+
const variablesInterfaceBody = t.tsInterfaceBody([
|
|
399
|
+
t.tsPropertySignature(t.identifier('input'), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
400
|
+
t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTypeAnnotation)),
|
|
401
|
+
]))),
|
|
402
|
+
]);
|
|
403
|
+
const variablesInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`), null, null, variablesInterfaceBody);
|
|
404
|
+
statements.push(t.exportNamedDeclaration(variablesInterface));
|
|
405
|
+
const deletedPkProp = t.tsPropertySignature(t.identifier(`deleted${(0, utils_1.ucFirst)(pkField.name)}`), t.tsTypeAnnotation(t.tsUnionType([pkTypeAnnotation, t.tsNullKeyword()])));
|
|
406
|
+
const clientMutationIdProp = t.tsPropertySignature(t.identifier('clientMutationId'), t.tsTypeAnnotation(t.tsUnionType([t.tsStringKeyword(), t.tsNullKeyword()])));
|
|
407
|
+
const resultInterfaceBody = t.tsInterfaceBody([
|
|
408
|
+
t.tsPropertySignature(t.identifier(mutationName), t.tsTypeAnnotation(t.tsTypeLiteral([clientMutationIdProp, deletedPkProp]))),
|
|
409
|
+
]);
|
|
410
|
+
const resultInterface = t.tsInterfaceDeclaration(t.identifier(`${(0, utils_1.ucFirst)(mutationName)}MutationResult`), null, null, resultInterfaceBody);
|
|
411
|
+
statements.push(t.exportNamedDeclaration(resultInterface));
|
|
412
|
+
const hookBodyStatements = [];
|
|
413
|
+
hookBodyStatements.push(t.variableDeclaration('const', [
|
|
414
|
+
t.variableDeclarator(t.identifier('queryClient'), t.callExpression(t.identifier('useQueryClient'), [])),
|
|
419
415
|
]));
|
|
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
|
-
});
|
|
416
|
+
const mutationOptions = [];
|
|
417
|
+
if (useCentralizedKeys) {
|
|
418
|
+
mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.memberExpression(t.identifier(mutationKeysName), t.identifier('all'))));
|
|
419
|
+
}
|
|
420
|
+
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`)))], t.callExpression(t.identifier('execute'), [
|
|
421
|
+
t.identifier(`${mutationName}MutationDocument`),
|
|
422
|
+
t.identifier('variables'),
|
|
423
|
+
]))));
|
|
424
|
+
const detailQueryKey = useCentralizedKeys
|
|
425
|
+
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name))])
|
|
426
|
+
: t.arrayExpression([
|
|
427
|
+
t.stringLiteral(typeName.toLowerCase()),
|
|
428
|
+
t.stringLiteral('detail'),
|
|
429
|
+
t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name)),
|
|
430
|
+
]);
|
|
431
|
+
const listQueryKey = useCentralizedKeys
|
|
432
|
+
? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [])
|
|
433
|
+
: t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]);
|
|
434
|
+
mutationOptions.push(t.objectProperty(t.identifier('onSuccess'), t.arrowFunctionExpression([t.identifier('_'), t.identifier('variables')], t.blockStatement([
|
|
435
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('removeQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), detailQueryKey)])])),
|
|
436
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), listQueryKey)])])),
|
|
437
|
+
]))));
|
|
438
|
+
mutationOptions.push(t.spreadElement(t.identifier('options')));
|
|
439
|
+
hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)])));
|
|
440
|
+
const optionsTypeStr = `Omit<UseMutationOptions<${(0, utils_1.ucFirst)(mutationName)}MutationResult, Error, ${(0, utils_1.ucFirst)(mutationName)}MutationVariables>, 'mutationFn'>`;
|
|
441
|
+
const optionsParam = t.identifier('options');
|
|
442
|
+
optionsParam.optional = true;
|
|
443
|
+
optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr)));
|
|
444
|
+
const hookFunc = t.functionDeclaration(t.identifier(hookName), [optionsParam], t.blockStatement(hookBodyStatements));
|
|
445
|
+
const hookExport = t.exportNamedDeclaration(hookFunc);
|
|
446
|
+
(0, babel_ast_1.addJSDocComment)(hookExport, [
|
|
447
|
+
`Mutation hook for deleting a ${typeName}`,
|
|
448
|
+
'',
|
|
449
|
+
'@example',
|
|
450
|
+
'```tsx',
|
|
451
|
+
`const { mutate, isPending } = ${hookName}();`,
|
|
452
|
+
'',
|
|
453
|
+
'mutate({',
|
|
454
|
+
' input: {',
|
|
455
|
+
` ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'},`,
|
|
456
|
+
' },',
|
|
457
|
+
'});',
|
|
458
|
+
'```',
|
|
459
|
+
]);
|
|
460
|
+
statements.push(hookExport);
|
|
461
|
+
const code = (0, babel_ast_1.generateCode)(statements);
|
|
462
|
+
const content = (0, utils_1.getGeneratedFileHeader)(`Delete mutation hook for ${typeName}`) + '\n\n' + code;
|
|
467
463
|
return {
|
|
468
464
|
fileName: (0, utils_1.getDeleteMutationFileName)(table),
|
|
469
|
-
content
|
|
465
|
+
content,
|
|
470
466
|
};
|
|
471
467
|
}
|
|
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
468
|
function generateAllMutationHooks(tables, options = {}) {
|
|
480
469
|
const files = [];
|
|
481
470
|
for (const table of tables) {
|