@constructive-io/graphql-codegen 2.24.0 → 2.26.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.
Files changed (66) hide show
  1. package/README.md +403 -279
  2. package/cli/codegen/babel-ast.d.ts +7 -0
  3. package/cli/codegen/babel-ast.js +15 -0
  4. package/cli/codegen/barrel.js +43 -14
  5. package/cli/codegen/custom-mutations.js +4 -4
  6. package/cli/codegen/custom-queries.js +12 -22
  7. package/cli/codegen/gql-ast.js +22 -1
  8. package/cli/codegen/index.js +1 -0
  9. package/cli/codegen/mutations.d.ts +2 -0
  10. package/cli/codegen/mutations.js +26 -13
  11. package/cli/codegen/orm/client-generator.js +475 -136
  12. package/cli/codegen/orm/custom-ops-generator.js +8 -3
  13. package/cli/codegen/orm/input-types-generator.js +22 -0
  14. package/cli/codegen/orm/model-generator.js +18 -5
  15. package/cli/codegen/orm/select-types.d.ts +33 -0
  16. package/cli/codegen/queries.d.ts +1 -1
  17. package/cli/codegen/queries.js +112 -35
  18. package/cli/codegen/utils.d.ts +6 -0
  19. package/cli/codegen/utils.js +19 -0
  20. package/cli/commands/generate-orm.d.ts +14 -0
  21. package/cli/commands/generate-orm.js +160 -44
  22. package/cli/commands/generate.d.ts +22 -0
  23. package/cli/commands/generate.js +195 -55
  24. package/cli/commands/init.js +29 -9
  25. package/cli/index.js +133 -28
  26. package/cli/watch/orchestrator.d.ts +4 -0
  27. package/cli/watch/orchestrator.js +4 -0
  28. package/esm/cli/codegen/babel-ast.d.ts +7 -0
  29. package/esm/cli/codegen/babel-ast.js +14 -0
  30. package/esm/cli/codegen/barrel.js +44 -15
  31. package/esm/cli/codegen/custom-mutations.js +5 -5
  32. package/esm/cli/codegen/custom-queries.js +13 -23
  33. package/esm/cli/codegen/gql-ast.js +23 -2
  34. package/esm/cli/codegen/index.js +1 -0
  35. package/esm/cli/codegen/mutations.d.ts +2 -0
  36. package/esm/cli/codegen/mutations.js +27 -14
  37. package/esm/cli/codegen/orm/client-generator.js +475 -136
  38. package/esm/cli/codegen/orm/custom-ops-generator.js +8 -3
  39. package/esm/cli/codegen/orm/input-types-generator.js +22 -0
  40. package/esm/cli/codegen/orm/model-generator.js +18 -5
  41. package/esm/cli/codegen/orm/select-types.d.ts +33 -0
  42. package/esm/cli/codegen/queries.d.ts +1 -1
  43. package/esm/cli/codegen/queries.js +114 -37
  44. package/esm/cli/codegen/utils.d.ts +6 -0
  45. package/esm/cli/codegen/utils.js +18 -0
  46. package/esm/cli/commands/generate-orm.d.ts +14 -0
  47. package/esm/cli/commands/generate-orm.js +161 -45
  48. package/esm/cli/commands/generate.d.ts +22 -0
  49. package/esm/cli/commands/generate.js +195 -56
  50. package/esm/cli/commands/init.js +29 -9
  51. package/esm/cli/index.js +134 -29
  52. package/esm/cli/watch/orchestrator.d.ts +4 -0
  53. package/esm/cli/watch/orchestrator.js +5 -1
  54. package/esm/types/config.d.ts +39 -2
  55. package/esm/types/config.js +88 -4
  56. package/esm/types/index.d.ts +2 -2
  57. package/esm/types/index.js +1 -1
  58. package/package.json +10 -7
  59. package/types/config.d.ts +39 -2
  60. package/types/config.js +91 -4
  61. package/types/index.d.ts +2 -2
  62. package/types/index.js +2 -1
  63. package/cli/codegen/orm/query-builder.d.ts +0 -161
  64. package/cli/codegen/orm/query-builder.js +0 -366
  65. package/esm/cli/codegen/orm/query-builder.d.ts +0 -161
  66. package/esm/cli/codegen/orm/query-builder.js +0 -353
@@ -6,7 +6,7 @@
6
6
  * 2. Infers table metadata from introspection (replaces _meta)
7
7
  * 3. Generates a Prisma-like ORM client with fluent API
8
8
  */
9
- import { resolveConfig } from '../../types/config';
9
+ import { isMultiConfig, mergeConfig, resolveConfig } from '../../types/config';
10
10
  import { createSchemaSource, validateSourceOptions, } from '../introspect/source';
11
11
  import { runCodegenPipeline, validateTablesFound } from './shared';
12
12
  import { findConfigFile, loadConfigFile } from './init';
@@ -16,9 +16,9 @@ import { generateOrm } from '../codegen/orm';
16
16
  * Execute the generate-orm command
17
17
  */
18
18
  export async function generateOrmCommand(options = {}) {
19
- const log = options.verbose ? console.log : () => { };
20
- // 1. Load config
21
- log('Loading configuration...');
19
+ if (options.verbose) {
20
+ console.log('Loading configuration...');
21
+ }
22
22
  const configResult = await loadConfig(options);
23
23
  if (!configResult.success) {
24
24
  return {
@@ -26,26 +26,73 @@ export async function generateOrmCommand(options = {}) {
26
26
  message: configResult.error,
27
27
  };
28
28
  }
29
- const config = configResult.config;
30
- // Use ORM output directory if specified, otherwise default
31
- const outputDir = options.output || config.orm?.output || './generated/orm';
32
- // Log source
33
- if (config.schema) {
34
- log(` Schema: ${config.schema}`);
29
+ const targets = configResult.targets ?? [];
30
+ if (targets.length === 0) {
31
+ return {
32
+ success: false,
33
+ message: 'No targets resolved from configuration.',
34
+ };
35
+ }
36
+ const isMultiTarget = configResult.isMulti ?? targets.length > 1;
37
+ const results = [];
38
+ for (const target of targets) {
39
+ const result = await generateOrmForTarget(target, options, isMultiTarget);
40
+ results.push(result);
41
+ }
42
+ if (!isMultiTarget) {
43
+ const [result] = results;
44
+ return {
45
+ success: result.success,
46
+ message: result.message,
47
+ targets: results,
48
+ tables: result.tables,
49
+ customQueries: result.customQueries,
50
+ customMutations: result.customMutations,
51
+ filesWritten: result.filesWritten,
52
+ errors: result.errors,
53
+ };
35
54
  }
36
- else {
37
- log(` Endpoint: ${config.endpoint}`);
55
+ const successCount = results.filter((result) => result.success).length;
56
+ const failedCount = results.length - successCount;
57
+ const summaryMessage = failedCount === 0
58
+ ? `Generated ORM clients for ${results.length} targets.`
59
+ : `Generated ORM clients for ${successCount} of ${results.length} targets.`;
60
+ return {
61
+ success: failedCount === 0,
62
+ message: summaryMessage,
63
+ targets: results,
64
+ errors: failedCount > 0
65
+ ? results.flatMap((result) => result.errors ?? [])
66
+ : undefined,
67
+ };
68
+ }
69
+ async function generateOrmForTarget(target, options, isMultiTarget) {
70
+ const config = target.config;
71
+ const outputDir = options.output || config.orm?.output || './generated/orm';
72
+ const prefix = isMultiTarget ? `[${target.name}] ` : '';
73
+ const log = options.verbose
74
+ ? (message) => console.log(`${prefix}${message}`)
75
+ : () => { };
76
+ const formatMessage = (message) => isMultiTarget ? `Target "${target.name}": ${message}` : message;
77
+ if (isMultiTarget) {
78
+ console.log(`\nTarget "${target.name}"`);
79
+ const sourceLabel = config.schema
80
+ ? `schema: ${config.schema}`
81
+ : `endpoint: ${config.endpoint}`;
82
+ console.log(` Source: ${sourceLabel}`);
83
+ console.log(` Output: ${outputDir}`);
38
84
  }
39
- log(` Output: ${outputDir}`);
40
- // 2. Create schema source
85
+ // 1. Validate source
41
86
  const sourceValidation = validateSourceOptions({
42
87
  endpoint: config.endpoint || undefined,
43
88
  schema: config.schema || undefined,
44
89
  });
45
90
  if (!sourceValidation.valid) {
46
91
  return {
92
+ name: target.name,
93
+ output: outputDir,
47
94
  success: false,
48
- message: sourceValidation.error,
95
+ message: formatMessage(sourceValidation.error),
49
96
  };
50
97
  }
51
98
  const source = createSchemaSource({
@@ -54,7 +101,7 @@ export async function generateOrmCommand(options = {}) {
54
101
  authorization: options.authorization || config.headers['Authorization'],
55
102
  headers: config.headers,
56
103
  });
57
- // 3. Run the codegen pipeline
104
+ // 2. Run the codegen pipeline
58
105
  let pipelineResult;
59
106
  try {
60
107
  pipelineResult = await runCodegenPipeline({
@@ -66,21 +113,25 @@ export async function generateOrmCommand(options = {}) {
66
113
  }
67
114
  catch (err) {
68
115
  return {
116
+ name: target.name,
117
+ output: outputDir,
69
118
  success: false,
70
- message: `Failed to fetch schema: ${err instanceof Error ? err.message : 'Unknown error'}`,
119
+ message: formatMessage(`Failed to fetch schema: ${err instanceof Error ? err.message : 'Unknown error'}`),
71
120
  };
72
121
  }
73
122
  const { tables, customOperations, stats } = pipelineResult;
74
- // 4. Validate tables found
123
+ // 3. Validate tables found
75
124
  const tablesValidation = validateTablesFound(tables);
76
125
  if (!tablesValidation.valid) {
77
126
  return {
127
+ name: target.name,
128
+ output: outputDir,
78
129
  success: false,
79
- message: tablesValidation.error,
130
+ message: formatMessage(tablesValidation.error),
80
131
  };
81
132
  }
82
- // 5. Generate ORM code
83
- console.log('Generating code...');
133
+ // 4. Generate ORM code
134
+ console.log(`${prefix}Generating code...`);
84
135
  const { files: generatedFiles, stats: genStats } = generateOrm({
85
136
  tables,
86
137
  customOperations: {
@@ -90,7 +141,7 @@ export async function generateOrmCommand(options = {}) {
90
141
  },
91
142
  config,
92
143
  });
93
- console.log(`Generated ${genStats.totalFiles} files`);
144
+ console.log(`${prefix}Generated ${genStats.totalFiles} files`);
94
145
  log(` ${genStats.tables} table models`);
95
146
  log(` ${genStats.customQueries} custom query operations`);
96
147
  log(` ${genStats.customMutations} custom mutation operations`);
@@ -98,15 +149,17 @@ export async function generateOrmCommand(options = {}) {
98
149
  const customMutations = customOperations.mutations.map((m) => m.name);
99
150
  if (options.dryRun) {
100
151
  return {
152
+ name: target.name,
153
+ output: outputDir,
101
154
  success: true,
102
- message: `Dry run complete. Would generate ${generatedFiles.length} files for ${tables.length} tables and ${stats.customQueries + stats.customMutations} custom operations.`,
155
+ message: formatMessage(`Dry run complete. Would generate ${generatedFiles.length} files for ${tables.length} tables and ${stats.customQueries + stats.customMutations} custom operations.`),
103
156
  tables: tables.map((t) => t.name),
104
157
  customQueries,
105
158
  customMutations,
106
159
  filesWritten: generatedFiles.map((f) => f.path),
107
160
  };
108
161
  }
109
- // 6. Write files
162
+ // 5. Write files
110
163
  log('Writing files...');
111
164
  const writeResult = await writeGeneratedFiles(generatedFiles, outputDir, [
112
165
  'models',
@@ -115,23 +168,45 @@ export async function generateOrmCommand(options = {}) {
115
168
  ]);
116
169
  if (!writeResult.success) {
117
170
  return {
171
+ name: target.name,
172
+ output: outputDir,
118
173
  success: false,
119
- message: `Failed to write files: ${writeResult.errors?.join(', ')}`,
174
+ message: formatMessage(`Failed to write files: ${writeResult.errors?.join(', ')}`),
120
175
  errors: writeResult.errors,
121
176
  };
122
177
  }
123
178
  const totalOps = customQueries.length + customMutations.length;
124
179
  const customOpsMsg = totalOps > 0 ? ` and ${totalOps} custom operations` : '';
125
180
  return {
181
+ name: target.name,
182
+ output: outputDir,
126
183
  success: true,
127
- message: `Generated ORM client for ${tables.length} tables${customOpsMsg}. Files written to ${outputDir}`,
184
+ message: formatMessage(`Generated ORM client for ${tables.length} tables${customOpsMsg}. Files written to ${outputDir}`),
128
185
  tables: tables.map((t) => t.name),
129
186
  customQueries,
130
187
  customMutations,
131
188
  filesWritten: writeResult.filesWritten,
132
189
  };
133
190
  }
191
+ function buildTargetOverrides(options) {
192
+ const overrides = {};
193
+ if (options.endpoint) {
194
+ overrides.endpoint = options.endpoint;
195
+ overrides.schema = undefined;
196
+ }
197
+ if (options.schema) {
198
+ overrides.schema = options.schema;
199
+ overrides.endpoint = undefined;
200
+ }
201
+ return overrides;
202
+ }
134
203
  async function loadConfig(options) {
204
+ if (options.endpoint && options.schema) {
205
+ return {
206
+ success: false,
207
+ error: 'Cannot use both --endpoint and --schema. Choose one source.',
208
+ };
209
+ }
135
210
  // Find config file
136
211
  let configPath = options.config;
137
212
  if (!configPath) {
@@ -145,29 +220,70 @@ async function loadConfig(options) {
145
220
  }
146
221
  baseConfig = loadResult.config;
147
222
  }
148
- // Override with CLI options
149
- const mergedConfig = {
150
- endpoint: options.endpoint || baseConfig.endpoint,
151
- schema: options.schema || baseConfig.schema,
152
- output: options.output || baseConfig.output,
153
- headers: baseConfig.headers,
154
- tables: baseConfig.tables,
155
- queries: baseConfig.queries,
156
- mutations: baseConfig.mutations,
157
- excludeFields: baseConfig.excludeFields,
158
- hooks: baseConfig.hooks,
159
- postgraphile: baseConfig.postgraphile,
160
- codegen: baseConfig.codegen,
161
- orm: baseConfig.orm,
162
- };
163
- // Validate at least one source is provided
223
+ const overrides = buildTargetOverrides(options);
224
+ if (isMultiConfig(baseConfig)) {
225
+ if (Object.keys(baseConfig.targets).length === 0) {
226
+ return {
227
+ success: false,
228
+ error: 'Config file defines no targets.',
229
+ };
230
+ }
231
+ if (!options.target &&
232
+ (options.endpoint || options.schema || options.output)) {
233
+ return {
234
+ success: false,
235
+ error: 'Multiple targets configured. Use --target with --endpoint, --schema, or --output.',
236
+ };
237
+ }
238
+ if (options.target && !baseConfig.targets[options.target]) {
239
+ return {
240
+ success: false,
241
+ error: `Target "${options.target}" not found in config file.`,
242
+ };
243
+ }
244
+ const selectedTargets = options.target
245
+ ? { [options.target]: baseConfig.targets[options.target] }
246
+ : baseConfig.targets;
247
+ const defaults = baseConfig.defaults ?? {};
248
+ const resolvedTargets = [];
249
+ for (const [name, target] of Object.entries(selectedTargets)) {
250
+ let mergedTarget = mergeConfig(defaults, target);
251
+ if (options.target && name === options.target) {
252
+ mergedTarget = mergeConfig(mergedTarget, overrides);
253
+ }
254
+ if (!mergedTarget.endpoint && !mergedTarget.schema) {
255
+ return {
256
+ success: false,
257
+ error: `Target "${name}" is missing an endpoint or schema.`,
258
+ };
259
+ }
260
+ resolvedTargets.push({
261
+ name,
262
+ config: resolveConfig(mergedTarget),
263
+ });
264
+ }
265
+ return {
266
+ success: true,
267
+ targets: resolvedTargets,
268
+ isMulti: true,
269
+ };
270
+ }
271
+ if (options.target) {
272
+ return {
273
+ success: false,
274
+ error: 'Config file does not define targets. Remove --target to continue.',
275
+ };
276
+ }
277
+ const mergedConfig = mergeConfig(baseConfig, overrides);
164
278
  if (!mergedConfig.endpoint && !mergedConfig.schema) {
165
279
  return {
166
280
  success: false,
167
281
  error: 'No source specified. Use --endpoint or --schema, or create a config file with "graphql-codegen init".',
168
282
  };
169
283
  }
170
- // Resolve with defaults
171
- const config = resolveConfig(mergedConfig);
172
- return { success: true, config };
284
+ return {
285
+ success: true,
286
+ targets: [{ name: 'default', config: resolveConfig(mergedConfig) }],
287
+ isMulti: false,
288
+ };
173
289
  }
@@ -1,6 +1,8 @@
1
1
  export interface GenerateOptions {
2
2
  /** Path to config file */
3
3
  config?: string;
4
+ /** Named target in a multi-target config */
5
+ target?: string;
4
6
  /** GraphQL endpoint URL (overrides config) */
5
7
  endpoint?: string;
6
8
  /** Path to GraphQL schema file (.graphql) */
@@ -16,9 +18,21 @@ export interface GenerateOptions {
16
18
  /** Skip custom operations (only generate table CRUD) */
17
19
  skipCustomOperations?: boolean;
18
20
  }
21
+ export interface GenerateTargetResult {
22
+ name: string;
23
+ output: string;
24
+ success: boolean;
25
+ message: string;
26
+ tables?: string[];
27
+ customQueries?: string[];
28
+ customMutations?: string[];
29
+ filesWritten?: string[];
30
+ errors?: string[];
31
+ }
19
32
  export interface GenerateResult {
20
33
  success: boolean;
21
34
  message: string;
35
+ targets?: GenerateTargetResult[];
22
36
  tables?: string[];
23
37
  customQueries?: string[];
24
38
  customMutations?: string[];
@@ -42,3 +56,11 @@ export interface WriteOptions {
42
56
  showProgress?: boolean;
43
57
  }
44
58
  export declare function writeGeneratedFiles(files: GeneratedFile[], outputDir: string, subdirs: string[], options?: WriteOptions): Promise<WriteResult>;
59
+ /**
60
+ * Format generated files using oxfmt
61
+ * Runs oxfmt on the output directory after all files are written
62
+ */
63
+ export declare function formatOutput(outputDir: string): {
64
+ success: boolean;
65
+ error?: string;
66
+ };