@constructive-io/graphql-codegen 2.24.1 → 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 (38) hide show
  1. package/README.md +403 -279
  2. package/cli/codegen/orm/client-generator.js +475 -136
  3. package/cli/codegen/orm/custom-ops-generator.js +8 -3
  4. package/cli/codegen/orm/model-generator.js +18 -5
  5. package/cli/codegen/orm/select-types.d.ts +33 -0
  6. package/cli/commands/generate-orm.d.ts +14 -0
  7. package/cli/commands/generate-orm.js +160 -44
  8. package/cli/commands/generate.d.ts +22 -0
  9. package/cli/commands/generate.js +195 -55
  10. package/cli/commands/init.js +29 -9
  11. package/cli/index.js +133 -28
  12. package/cli/watch/orchestrator.d.ts +4 -0
  13. package/cli/watch/orchestrator.js +4 -0
  14. package/esm/cli/codegen/orm/client-generator.js +475 -136
  15. package/esm/cli/codegen/orm/custom-ops-generator.js +8 -3
  16. package/esm/cli/codegen/orm/model-generator.js +18 -5
  17. package/esm/cli/codegen/orm/select-types.d.ts +33 -0
  18. package/esm/cli/commands/generate-orm.d.ts +14 -0
  19. package/esm/cli/commands/generate-orm.js +161 -45
  20. package/esm/cli/commands/generate.d.ts +22 -0
  21. package/esm/cli/commands/generate.js +195 -56
  22. package/esm/cli/commands/init.js +29 -9
  23. package/esm/cli/index.js +134 -29
  24. package/esm/cli/watch/orchestrator.d.ts +4 -0
  25. package/esm/cli/watch/orchestrator.js +5 -1
  26. package/esm/types/config.d.ts +39 -2
  27. package/esm/types/config.js +88 -4
  28. package/esm/types/index.d.ts +2 -2
  29. package/esm/types/index.js +1 -1
  30. package/package.json +10 -7
  31. package/types/config.d.ts +39 -2
  32. package/types/config.js +91 -4
  33. package/types/index.d.ts +2 -2
  34. package/types/index.js +2 -1
  35. package/cli/codegen/orm/query-builder.d.ts +0 -161
  36. package/cli/codegen/orm/query-builder.js +0 -366
  37. package/esm/cli/codegen/orm/query-builder.d.ts +0 -161
  38. package/esm/cli/codegen/orm/query-builder.js +0 -353
package/cli/index.js CHANGED
@@ -42,6 +42,17 @@ const generate_1 = require("./commands/generate");
42
42
  const generate_orm_1 = require("./commands/generate-orm");
43
43
  const watch_1 = require("./watch");
44
44
  const config_1 = require("../types/config");
45
+ /**
46
+ * Format duration in a human-readable way
47
+ * - Under 1 second: show milliseconds (e.g., "123ms")
48
+ * - Over 1 second: show seconds with 2 decimal places (e.g., "1.23s")
49
+ */
50
+ function formatDuration(ms) {
51
+ if (ms < 1000) {
52
+ return `${Math.round(ms)}ms`;
53
+ }
54
+ return `${(ms / 1000).toFixed(2)}s`;
55
+ }
45
56
  const program = new commander_1.Command();
46
57
  /**
47
58
  * Load configuration for watch mode, merging CLI options with config file
@@ -61,23 +72,27 @@ async function loadWatchConfig(options) {
61
72
  }
62
73
  baseConfig = loadResult.config;
63
74
  }
64
- // Merge CLI options with config
65
- const mergedConfig = {
66
- endpoint: options.endpoint || baseConfig.endpoint || '',
67
- output: baseConfig.output,
68
- headers: baseConfig.headers,
69
- tables: baseConfig.tables,
70
- queries: baseConfig.queries,
71
- mutations: baseConfig.mutations,
72
- excludeFields: baseConfig.excludeFields,
73
- hooks: baseConfig.hooks,
74
- postgraphile: baseConfig.postgraphile,
75
- codegen: baseConfig.codegen,
76
- orm: baseConfig.orm,
77
- reactQuery: baseConfig.reactQuery,
75
+ if ((0, config_1.isMultiConfig)(baseConfig)) {
76
+ if (!options.target) {
77
+ console.error('x Watch mode requires --target when using multiple targets.');
78
+ return null;
79
+ }
80
+ if (!baseConfig.targets[options.target]) {
81
+ console.error(`x Target "${options.target}" not found in config file.`);
82
+ return null;
83
+ }
84
+ }
85
+ else if (options.target) {
86
+ console.error('x Config file does not define targets. Remove --target.');
87
+ return null;
88
+ }
89
+ const sourceOverrides = {};
90
+ if (options.endpoint) {
91
+ sourceOverrides.endpoint = options.endpoint;
92
+ sourceOverrides.schema = undefined;
93
+ }
94
+ const watchOverrides = {
78
95
  watch: {
79
- ...baseConfig.watch,
80
- // CLI options override config
81
96
  ...(options.pollInterval !== undefined && {
82
97
  pollInterval: options.pollInterval,
83
98
  }),
@@ -86,11 +101,26 @@ async function loadWatchConfig(options) {
86
101
  ...(options.clear !== undefined && { clearScreen: options.clear }),
87
102
  },
88
103
  };
89
- if (!mergedConfig.endpoint) {
90
- console.error('x No endpoint specified. Use --endpoint or create a config file with "graphql-codegen init".');
104
+ let mergedTarget;
105
+ if ((0, config_1.isMultiConfig)(baseConfig)) {
106
+ const defaults = baseConfig.defaults ?? {};
107
+ const targetConfig = baseConfig.targets[options.target];
108
+ mergedTarget = (0, config_1.mergeConfig)(defaults, targetConfig);
109
+ }
110
+ else {
111
+ mergedTarget = baseConfig;
112
+ }
113
+ mergedTarget = (0, config_1.mergeConfig)(mergedTarget, sourceOverrides);
114
+ mergedTarget = (0, config_1.mergeConfig)(mergedTarget, watchOverrides);
115
+ if (!mergedTarget.endpoint) {
116
+ console.error('x No endpoint specified. Watch mode only supports live endpoints.');
117
+ return null;
118
+ }
119
+ if (mergedTarget.schema) {
120
+ console.error('x Watch mode is only supported with an endpoint, not schema.');
91
121
  return null;
92
122
  }
93
- return (0, config_1.resolveConfig)(mergedConfig);
123
+ return (0, config_1.resolveConfig)(mergedTarget);
94
124
  }
95
125
  program
96
126
  .name('graphql-codegen')
@@ -105,17 +135,19 @@ program
105
135
  .option('-e, --endpoint <url>', 'GraphQL endpoint URL to pre-populate')
106
136
  .option('-o, --output <dir>', 'Output directory to pre-populate', './generated')
107
137
  .action(async (options) => {
138
+ const startTime = performance.now();
108
139
  const result = await (0, init_1.initCommand)({
109
140
  directory: options.directory,
110
141
  force: options.force,
111
142
  endpoint: options.endpoint,
112
143
  output: options.output,
113
144
  });
145
+ const duration = formatDuration(performance.now() - startTime);
114
146
  if (result.success) {
115
- console.log('[ok]', result.message);
147
+ console.log('[ok]', result.message, `(${duration})`);
116
148
  }
117
149
  else {
118
- console.error('x', result.message);
150
+ console.error('x', result.message, `(${duration})`);
119
151
  process.exit(1);
120
152
  }
121
153
  });
@@ -124,6 +156,7 @@ program
124
156
  .command('generate')
125
157
  .description('Generate SDK from GraphQL endpoint or schema file')
126
158
  .option('-c, --config <path>', 'Path to config file')
159
+ .option('-t, --target <name>', 'Target name in config file')
127
160
  .option('-e, --endpoint <url>', 'GraphQL endpoint URL (overrides config)')
128
161
  .option('-s, --schema <path>', 'Path to GraphQL schema file (.graphql)')
129
162
  .option('-o, --output <dir>', 'Output directory (overrides config)')
@@ -136,6 +169,7 @@ program
136
169
  .option('--touch <file>', 'File to touch on schema change')
137
170
  .option('--no-clear', 'Do not clear terminal on regeneration')
138
171
  .action(async (options) => {
172
+ const startTime = performance.now();
139
173
  // Validate source options
140
174
  if (options.endpoint && options.schema) {
141
175
  console.error('x Cannot use both --endpoint and --schema. Choose one source.');
@@ -156,6 +190,8 @@ program
156
190
  generatorType: 'generate',
157
191
  verbose: options.verbose,
158
192
  authorization: options.authorization,
193
+ configPath: options.config,
194
+ target: options.target,
159
195
  outputDir: options.output,
160
196
  });
161
197
  return;
@@ -163,6 +199,7 @@ program
163
199
  // Normal one-shot generation
164
200
  const result = await (0, generate_1.generateCommand)({
165
201
  config: options.config,
202
+ target: options.target,
166
203
  endpoint: options.endpoint,
167
204
  schema: options.schema,
168
205
  output: options.output,
@@ -170,8 +207,34 @@ program
170
207
  verbose: options.verbose,
171
208
  dryRun: options.dryRun,
172
209
  });
210
+ const duration = formatDuration(performance.now() - startTime);
211
+ const targetResults = result.targets ?? [];
212
+ const hasNamedTargets = targetResults.length > 0 &&
213
+ (targetResults.length > 1 || targetResults[0]?.name !== 'default');
214
+ if (hasNamedTargets) {
215
+ console.log(result.success ? '[ok]' : 'x', result.message);
216
+ targetResults.forEach((target) => {
217
+ const status = target.success ? '[ok]' : 'x';
218
+ console.log(`\n${status} ${target.message}`);
219
+ if (target.tables && target.tables.length > 0) {
220
+ console.log(' Tables:');
221
+ target.tables.forEach((table) => console.log(` - ${table}`));
222
+ }
223
+ if (target.filesWritten && target.filesWritten.length > 0) {
224
+ console.log(' Files written:');
225
+ target.filesWritten.forEach((file) => console.log(` - ${file}`));
226
+ }
227
+ if (!target.success && target.errors) {
228
+ target.errors.forEach((error) => console.error(` - ${error}`));
229
+ }
230
+ });
231
+ if (!result.success) {
232
+ process.exit(1);
233
+ }
234
+ return;
235
+ }
173
236
  if (result.success) {
174
- console.log('[ok]', result.message);
237
+ console.log('[ok]', result.message, `(${duration})`);
175
238
  if (result.tables && result.tables.length > 0) {
176
239
  console.log('\nTables:');
177
240
  result.tables.forEach((t) => console.log(` - ${t}`));
@@ -182,7 +245,7 @@ program
182
245
  }
183
246
  }
184
247
  else {
185
- console.error('x', result.message);
248
+ console.error('x', result.message, `(${duration})`);
186
249
  if (result.errors) {
187
250
  result.errors.forEach((e) => console.error(' -', e));
188
251
  }
@@ -194,9 +257,10 @@ program
194
257
  .command('generate-orm')
195
258
  .description('Generate Prisma-like ORM client from GraphQL endpoint or schema file')
196
259
  .option('-c, --config <path>', 'Path to config file')
260
+ .option('-t, --target <name>', 'Target name in config file')
197
261
  .option('-e, --endpoint <url>', 'GraphQL endpoint URL (overrides config)')
198
262
  .option('-s, --schema <path>', 'Path to GraphQL schema file (.graphql)')
199
- .option('-o, --output <dir>', 'Output directory (overrides config)', './generated/orm')
263
+ .option('-o, --output <dir>', 'Output directory (overrides config)')
200
264
  .option('-a, --authorization <header>', 'Authorization header value')
201
265
  .option('-v, --verbose', 'Verbose output', false)
202
266
  .option('--dry-run', 'Dry run - show what would be generated without writing files', false)
@@ -207,6 +271,7 @@ program
207
271
  .option('--touch <file>', 'File to touch on schema change')
208
272
  .option('--no-clear', 'Do not clear terminal on regeneration')
209
273
  .action(async (options) => {
274
+ const startTime = performance.now();
210
275
  // Validate source options
211
276
  if (options.endpoint && options.schema) {
212
277
  console.error('x Cannot use both --endpoint and --schema. Choose one source.');
@@ -227,6 +292,8 @@ program
227
292
  generatorType: 'generate-orm',
228
293
  verbose: options.verbose,
229
294
  authorization: options.authorization,
295
+ configPath: options.config,
296
+ target: options.target,
230
297
  outputDir: options.output,
231
298
  skipCustomOperations: options.skipCustomOperations,
232
299
  });
@@ -235,6 +302,7 @@ program
235
302
  // Normal one-shot generation
236
303
  const result = await (0, generate_orm_1.generateOrmCommand)({
237
304
  config: options.config,
305
+ target: options.target,
238
306
  endpoint: options.endpoint,
239
307
  schema: options.schema,
240
308
  output: options.output,
@@ -243,8 +311,42 @@ program
243
311
  dryRun: options.dryRun,
244
312
  skipCustomOperations: options.skipCustomOperations,
245
313
  });
314
+ const duration = formatDuration(performance.now() - startTime);
315
+ const targetResults = result.targets ?? [];
316
+ const hasNamedTargets = targetResults.length > 0 &&
317
+ (targetResults.length > 1 || targetResults[0]?.name !== 'default');
318
+ if (hasNamedTargets) {
319
+ console.log(result.success ? '[ok]' : 'x', result.message);
320
+ targetResults.forEach((target) => {
321
+ const status = target.success ? '[ok]' : 'x';
322
+ console.log(`\n${status} ${target.message}`);
323
+ if (target.tables && target.tables.length > 0) {
324
+ console.log(' Tables:');
325
+ target.tables.forEach((table) => console.log(` - ${table}`));
326
+ }
327
+ if (target.customQueries && target.customQueries.length > 0) {
328
+ console.log(' Custom Queries:');
329
+ target.customQueries.forEach((query) => console.log(` - ${query}`));
330
+ }
331
+ if (target.customMutations && target.customMutations.length > 0) {
332
+ console.log(' Custom Mutations:');
333
+ target.customMutations.forEach((mutation) => console.log(` - ${mutation}`));
334
+ }
335
+ if (target.filesWritten && target.filesWritten.length > 0) {
336
+ console.log(' Files written:');
337
+ target.filesWritten.forEach((file) => console.log(` - ${file}`));
338
+ }
339
+ if (!target.success && target.errors) {
340
+ target.errors.forEach((error) => console.error(` - ${error}`));
341
+ }
342
+ });
343
+ if (!result.success) {
344
+ process.exit(1);
345
+ }
346
+ return;
347
+ }
246
348
  if (result.success) {
247
- console.log('[ok]', result.message);
349
+ console.log('[ok]', result.message, `(${duration})`);
248
350
  if (result.tables && result.tables.length > 0) {
249
351
  console.log('\nTables:');
250
352
  result.tables.forEach((t) => console.log(` - ${t}`));
@@ -263,7 +365,7 @@ program
263
365
  }
264
366
  }
265
367
  else {
266
- console.error('x', result.message);
368
+ console.error('x', result.message, `(${duration})`);
267
369
  if (result.errors) {
268
370
  result.errors.forEach((e) => console.error(' -', e));
269
371
  }
@@ -279,6 +381,7 @@ program
279
381
  .option('-a, --authorization <header>', 'Authorization header value')
280
382
  .option('--json', 'Output as JSON', false)
281
383
  .action(async (options) => {
384
+ const startTime = performance.now();
282
385
  // Validate source options
283
386
  if (!options.endpoint && !options.schema) {
284
387
  console.error('x Either --endpoint or --schema must be provided.');
@@ -299,11 +402,12 @@ program
299
402
  console.log('Fetching schema from', source.describe(), '...');
300
403
  const { introspection } = await source.fetch();
301
404
  const tables = inferTablesFromIntrospection(introspection);
405
+ const duration = formatDuration(performance.now() - startTime);
302
406
  if (options.json) {
303
407
  console.log(JSON.stringify(tables, null, 2));
304
408
  }
305
409
  else {
306
- console.log(`\n[ok] Found ${tables.length} tables:\n`);
410
+ console.log(`\n[ok] Found ${tables.length} tables (${duration}):\n`);
307
411
  tables.forEach((table) => {
308
412
  const fieldCount = table.fields.length;
309
413
  const relationCount = table.relations.belongsTo.length +
@@ -315,7 +419,8 @@ program
315
419
  }
316
420
  }
317
421
  catch (err) {
318
- console.error('x Failed to introspect schema:', err instanceof Error ? err.message : err);
422
+ const duration = formatDuration(performance.now() - startTime);
423
+ console.error('x Failed to introspect schema:', err instanceof Error ? err.message : err, `(${duration})`);
319
424
  process.exit(1);
320
425
  }
321
426
  });
@@ -10,6 +10,10 @@ export interface WatchOrchestratorOptions {
10
10
  generatorType: GeneratorType;
11
11
  verbose: boolean;
12
12
  authorization?: string;
13
+ /** Config file path for regeneration */
14
+ configPath?: string;
15
+ /** Target name for multi-target configs */
16
+ target?: string;
13
17
  /** Override output directory (for ORM) */
14
18
  outputDir?: string;
15
19
  /** Skip custom operations flag */
@@ -138,6 +138,8 @@ class WatchOrchestrator {
138
138
  let result;
139
139
  if (this.options.generatorType === 'generate') {
140
140
  result = await (0, generate_1.generateCommand)({
141
+ config: this.options.configPath,
142
+ target: this.options.target,
141
143
  endpoint: this.options.config.endpoint,
142
144
  output: this.options.outputDir ?? this.options.config.output,
143
145
  authorization: this.options.authorization,
@@ -147,6 +149,8 @@ class WatchOrchestrator {
147
149
  }
148
150
  else {
149
151
  result = await (0, generate_orm_1.generateOrmCommand)({
152
+ config: this.options.configPath,
153
+ target: this.options.target,
150
154
  endpoint: this.options.config.endpoint,
151
155
  output: this.options.outputDir ?? this.options.config.orm?.output,
152
156
  authorization: this.options.authorization,