@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.
Files changed (92) hide show
  1. package/README.md +147 -2
  2. package/cli/codegen/babel-ast.d.ts +53 -0
  3. package/cli/codegen/babel-ast.js +160 -0
  4. package/cli/codegen/barrel.d.ts +7 -2
  5. package/cli/codegen/barrel.js +193 -102
  6. package/cli/codegen/client.js +61 -0
  7. package/cli/codegen/custom-mutations.d.ts +2 -12
  8. package/cli/codegen/custom-mutations.js +116 -124
  9. package/cli/codegen/custom-queries.d.ts +2 -10
  10. package/cli/codegen/custom-queries.js +236 -335
  11. package/cli/codegen/gql-ast.js +22 -1
  12. package/cli/codegen/index.d.ts +3 -0
  13. package/cli/codegen/index.js +73 -3
  14. package/cli/codegen/invalidation.d.ts +20 -0
  15. package/cli/codegen/invalidation.js +327 -0
  16. package/cli/codegen/mutation-keys.d.ts +24 -0
  17. package/cli/codegen/mutation-keys.js +247 -0
  18. package/cli/codegen/mutations.d.ts +5 -19
  19. package/cli/codegen/mutations.js +385 -383
  20. package/cli/codegen/orm/barrel.d.ts +1 -1
  21. package/cli/codegen/orm/barrel.js +42 -10
  22. package/cli/codegen/orm/client-generator.d.ts +1 -19
  23. package/cli/codegen/orm/client-generator.js +108 -77
  24. package/cli/codegen/orm/custom-ops-generator.d.ts +1 -12
  25. package/cli/codegen/orm/custom-ops-generator.js +192 -235
  26. package/cli/codegen/orm/input-types-generator.d.ts +13 -1
  27. package/cli/codegen/orm/input-types-generator.js +425 -147
  28. package/cli/codegen/orm/model-generator.d.ts +1 -19
  29. package/cli/codegen/orm/model-generator.js +229 -234
  30. package/cli/codegen/queries.d.ts +4 -12
  31. package/cli/codegen/queries.js +660 -390
  32. package/cli/codegen/query-keys.d.ts +15 -0
  33. package/cli/codegen/query-keys.js +477 -0
  34. package/cli/codegen/scalars.js +1 -0
  35. package/cli/codegen/schema-types-generator.d.ts +15 -10
  36. package/cli/codegen/schema-types-generator.js +87 -175
  37. package/cli/codegen/type-resolver.d.ts +1 -30
  38. package/cli/codegen/type-resolver.js +0 -53
  39. package/cli/codegen/types.d.ts +1 -1
  40. package/cli/codegen/types.js +76 -21
  41. package/cli/codegen/utils.d.ts +6 -0
  42. package/cli/codegen/utils.js +19 -0
  43. package/esm/cli/codegen/babel-ast.d.ts +53 -0
  44. package/esm/cli/codegen/babel-ast.js +111 -0
  45. package/esm/cli/codegen/barrel.d.ts +7 -2
  46. package/esm/cli/codegen/barrel.js +161 -103
  47. package/esm/cli/codegen/client.js +61 -0
  48. package/esm/cli/codegen/custom-mutations.d.ts +2 -12
  49. package/esm/cli/codegen/custom-mutations.js +83 -124
  50. package/esm/cli/codegen/custom-queries.d.ts +2 -10
  51. package/esm/cli/codegen/custom-queries.js +204 -336
  52. package/esm/cli/codegen/gql-ast.js +23 -2
  53. package/esm/cli/codegen/index.d.ts +3 -0
  54. package/esm/cli/codegen/index.js +69 -2
  55. package/esm/cli/codegen/invalidation.d.ts +20 -0
  56. package/esm/cli/codegen/invalidation.js +291 -0
  57. package/esm/cli/codegen/mutation-keys.d.ts +24 -0
  58. package/esm/cli/codegen/mutation-keys.js +211 -0
  59. package/esm/cli/codegen/mutations.d.ts +5 -19
  60. package/esm/cli/codegen/mutations.js +353 -384
  61. package/esm/cli/codegen/orm/barrel.d.ts +1 -1
  62. package/esm/cli/codegen/orm/barrel.js +10 -11
  63. package/esm/cli/codegen/orm/client-generator.d.ts +1 -19
  64. package/esm/cli/codegen/orm/client-generator.js +76 -78
  65. package/esm/cli/codegen/orm/custom-ops-generator.d.ts +1 -12
  66. package/esm/cli/codegen/orm/custom-ops-generator.js +160 -236
  67. package/esm/cli/codegen/orm/input-types-generator.d.ts +13 -1
  68. package/esm/cli/codegen/orm/input-types-generator.js +393 -148
  69. package/esm/cli/codegen/orm/model-generator.d.ts +1 -19
  70. package/esm/cli/codegen/orm/model-generator.js +197 -235
  71. package/esm/cli/codegen/queries.d.ts +4 -12
  72. package/esm/cli/codegen/queries.js +628 -391
  73. package/esm/cli/codegen/query-keys.d.ts +15 -0
  74. package/esm/cli/codegen/query-keys.js +441 -0
  75. package/esm/cli/codegen/scalars.js +1 -0
  76. package/esm/cli/codegen/schema-types-generator.d.ts +15 -10
  77. package/esm/cli/codegen/schema-types-generator.js +54 -175
  78. package/esm/cli/codegen/type-resolver.d.ts +1 -30
  79. package/esm/cli/codegen/type-resolver.js +0 -49
  80. package/esm/cli/codegen/types.d.ts +1 -1
  81. package/esm/cli/codegen/types.js +44 -22
  82. package/esm/cli/codegen/utils.d.ts +6 -0
  83. package/esm/cli/codegen/utils.js +18 -0
  84. package/esm/types/config.d.ts +75 -0
  85. package/esm/types/config.js +18 -0
  86. package/package.json +6 -4
  87. package/types/config.d.ts +75 -0
  88. package/types/config.js +19 -1
  89. package/cli/codegen/ts-ast.d.ts +0 -124
  90. package/cli/codegen/ts-ast.js +0 -280
  91. package/esm/cli/codegen/ts-ast.d.ts +0 -124
  92. package/esm/cli/codegen/ts-ast.js +0 -260
@@ -1,13 +1,18 @@
1
- import { createProject, createSourceFile, getFormattedOutput, createFileHeader, createImport, createInterface, createConst, } from './ts-ast';
1
+ import * as t from '@babel/types';
2
+ import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast';
2
3
  import { buildCustomMutationString } from './schema-gql-ast';
3
4
  import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, createTypeTracker, } from './type-resolver';
4
- /**
5
- * Generate a custom mutation hook file
6
- * When reactQueryEnabled is false, returns null since mutations require React Query
7
- */
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
+ }
8
14
  export function generateCustomMutationHook(options) {
9
15
  const { operation, reactQueryEnabled = true } = options;
10
- // Mutations require React Query - skip generation when disabled
11
16
  if (!reactQueryEnabled) {
12
17
  return null;
13
18
  }
@@ -21,150 +26,103 @@ export function generateCustomMutationHook(options) {
21
26
  }
22
27
  }
23
28
  function generateCustomMutationHookInternal(options) {
24
- const { operation, typeRegistry, maxDepth = 2, skipQueryField = true, tableTypeNames } = options;
25
- const project = createProject();
29
+ const { operation, typeRegistry, maxDepth = 2, skipQueryField = true, tableTypeNames, useCentralizedKeys = true, } = options;
26
30
  const hookName = getOperationHookName(operation.name, 'mutation');
27
31
  const fileName = getOperationFileName(operation.name, 'mutation');
28
32
  const variablesTypeName = getOperationVariablesTypeName(operation.name, 'mutation');
29
33
  const resultTypeName = getOperationResultTypeName(operation.name, 'mutation');
30
34
  const documentConstName = getDocumentConstName(operation.name, 'mutation');
31
- // Create type tracker to collect referenced types (with table type awareness)
32
35
  const tracker = createTypeTracker({ tableTypeNames });
33
- // Generate GraphQL document
34
36
  const mutationDocument = buildCustomMutationString({
35
37
  operation,
36
38
  typeRegistry,
37
39
  maxDepth,
38
40
  skipQueryField,
39
41
  });
40
- const sourceFile = createSourceFile(project, fileName);
41
- // Add file header
42
- sourceFile.insertText(0, createFileHeader(`Custom mutation hook for ${operation.name}`) + '\n\n');
43
- // Generate variables interface if there are arguments (with tracking)
44
- let variablesProps = [];
45
- if (operation.args.length > 0) {
46
- variablesProps = generateVariablesProperties(operation.args, tracker);
47
- }
48
- // Generate result interface (with tracking)
42
+ const statements = [];
43
+ const variablesProps = operation.args.length > 0
44
+ ? generateVariablesProperties(operation.args, tracker)
45
+ : [];
49
46
  const resultType = typeRefToTsType(operation.returnType, tracker);
50
- const resultProps = [
51
- { name: operation.name, type: resultType },
52
- ];
53
- // Get importable types from tracker (separated by source)
54
- const schemaTypes = tracker.getImportableTypes(); // From schema-types.ts
55
- const tableTypes = tracker.getTableTypes(); // From types.ts
56
- // Add imports
57
- const imports = [
58
- createImport({
59
- moduleSpecifier: '@tanstack/react-query',
60
- namedImports: ['useMutation'],
61
- typeOnlyNamedImports: ['UseMutationOptions'],
62
- }),
63
- createImport({
64
- moduleSpecifier: '../client',
65
- namedImports: ['execute'],
66
- }),
67
- ];
68
- // Add types.ts import for table entity types
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);
69
56
  if (tableTypes.length > 0) {
70
- imports.push(createImport({
71
- moduleSpecifier: '../types',
72
- typeOnlyNamedImports: tableTypes,
73
- }));
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);
74
60
  }
75
- // Add schema-types import for Input/Payload/Enum types
76
61
  if (schemaTypes.length > 0) {
77
- imports.push(createImport({
78
- moduleSpecifier: '../schema-types',
79
- typeOnlyNamedImports: schemaTypes,
80
- }));
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);
81
65
  }
82
- sourceFile.addImportDeclarations(imports);
83
- // Add mutation document constant
84
- sourceFile.addVariableStatement(createConst(documentConstName, '`\n' + mutationDocument + '`', {
85
- docs: ['GraphQL mutation document'],
86
- }));
87
- // Add variables interface
66
+ if (useCentralizedKeys) {
67
+ const mutationKeyImport = t.importDeclaration([t.importSpecifier(t.identifier('customMutationKeys'), t.identifier('customMutationKeys'))], t.stringLiteral('../mutation-keys'));
68
+ statements.push(mutationKeyImport);
69
+ }
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);
88
76
  if (operation.args.length > 0) {
89
- sourceFile.addInterface(createInterface(variablesTypeName, variablesProps));
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));
90
84
  }
91
- // Add result interface
92
- sourceFile.addInterface(createInterface(resultTypeName, resultProps));
93
- // Generate hook function
94
- const hookParams = generateHookParameters(operation, variablesTypeName, resultTypeName);
95
- const hookBody = generateHookBody(operation, documentConstName, variablesTypeName, resultTypeName);
96
- // Note: docs can cause ts-morph issues with certain content, so we skip them
97
- sourceFile.addFunction({
98
- name: hookName,
99
- isExported: true,
100
- parameters: hookParams,
101
- statements: hookBody,
102
- });
103
- return {
104
- fileName,
105
- content: getFormattedOutput(sourceFile),
106
- operationName: operation.name,
107
- };
108
- }
109
- // ============================================================================
110
- // Helper functions
111
- // ============================================================================
112
- /**
113
- * Generate interface properties from CleanArguments
114
- */
115
- function generateVariablesProperties(args, tracker) {
116
- return args.map((arg) => ({
117
- name: arg.name,
118
- type: typeRefToTsType(arg.type, tracker),
119
- optional: !isTypeRequired(arg.type),
120
- docs: arg.description ? [arg.description] : undefined,
121
- }));
122
- }
123
- /**
124
- * Generate hook function parameters
125
- */
126
- function generateHookParameters(operation, variablesTypeName, resultTypeName) {
127
- const hasArgs = operation.args.length > 0;
128
- // Mutation hooks use UseMutationOptions with variables as the second type param
129
- const optionsType = hasArgs
130
- ? `Omit<UseMutationOptions<${resultTypeName}, Error, ${variablesTypeName}>, 'mutationFn'>`
131
- : `Omit<UseMutationOptions<${resultTypeName}, Error, void>, 'mutationFn'>`;
132
- return [
133
- {
134
- name: 'options',
135
- type: optionsType,
136
- hasQuestionToken: true,
137
- },
138
- ];
139
- }
140
- /**
141
- * Generate hook function body
142
- */
143
- function generateHookBody(operation, documentConstName, variablesTypeName, resultTypeName) {
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));
144
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)), [])));
95
+ }
145
96
  if (hasArgs) {
146
- return `return useMutation({
147
- mutationFn: (variables: ${variablesTypeName}) =>
148
- execute<${resultTypeName}, ${variablesTypeName}>(
149
- ${documentConstName},
150
- variables
151
- ),
152
- ...options,
153
- });`;
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
+ ]))));
154
101
  }
155
102
  else {
156
- return `return useMutation({
157
- mutationFn: () => execute<${resultTypeName}>(${documentConstName}),
158
- ...options,
159
- });`;
103
+ mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(documentConstName)], [t.tsTypeReference(t.identifier(resultTypeName))]))));
160
104
  }
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;
118
+ return {
119
+ fileName,
120
+ content,
121
+ operationName: operation.name,
122
+ };
161
123
  }
162
- /**
163
- * Generate all custom mutation hook files
164
- * When reactQueryEnabled is false, returns empty array since mutations require React Query
165
- */
166
124
  export function generateAllCustomMutationHooks(options) {
167
- const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames } = options;
125
+ const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames, useCentralizedKeys = true, } = options;
168
126
  return operations
169
127
  .filter((op) => op.kind === 'mutation')
170
128
  .map((operation) => generateCustomMutationHook({
@@ -174,6 +132,7 @@ export function generateAllCustomMutationHooks(options) {
174
132
  skipQueryField,
175
133
  reactQueryEnabled,
176
134
  tableTypeNames,
135
+ useCentralizedKeys,
177
136
  }))
178
137
  .filter((result) => result !== null);
179
138
  }
@@ -21,26 +21,18 @@ export interface GenerateCustomQueryHookOptions {
21
21
  typeRegistry: TypeRegistry;
22
22
  maxDepth?: number;
23
23
  skipQueryField?: boolean;
24
- /** Whether to generate React Query hooks (default: true for backwards compatibility) */
25
24
  reactQueryEnabled?: boolean;
26
- /** Table entity type names (for import path resolution) */
27
25
  tableTypeNames?: Set<string>;
26
+ useCentralizedKeys?: boolean;
28
27
  }
29
- /**
30
- * Generate a custom query hook file
31
- */
32
28
  export declare function generateCustomQueryHook(options: GenerateCustomQueryHookOptions): GeneratedCustomQueryFile;
33
29
  export interface GenerateAllCustomQueryHooksOptions {
34
30
  operations: CleanOperation[];
35
31
  typeRegistry: TypeRegistry;
36
32
  maxDepth?: number;
37
33
  skipQueryField?: boolean;
38
- /** Whether to generate React Query hooks (default: true for backwards compatibility) */
39
34
  reactQueryEnabled?: boolean;
40
- /** Table entity type names (for import path resolution) */
41
35
  tableTypeNames?: Set<string>;
36
+ useCentralizedKeys?: boolean;
42
37
  }
43
- /**
44
- * Generate all custom query hook files
45
- */
46
38
  export declare function generateAllCustomQueryHooks(options: GenerateAllCustomQueryHooksOptions): GeneratedCustomQueryFile[];