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