@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.
- package/README.md +403 -279
- package/cli/codegen/orm/client-generator.js +475 -136
- package/cli/codegen/orm/custom-ops-generator.js +8 -3
- package/cli/codegen/orm/model-generator.js +18 -5
- package/cli/codegen/orm/select-types.d.ts +33 -0
- package/cli/commands/generate-orm.d.ts +14 -0
- package/cli/commands/generate-orm.js +160 -44
- package/cli/commands/generate.d.ts +22 -0
- package/cli/commands/generate.js +195 -55
- package/cli/commands/init.js +29 -9
- package/cli/index.js +133 -28
- package/cli/watch/orchestrator.d.ts +4 -0
- package/cli/watch/orchestrator.js +4 -0
- package/esm/cli/codegen/orm/client-generator.js +475 -136
- package/esm/cli/codegen/orm/custom-ops-generator.js +8 -3
- package/esm/cli/codegen/orm/model-generator.js +18 -5
- package/esm/cli/codegen/orm/select-types.d.ts +33 -0
- package/esm/cli/commands/generate-orm.d.ts +14 -0
- package/esm/cli/commands/generate-orm.js +161 -45
- package/esm/cli/commands/generate.d.ts +22 -0
- package/esm/cli/commands/generate.js +195 -56
- package/esm/cli/commands/init.js +29 -9
- package/esm/cli/index.js +134 -29
- package/esm/cli/watch/orchestrator.d.ts +4 -0
- package/esm/cli/watch/orchestrator.js +5 -1
- package/esm/types/config.d.ts +39 -2
- package/esm/types/config.js +88 -4
- package/esm/types/index.d.ts +2 -2
- package/esm/types/index.js +1 -1
- package/package.json +10 -7
- package/types/config.d.ts +39 -2
- package/types/config.js +91 -4
- package/types/index.d.ts +2 -2
- package/types/index.js +2 -1
- package/cli/codegen/orm/query-builder.d.ts +0 -161
- package/cli/codegen/orm/query-builder.js +0 -366
- package/esm/cli/codegen/orm/query-builder.d.ts +0 -161
- 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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
90
|
-
|
|
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)(
|
|
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)'
|
|
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
|
-
|
|
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,
|