@constructive-io/graphql-codegen 2.27.2 → 2.28.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/cli/index.d.ts +4 -1
- package/cli/index.js +343 -146
- package/esm/cli/index.d.ts +4 -1
- package/esm/cli/index.js +341 -146
- package/package.json +10 -8
package/cli/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
/**
|
|
2
3
|
* CLI entry point for graphql-codegen
|
|
3
4
|
*/
|
|
4
|
-
|
|
5
|
+
import { CLIOptions, Inquirerer, ParsedArgs } from 'inquirerer';
|
|
6
|
+
export declare const commands: (argv: Partial<ParsedArgs>, prompter: Inquirerer, _options: CLIOptions) => Promise<Partial<ParsedArgs>>;
|
|
7
|
+
export declare const options: Partial<CLIOptions>;
|
package/cli/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
"use strict";
|
|
2
3
|
/**
|
|
3
4
|
* CLI entry point for graphql-codegen
|
|
@@ -36,12 +37,102 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
36
37
|
};
|
|
37
38
|
})();
|
|
38
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
|
|
40
|
+
exports.options = exports.commands = void 0;
|
|
41
|
+
const inquirerer_1 = require("inquirerer");
|
|
40
42
|
const init_1 = require("./commands/init");
|
|
41
43
|
const generate_1 = require("./commands/generate");
|
|
42
44
|
const generate_orm_1 = require("./commands/generate-orm");
|
|
43
45
|
const watch_1 = require("./watch");
|
|
44
46
|
const config_1 = require("../types/config");
|
|
47
|
+
const usageText = `
|
|
48
|
+
graphql-codegen - CLI for generating GraphQL SDK from PostGraphile endpoints or schema files
|
|
49
|
+
|
|
50
|
+
Usage:
|
|
51
|
+
graphql-codegen <command> [options]
|
|
52
|
+
|
|
53
|
+
Commands:
|
|
54
|
+
init Initialize a new graphql-codegen configuration file
|
|
55
|
+
generate Generate SDK from GraphQL endpoint or schema file
|
|
56
|
+
generate-orm Generate Prisma-like ORM client from GraphQL endpoint or schema file
|
|
57
|
+
introspect Introspect a GraphQL endpoint or schema file and print table info
|
|
58
|
+
|
|
59
|
+
Options:
|
|
60
|
+
--help, -h Show this help message
|
|
61
|
+
--version, -v Show version number
|
|
62
|
+
|
|
63
|
+
Run 'graphql-codegen <command> --help' for more information on a command.
|
|
64
|
+
`;
|
|
65
|
+
const initUsageText = `
|
|
66
|
+
graphql-codegen init - Initialize a new graphql-codegen configuration file
|
|
67
|
+
|
|
68
|
+
Usage:
|
|
69
|
+
graphql-codegen init [options]
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
--directory, -d <dir> Target directory for the config file (default: .)
|
|
73
|
+
--force, -f Force overwrite existing config
|
|
74
|
+
--endpoint, -e <url> GraphQL endpoint URL to pre-populate
|
|
75
|
+
--output, -o <dir> Output directory to pre-populate (default: ./generated)
|
|
76
|
+
--help, -h Show this help message
|
|
77
|
+
`;
|
|
78
|
+
const generateUsageText = `
|
|
79
|
+
graphql-codegen generate - Generate SDK from GraphQL endpoint or schema file
|
|
80
|
+
|
|
81
|
+
Usage:
|
|
82
|
+
graphql-codegen generate [options]
|
|
83
|
+
|
|
84
|
+
Options:
|
|
85
|
+
--config, -c <path> Path to config file
|
|
86
|
+
--target, -t <name> Target name in config file
|
|
87
|
+
--endpoint, -e <url> GraphQL endpoint URL (overrides config)
|
|
88
|
+
--schema, -s <path> Path to GraphQL schema file (.graphql)
|
|
89
|
+
--output, -o <dir> Output directory (overrides config)
|
|
90
|
+
--authorization, -a <header> Authorization header value
|
|
91
|
+
--verbose, -v Verbose output
|
|
92
|
+
--dry-run Dry run - show what would be generated without writing files
|
|
93
|
+
--watch, -w Watch mode - poll endpoint for schema changes (in-memory)
|
|
94
|
+
--poll-interval <ms> Polling interval in milliseconds (default: 3000)
|
|
95
|
+
--debounce <ms> Debounce delay before regenerating (default: 800)
|
|
96
|
+
--touch <file> File to touch on schema change
|
|
97
|
+
--no-clear Do not clear terminal on regeneration
|
|
98
|
+
--help, -h Show this help message
|
|
99
|
+
`;
|
|
100
|
+
const generateOrmUsageText = `
|
|
101
|
+
graphql-codegen generate-orm - Generate Prisma-like ORM client from GraphQL endpoint or schema file
|
|
102
|
+
|
|
103
|
+
Usage:
|
|
104
|
+
graphql-codegen generate-orm [options]
|
|
105
|
+
|
|
106
|
+
Options:
|
|
107
|
+
--config, -c <path> Path to config file
|
|
108
|
+
--target, -t <name> Target name in config file
|
|
109
|
+
--endpoint, -e <url> GraphQL endpoint URL (overrides config)
|
|
110
|
+
--schema, -s <path> Path to GraphQL schema file (.graphql)
|
|
111
|
+
--output, -o <dir> Output directory (overrides config)
|
|
112
|
+
--authorization, -a <header> Authorization header value
|
|
113
|
+
--verbose, -v Verbose output
|
|
114
|
+
--dry-run Dry run - show what would be generated without writing files
|
|
115
|
+
--skip-custom-operations Skip custom operations (only generate table CRUD)
|
|
116
|
+
--watch, -w Watch mode - poll endpoint for schema changes (in-memory)
|
|
117
|
+
--poll-interval <ms> Polling interval in milliseconds (default: 3000)
|
|
118
|
+
--debounce <ms> Debounce delay before regenerating (default: 800)
|
|
119
|
+
--touch <file> File to touch on schema change
|
|
120
|
+
--no-clear Do not clear terminal on regeneration
|
|
121
|
+
--help, -h Show this help message
|
|
122
|
+
`;
|
|
123
|
+
const introspectUsageText = `
|
|
124
|
+
graphql-codegen introspect - Introspect a GraphQL endpoint or schema file and print table info
|
|
125
|
+
|
|
126
|
+
Usage:
|
|
127
|
+
graphql-codegen introspect [options]
|
|
128
|
+
|
|
129
|
+
Options:
|
|
130
|
+
--endpoint, -e <url> GraphQL endpoint URL
|
|
131
|
+
--schema, -s <path> Path to GraphQL schema file (.graphql)
|
|
132
|
+
--authorization, -a <header> Authorization header value
|
|
133
|
+
--json Output as JSON
|
|
134
|
+
--help, -h Show this help message
|
|
135
|
+
`;
|
|
45
136
|
/**
|
|
46
137
|
* Format duration in a human-readable way
|
|
47
138
|
* - Under 1 second: show milliseconds (e.g., "123ms")
|
|
@@ -53,12 +144,10 @@ function formatDuration(ms) {
|
|
|
53
144
|
}
|
|
54
145
|
return `${(ms / 1000).toFixed(2)}s`;
|
|
55
146
|
}
|
|
56
|
-
const program = new commander_1.Command();
|
|
57
147
|
/**
|
|
58
148
|
* Load configuration for watch mode, merging CLI options with config file
|
|
59
149
|
*/
|
|
60
150
|
async function loadWatchConfig(options) {
|
|
61
|
-
// Find config file
|
|
62
151
|
let configPath = options.config;
|
|
63
152
|
if (!configPath) {
|
|
64
153
|
configPath = (0, init_1.findConfigFile)() ?? undefined;
|
|
@@ -122,25 +211,20 @@ async function loadWatchConfig(options) {
|
|
|
122
211
|
}
|
|
123
212
|
return (0, config_1.resolveConfig)(mergedTarget);
|
|
124
213
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
.option('-d, --directory <dir>', 'Target directory for the config file', '.')
|
|
134
|
-
.option('-f, --force', 'Force overwrite existing config', false)
|
|
135
|
-
.option('-e, --endpoint <url>', 'GraphQL endpoint URL to pre-populate')
|
|
136
|
-
.option('-o, --output <dir>', 'Output directory to pre-populate', './generated')
|
|
137
|
-
.action(async (options) => {
|
|
214
|
+
/**
|
|
215
|
+
* Init command handler
|
|
216
|
+
*/
|
|
217
|
+
async function handleInit(argv) {
|
|
218
|
+
if (argv.help || argv.h) {
|
|
219
|
+
console.log(initUsageText);
|
|
220
|
+
process.exit(0);
|
|
221
|
+
}
|
|
138
222
|
const startTime = performance.now();
|
|
139
223
|
const result = await (0, init_1.initCommand)({
|
|
140
|
-
directory:
|
|
141
|
-
force:
|
|
142
|
-
endpoint:
|
|
143
|
-
output:
|
|
224
|
+
directory: argv.directory || argv.d || '.',
|
|
225
|
+
force: !!(argv.force || argv.f),
|
|
226
|
+
endpoint: argv.endpoint || argv.e,
|
|
227
|
+
output: argv.output || argv.o || './generated',
|
|
144
228
|
});
|
|
145
229
|
const duration = formatDuration(performance.now() - startTime);
|
|
146
230
|
if (result.success) {
|
|
@@ -150,62 +234,75 @@ program
|
|
|
150
234
|
console.error('x', result.message, `(${duration})`);
|
|
151
235
|
process.exit(1);
|
|
152
236
|
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
.option('-o, --output <dir>', 'Output directory (overrides config)')
|
|
163
|
-
.option('-a, --authorization <header>', 'Authorization header value')
|
|
164
|
-
.option('-v, --verbose', 'Verbose output', false)
|
|
165
|
-
.option('--dry-run', 'Dry run - show what would be generated without writing files', false)
|
|
166
|
-
.option('-w, --watch', 'Watch mode - poll endpoint for schema changes (in-memory)', false)
|
|
167
|
-
.option('--poll-interval <ms>', 'Polling interval in milliseconds (default: 3000)', parseInt)
|
|
168
|
-
.option('--debounce <ms>', 'Debounce delay before regenerating (default: 800)', parseInt)
|
|
169
|
-
.option('--touch <file>', 'File to touch on schema change')
|
|
170
|
-
.option('--no-clear', 'Do not clear terminal on regeneration')
|
|
171
|
-
.action(async (options) => {
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Generate command handler
|
|
240
|
+
*/
|
|
241
|
+
async function handleGenerate(argv) {
|
|
242
|
+
if (argv.help || argv.h) {
|
|
243
|
+
console.log(generateUsageText);
|
|
244
|
+
process.exit(0);
|
|
245
|
+
}
|
|
172
246
|
const startTime = performance.now();
|
|
173
|
-
|
|
174
|
-
|
|
247
|
+
const config = argv.config || argv.c;
|
|
248
|
+
const target = argv.target || argv.t;
|
|
249
|
+
const endpoint = argv.endpoint || argv.e;
|
|
250
|
+
const schema = argv.schema || argv.s;
|
|
251
|
+
const output = argv.output || argv.o;
|
|
252
|
+
const authorization = argv.authorization || argv.a;
|
|
253
|
+
const verbose = !!(argv.verbose || argv.v);
|
|
254
|
+
const dryRun = !!(argv['dry-run'] || argv.dryRun);
|
|
255
|
+
const watch = !!(argv.watch || argv.w);
|
|
256
|
+
const pollInterval = argv['poll-interval'] !== undefined
|
|
257
|
+
? parseInt(argv['poll-interval'], 10)
|
|
258
|
+
: undefined;
|
|
259
|
+
const debounce = argv.debounce !== undefined
|
|
260
|
+
? parseInt(argv.debounce, 10)
|
|
261
|
+
: undefined;
|
|
262
|
+
const touch = argv.touch;
|
|
263
|
+
const clear = argv.clear !== false;
|
|
264
|
+
if (endpoint && schema) {
|
|
175
265
|
console.error('x Cannot use both --endpoint and --schema. Choose one source.');
|
|
176
266
|
process.exit(1);
|
|
177
267
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (options.schema) {
|
|
268
|
+
if (watch) {
|
|
269
|
+
if (schema) {
|
|
181
270
|
console.error('x Watch mode is only supported with --endpoint, not --schema.');
|
|
182
271
|
process.exit(1);
|
|
183
272
|
}
|
|
184
|
-
const
|
|
185
|
-
|
|
273
|
+
const watchConfig = await loadWatchConfig({
|
|
274
|
+
config,
|
|
275
|
+
target,
|
|
276
|
+
endpoint,
|
|
277
|
+
output,
|
|
278
|
+
pollInterval,
|
|
279
|
+
debounce,
|
|
280
|
+
touch,
|
|
281
|
+
clear,
|
|
282
|
+
});
|
|
283
|
+
if (!watchConfig) {
|
|
186
284
|
process.exit(1);
|
|
187
285
|
}
|
|
188
286
|
await (0, watch_1.startWatch)({
|
|
189
|
-
config,
|
|
287
|
+
config: watchConfig,
|
|
190
288
|
generatorType: 'generate',
|
|
191
|
-
verbose
|
|
192
|
-
authorization
|
|
193
|
-
configPath:
|
|
194
|
-
target
|
|
195
|
-
outputDir:
|
|
289
|
+
verbose,
|
|
290
|
+
authorization,
|
|
291
|
+
configPath: config,
|
|
292
|
+
target,
|
|
293
|
+
outputDir: output,
|
|
196
294
|
});
|
|
197
295
|
return;
|
|
198
296
|
}
|
|
199
|
-
// Normal one-shot generation
|
|
200
297
|
const result = await (0, generate_1.generateCommand)({
|
|
201
|
-
config
|
|
202
|
-
target
|
|
203
|
-
endpoint
|
|
204
|
-
schema
|
|
205
|
-
output
|
|
206
|
-
authorization
|
|
207
|
-
verbose
|
|
208
|
-
dryRun
|
|
298
|
+
config,
|
|
299
|
+
target,
|
|
300
|
+
endpoint,
|
|
301
|
+
schema,
|
|
302
|
+
output,
|
|
303
|
+
authorization,
|
|
304
|
+
verbose,
|
|
305
|
+
dryRun,
|
|
209
306
|
});
|
|
210
307
|
const duration = formatDuration(performance.now() - startTime);
|
|
211
308
|
const targetResults = result.targets ?? [];
|
|
@@ -213,19 +310,19 @@ program
|
|
|
213
310
|
(targetResults.length > 1 || targetResults[0]?.name !== 'default');
|
|
214
311
|
if (hasNamedTargets) {
|
|
215
312
|
console.log(result.success ? '[ok]' : 'x', result.message);
|
|
216
|
-
targetResults.forEach((
|
|
217
|
-
const status =
|
|
218
|
-
console.log(`\n${status} ${
|
|
219
|
-
if (
|
|
313
|
+
targetResults.forEach((t) => {
|
|
314
|
+
const status = t.success ? '[ok]' : 'x';
|
|
315
|
+
console.log(`\n${status} ${t.message}`);
|
|
316
|
+
if (t.tables && t.tables.length > 0) {
|
|
220
317
|
console.log(' Tables:');
|
|
221
|
-
|
|
318
|
+
t.tables.forEach((table) => console.log(` - ${table}`));
|
|
222
319
|
}
|
|
223
|
-
if (
|
|
320
|
+
if (t.filesWritten && t.filesWritten.length > 0) {
|
|
224
321
|
console.log(' Files written:');
|
|
225
|
-
|
|
322
|
+
t.filesWritten.forEach((file) => console.log(` - ${file}`));
|
|
226
323
|
}
|
|
227
|
-
if (!
|
|
228
|
-
|
|
324
|
+
if (!t.success && t.errors) {
|
|
325
|
+
t.errors.forEach((error) => console.error(` - ${error}`));
|
|
229
326
|
}
|
|
230
327
|
});
|
|
231
328
|
if (!result.success) {
|
|
@@ -251,65 +348,78 @@ program
|
|
|
251
348
|
}
|
|
252
349
|
process.exit(1);
|
|
253
350
|
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
.option('-o, --output <dir>', 'Output directory (overrides config)')
|
|
264
|
-
.option('-a, --authorization <header>', 'Authorization header value')
|
|
265
|
-
.option('-v, --verbose', 'Verbose output', false)
|
|
266
|
-
.option('--dry-run', 'Dry run - show what would be generated without writing files', false)
|
|
267
|
-
.option('--skip-custom-operations', 'Skip custom operations (only generate table CRUD)', false)
|
|
268
|
-
.option('-w, --watch', 'Watch mode - poll endpoint for schema changes (in-memory)', false)
|
|
269
|
-
.option('--poll-interval <ms>', 'Polling interval in milliseconds (default: 3000)', parseInt)
|
|
270
|
-
.option('--debounce <ms>', 'Debounce delay before regenerating (default: 800)', parseInt)
|
|
271
|
-
.option('--touch <file>', 'File to touch on schema change')
|
|
272
|
-
.option('--no-clear', 'Do not clear terminal on regeneration')
|
|
273
|
-
.action(async (options) => {
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Generate ORM command handler
|
|
354
|
+
*/
|
|
355
|
+
async function handleGenerateOrm(argv) {
|
|
356
|
+
if (argv.help || argv.h) {
|
|
357
|
+
console.log(generateOrmUsageText);
|
|
358
|
+
process.exit(0);
|
|
359
|
+
}
|
|
274
360
|
const startTime = performance.now();
|
|
275
|
-
|
|
276
|
-
|
|
361
|
+
const config = argv.config || argv.c;
|
|
362
|
+
const target = argv.target || argv.t;
|
|
363
|
+
const endpoint = argv.endpoint || argv.e;
|
|
364
|
+
const schema = argv.schema || argv.s;
|
|
365
|
+
const output = argv.output || argv.o;
|
|
366
|
+
const authorization = argv.authorization || argv.a;
|
|
367
|
+
const verbose = !!(argv.verbose || argv.v);
|
|
368
|
+
const dryRun = !!(argv['dry-run'] || argv.dryRun);
|
|
369
|
+
const skipCustomOperations = !!(argv['skip-custom-operations'] || argv.skipCustomOperations);
|
|
370
|
+
const watch = !!(argv.watch || argv.w);
|
|
371
|
+
const pollInterval = argv['poll-interval'] !== undefined
|
|
372
|
+
? parseInt(argv['poll-interval'], 10)
|
|
373
|
+
: undefined;
|
|
374
|
+
const debounce = argv.debounce !== undefined
|
|
375
|
+
? parseInt(argv.debounce, 10)
|
|
376
|
+
: undefined;
|
|
377
|
+
const touch = argv.touch;
|
|
378
|
+
const clear = argv.clear !== false;
|
|
379
|
+
if (endpoint && schema) {
|
|
277
380
|
console.error('x Cannot use both --endpoint and --schema. Choose one source.');
|
|
278
381
|
process.exit(1);
|
|
279
382
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (options.schema) {
|
|
383
|
+
if (watch) {
|
|
384
|
+
if (schema) {
|
|
283
385
|
console.error('x Watch mode is only supported with --endpoint, not --schema.');
|
|
284
386
|
process.exit(1);
|
|
285
387
|
}
|
|
286
|
-
const
|
|
287
|
-
|
|
388
|
+
const watchConfig = await loadWatchConfig({
|
|
389
|
+
config,
|
|
390
|
+
target,
|
|
391
|
+
endpoint,
|
|
392
|
+
output,
|
|
393
|
+
pollInterval,
|
|
394
|
+
debounce,
|
|
395
|
+
touch,
|
|
396
|
+
clear,
|
|
397
|
+
});
|
|
398
|
+
if (!watchConfig) {
|
|
288
399
|
process.exit(1);
|
|
289
400
|
}
|
|
290
401
|
await (0, watch_1.startWatch)({
|
|
291
|
-
config,
|
|
402
|
+
config: watchConfig,
|
|
292
403
|
generatorType: 'generate-orm',
|
|
293
|
-
verbose
|
|
294
|
-
authorization
|
|
295
|
-
configPath:
|
|
296
|
-
target
|
|
297
|
-
outputDir:
|
|
298
|
-
skipCustomOperations
|
|
404
|
+
verbose,
|
|
405
|
+
authorization,
|
|
406
|
+
configPath: config,
|
|
407
|
+
target,
|
|
408
|
+
outputDir: output,
|
|
409
|
+
skipCustomOperations,
|
|
299
410
|
});
|
|
300
411
|
return;
|
|
301
412
|
}
|
|
302
|
-
// Normal one-shot generation
|
|
303
413
|
const result = await (0, generate_orm_1.generateOrmCommand)({
|
|
304
|
-
config
|
|
305
|
-
target
|
|
306
|
-
endpoint
|
|
307
|
-
schema
|
|
308
|
-
output
|
|
309
|
-
authorization
|
|
310
|
-
verbose
|
|
311
|
-
dryRun
|
|
312
|
-
skipCustomOperations
|
|
414
|
+
config,
|
|
415
|
+
target,
|
|
416
|
+
endpoint,
|
|
417
|
+
schema,
|
|
418
|
+
output,
|
|
419
|
+
authorization,
|
|
420
|
+
verbose,
|
|
421
|
+
dryRun,
|
|
422
|
+
skipCustomOperations,
|
|
313
423
|
});
|
|
314
424
|
const duration = formatDuration(performance.now() - startTime);
|
|
315
425
|
const targetResults = result.targets ?? [];
|
|
@@ -317,27 +427,27 @@ program
|
|
|
317
427
|
(targetResults.length > 1 || targetResults[0]?.name !== 'default');
|
|
318
428
|
if (hasNamedTargets) {
|
|
319
429
|
console.log(result.success ? '[ok]' : 'x', result.message);
|
|
320
|
-
targetResults.forEach((
|
|
321
|
-
const status =
|
|
322
|
-
console.log(`\n${status} ${
|
|
323
|
-
if (
|
|
430
|
+
targetResults.forEach((t) => {
|
|
431
|
+
const status = t.success ? '[ok]' : 'x';
|
|
432
|
+
console.log(`\n${status} ${t.message}`);
|
|
433
|
+
if (t.tables && t.tables.length > 0) {
|
|
324
434
|
console.log(' Tables:');
|
|
325
|
-
|
|
435
|
+
t.tables.forEach((table) => console.log(` - ${table}`));
|
|
326
436
|
}
|
|
327
|
-
if (
|
|
437
|
+
if (t.customQueries && t.customQueries.length > 0) {
|
|
328
438
|
console.log(' Custom Queries:');
|
|
329
|
-
|
|
439
|
+
t.customQueries.forEach((query) => console.log(` - ${query}`));
|
|
330
440
|
}
|
|
331
|
-
if (
|
|
441
|
+
if (t.customMutations && t.customMutations.length > 0) {
|
|
332
442
|
console.log(' Custom Mutations:');
|
|
333
|
-
|
|
443
|
+
t.customMutations.forEach((mutation) => console.log(` - ${mutation}`));
|
|
334
444
|
}
|
|
335
|
-
if (
|
|
445
|
+
if (t.filesWritten && t.filesWritten.length > 0) {
|
|
336
446
|
console.log(' Files written:');
|
|
337
|
-
|
|
447
|
+
t.filesWritten.forEach((file) => console.log(` - ${file}`));
|
|
338
448
|
}
|
|
339
|
-
if (!
|
|
340
|
-
|
|
449
|
+
if (!t.success && t.errors) {
|
|
450
|
+
t.errors.forEach((error) => console.error(` - ${error}`));
|
|
341
451
|
}
|
|
342
452
|
});
|
|
343
453
|
if (!result.success) {
|
|
@@ -371,23 +481,25 @@ program
|
|
|
371
481
|
}
|
|
372
482
|
process.exit(1);
|
|
373
483
|
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
.
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
.action(async (options) => {
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Introspect command handler
|
|
487
|
+
*/
|
|
488
|
+
async function handleIntrospect(argv) {
|
|
489
|
+
if (argv.help || argv.h) {
|
|
490
|
+
console.log(introspectUsageText);
|
|
491
|
+
process.exit(0);
|
|
492
|
+
}
|
|
384
493
|
const startTime = performance.now();
|
|
385
|
-
|
|
386
|
-
|
|
494
|
+
const endpoint = argv.endpoint || argv.e;
|
|
495
|
+
const schema = argv.schema || argv.s;
|
|
496
|
+
const authorization = argv.authorization || argv.a;
|
|
497
|
+
const json = !!argv.json;
|
|
498
|
+
if (!endpoint && !schema) {
|
|
387
499
|
console.error('x Either --endpoint or --schema must be provided.');
|
|
388
500
|
process.exit(1);
|
|
389
501
|
}
|
|
390
|
-
if (
|
|
502
|
+
if (endpoint && schema) {
|
|
391
503
|
console.error('x Cannot use both --endpoint and --schema. Choose one source.');
|
|
392
504
|
process.exit(1);
|
|
393
505
|
}
|
|
@@ -395,15 +507,15 @@ program
|
|
|
395
507
|
const { inferTablesFromIntrospection } = await Promise.resolve().then(() => __importStar(require('./introspect/infer-tables')));
|
|
396
508
|
try {
|
|
397
509
|
const source = createSchemaSource({
|
|
398
|
-
endpoint
|
|
399
|
-
schema
|
|
400
|
-
authorization
|
|
510
|
+
endpoint,
|
|
511
|
+
schema,
|
|
512
|
+
authorization,
|
|
401
513
|
});
|
|
402
514
|
console.log('Fetching schema from', source.describe(), '...');
|
|
403
515
|
const { introspection } = await source.fetch();
|
|
404
516
|
const tables = inferTablesFromIntrospection(introspection);
|
|
405
517
|
const duration = formatDuration(performance.now() - startTime);
|
|
406
|
-
if (
|
|
518
|
+
if (json) {
|
|
407
519
|
console.log(JSON.stringify(tables, null, 2));
|
|
408
520
|
}
|
|
409
521
|
else {
|
|
@@ -423,5 +535,90 @@ program
|
|
|
423
535
|
console.error('x Failed to introspect schema:', err instanceof Error ? err.message : err, `(${duration})`);
|
|
424
536
|
process.exit(1);
|
|
425
537
|
}
|
|
426
|
-
}
|
|
427
|
-
|
|
538
|
+
}
|
|
539
|
+
const createCommandMap = () => {
|
|
540
|
+
return {
|
|
541
|
+
init: handleInit,
|
|
542
|
+
generate: handleGenerate,
|
|
543
|
+
'generate-orm': handleGenerateOrm,
|
|
544
|
+
introspect: handleIntrospect,
|
|
545
|
+
};
|
|
546
|
+
};
|
|
547
|
+
const commands = async (argv, prompter, _options) => {
|
|
548
|
+
if (argv.version || argv.v) {
|
|
549
|
+
const pkg = (0, inquirerer_1.getPackageJson)(__dirname);
|
|
550
|
+
console.log(pkg.version);
|
|
551
|
+
process.exit(0);
|
|
552
|
+
}
|
|
553
|
+
const { first: command, newArgv } = (0, inquirerer_1.extractFirst)(argv);
|
|
554
|
+
if ((argv.help || argv.h) && !command) {
|
|
555
|
+
console.log(usageText);
|
|
556
|
+
process.exit(0);
|
|
557
|
+
}
|
|
558
|
+
if (command === 'help') {
|
|
559
|
+
console.log(usageText);
|
|
560
|
+
process.exit(0);
|
|
561
|
+
}
|
|
562
|
+
const commandMap = createCommandMap();
|
|
563
|
+
if (!command) {
|
|
564
|
+
const answer = await prompter.prompt(argv, [
|
|
565
|
+
{
|
|
566
|
+
type: 'autocomplete',
|
|
567
|
+
name: 'command',
|
|
568
|
+
message: 'What do you want to do?',
|
|
569
|
+
options: Object.keys(commandMap),
|
|
570
|
+
},
|
|
571
|
+
]);
|
|
572
|
+
const selectedCommand = answer.command;
|
|
573
|
+
const commandFn = commandMap[selectedCommand];
|
|
574
|
+
if (commandFn) {
|
|
575
|
+
await commandFn(newArgv);
|
|
576
|
+
}
|
|
577
|
+
prompter.close();
|
|
578
|
+
return argv;
|
|
579
|
+
}
|
|
580
|
+
const commandFn = commandMap[command];
|
|
581
|
+
if (!commandFn) {
|
|
582
|
+
console.log(usageText);
|
|
583
|
+
await (0, inquirerer_1.cliExitWithError)(`Unknown command: ${command}`);
|
|
584
|
+
}
|
|
585
|
+
await commandFn(newArgv);
|
|
586
|
+
prompter.close();
|
|
587
|
+
return argv;
|
|
588
|
+
};
|
|
589
|
+
exports.commands = commands;
|
|
590
|
+
exports.options = {
|
|
591
|
+
minimistOpts: {
|
|
592
|
+
alias: {
|
|
593
|
+
v: 'version',
|
|
594
|
+
h: 'help',
|
|
595
|
+
c: 'config',
|
|
596
|
+
t: 'target',
|
|
597
|
+
e: 'endpoint',
|
|
598
|
+
s: 'schema',
|
|
599
|
+
o: 'output',
|
|
600
|
+
a: 'authorization',
|
|
601
|
+
d: 'directory',
|
|
602
|
+
f: 'force',
|
|
603
|
+
w: 'watch',
|
|
604
|
+
},
|
|
605
|
+
boolean: ['help', 'version', 'force', 'verbose', 'dry-run', 'watch', 'json', 'skip-custom-operations', 'clear'],
|
|
606
|
+
string: ['config', 'target', 'endpoint', 'schema', 'output', 'authorization', 'directory', 'touch', 'poll-interval', 'debounce'],
|
|
607
|
+
default: {
|
|
608
|
+
clear: true,
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
};
|
|
612
|
+
if (require.main === module) {
|
|
613
|
+
if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
614
|
+
const pkg = (0, inquirerer_1.getPackageJson)(__dirname);
|
|
615
|
+
console.log(pkg.version);
|
|
616
|
+
process.exit(0);
|
|
617
|
+
}
|
|
618
|
+
const app = new inquirerer_1.CLI(exports.commands, exports.options);
|
|
619
|
+
app.run().then(() => {
|
|
620
|
+
}).catch((error) => {
|
|
621
|
+
console.error('Unexpected error:', error);
|
|
622
|
+
process.exit(1);
|
|
623
|
+
});
|
|
624
|
+
}
|
package/esm/cli/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
/**
|
|
2
3
|
* CLI entry point for graphql-codegen
|
|
3
4
|
*/
|
|
4
|
-
|
|
5
|
+
import { CLIOptions, Inquirerer, ParsedArgs } from 'inquirerer';
|
|
6
|
+
export declare const commands: (argv: Partial<ParsedArgs>, prompter: Inquirerer, _options: CLIOptions) => Promise<Partial<ParsedArgs>>;
|
|
7
|
+
export declare const options: Partial<CLIOptions>;
|
package/esm/cli/index.js
CHANGED
|
@@ -1,12 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
/**
|
|
2
3
|
* CLI entry point for graphql-codegen
|
|
3
4
|
*/
|
|
4
|
-
import {
|
|
5
|
+
import { CLI, cliExitWithError, extractFirst, getPackageJson } from 'inquirerer';
|
|
5
6
|
import { initCommand, findConfigFile, loadConfigFile } from './commands/init';
|
|
6
7
|
import { generateCommand } from './commands/generate';
|
|
7
8
|
import { generateOrmCommand } from './commands/generate-orm';
|
|
8
9
|
import { startWatch } from './watch';
|
|
9
10
|
import { isMultiConfig, mergeConfig, resolveConfig, } from '../types/config';
|
|
11
|
+
const usageText = `
|
|
12
|
+
graphql-codegen - CLI for generating GraphQL SDK from PostGraphile endpoints or schema files
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
graphql-codegen <command> [options]
|
|
16
|
+
|
|
17
|
+
Commands:
|
|
18
|
+
init Initialize a new graphql-codegen configuration file
|
|
19
|
+
generate Generate SDK from GraphQL endpoint or schema file
|
|
20
|
+
generate-orm Generate Prisma-like ORM client from GraphQL endpoint or schema file
|
|
21
|
+
introspect Introspect a GraphQL endpoint or schema file and print table info
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
--help, -h Show this help message
|
|
25
|
+
--version, -v Show version number
|
|
26
|
+
|
|
27
|
+
Run 'graphql-codegen <command> --help' for more information on a command.
|
|
28
|
+
`;
|
|
29
|
+
const initUsageText = `
|
|
30
|
+
graphql-codegen init - Initialize a new graphql-codegen configuration file
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
graphql-codegen init [options]
|
|
34
|
+
|
|
35
|
+
Options:
|
|
36
|
+
--directory, -d <dir> Target directory for the config file (default: .)
|
|
37
|
+
--force, -f Force overwrite existing config
|
|
38
|
+
--endpoint, -e <url> GraphQL endpoint URL to pre-populate
|
|
39
|
+
--output, -o <dir> Output directory to pre-populate (default: ./generated)
|
|
40
|
+
--help, -h Show this help message
|
|
41
|
+
`;
|
|
42
|
+
const generateUsageText = `
|
|
43
|
+
graphql-codegen generate - Generate SDK from GraphQL endpoint or schema file
|
|
44
|
+
|
|
45
|
+
Usage:
|
|
46
|
+
graphql-codegen generate [options]
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
--config, -c <path> Path to config file
|
|
50
|
+
--target, -t <name> Target name in config file
|
|
51
|
+
--endpoint, -e <url> GraphQL endpoint URL (overrides config)
|
|
52
|
+
--schema, -s <path> Path to GraphQL schema file (.graphql)
|
|
53
|
+
--output, -o <dir> Output directory (overrides config)
|
|
54
|
+
--authorization, -a <header> Authorization header value
|
|
55
|
+
--verbose, -v Verbose output
|
|
56
|
+
--dry-run Dry run - show what would be generated without writing files
|
|
57
|
+
--watch, -w Watch mode - poll endpoint for schema changes (in-memory)
|
|
58
|
+
--poll-interval <ms> Polling interval in milliseconds (default: 3000)
|
|
59
|
+
--debounce <ms> Debounce delay before regenerating (default: 800)
|
|
60
|
+
--touch <file> File to touch on schema change
|
|
61
|
+
--no-clear Do not clear terminal on regeneration
|
|
62
|
+
--help, -h Show this help message
|
|
63
|
+
`;
|
|
64
|
+
const generateOrmUsageText = `
|
|
65
|
+
graphql-codegen generate-orm - Generate Prisma-like ORM client from GraphQL endpoint or schema file
|
|
66
|
+
|
|
67
|
+
Usage:
|
|
68
|
+
graphql-codegen generate-orm [options]
|
|
69
|
+
|
|
70
|
+
Options:
|
|
71
|
+
--config, -c <path> Path to config file
|
|
72
|
+
--target, -t <name> Target name in config file
|
|
73
|
+
--endpoint, -e <url> GraphQL endpoint URL (overrides config)
|
|
74
|
+
--schema, -s <path> Path to GraphQL schema file (.graphql)
|
|
75
|
+
--output, -o <dir> Output directory (overrides config)
|
|
76
|
+
--authorization, -a <header> Authorization header value
|
|
77
|
+
--verbose, -v Verbose output
|
|
78
|
+
--dry-run Dry run - show what would be generated without writing files
|
|
79
|
+
--skip-custom-operations Skip custom operations (only generate table CRUD)
|
|
80
|
+
--watch, -w Watch mode - poll endpoint for schema changes (in-memory)
|
|
81
|
+
--poll-interval <ms> Polling interval in milliseconds (default: 3000)
|
|
82
|
+
--debounce <ms> Debounce delay before regenerating (default: 800)
|
|
83
|
+
--touch <file> File to touch on schema change
|
|
84
|
+
--no-clear Do not clear terminal on regeneration
|
|
85
|
+
--help, -h Show this help message
|
|
86
|
+
`;
|
|
87
|
+
const introspectUsageText = `
|
|
88
|
+
graphql-codegen introspect - Introspect a GraphQL endpoint or schema file and print table info
|
|
89
|
+
|
|
90
|
+
Usage:
|
|
91
|
+
graphql-codegen introspect [options]
|
|
92
|
+
|
|
93
|
+
Options:
|
|
94
|
+
--endpoint, -e <url> GraphQL endpoint URL
|
|
95
|
+
--schema, -s <path> Path to GraphQL schema file (.graphql)
|
|
96
|
+
--authorization, -a <header> Authorization header value
|
|
97
|
+
--json Output as JSON
|
|
98
|
+
--help, -h Show this help message
|
|
99
|
+
`;
|
|
10
100
|
/**
|
|
11
101
|
* Format duration in a human-readable way
|
|
12
102
|
* - Under 1 second: show milliseconds (e.g., "123ms")
|
|
@@ -18,12 +108,10 @@ function formatDuration(ms) {
|
|
|
18
108
|
}
|
|
19
109
|
return `${(ms / 1000).toFixed(2)}s`;
|
|
20
110
|
}
|
|
21
|
-
const program = new Command();
|
|
22
111
|
/**
|
|
23
112
|
* Load configuration for watch mode, merging CLI options with config file
|
|
24
113
|
*/
|
|
25
114
|
async function loadWatchConfig(options) {
|
|
26
|
-
// Find config file
|
|
27
115
|
let configPath = options.config;
|
|
28
116
|
if (!configPath) {
|
|
29
117
|
configPath = findConfigFile() ?? undefined;
|
|
@@ -87,25 +175,20 @@ async function loadWatchConfig(options) {
|
|
|
87
175
|
}
|
|
88
176
|
return resolveConfig(mergedTarget);
|
|
89
177
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.option('-d, --directory <dir>', 'Target directory for the config file', '.')
|
|
99
|
-
.option('-f, --force', 'Force overwrite existing config', false)
|
|
100
|
-
.option('-e, --endpoint <url>', 'GraphQL endpoint URL to pre-populate')
|
|
101
|
-
.option('-o, --output <dir>', 'Output directory to pre-populate', './generated')
|
|
102
|
-
.action(async (options) => {
|
|
178
|
+
/**
|
|
179
|
+
* Init command handler
|
|
180
|
+
*/
|
|
181
|
+
async function handleInit(argv) {
|
|
182
|
+
if (argv.help || argv.h) {
|
|
183
|
+
console.log(initUsageText);
|
|
184
|
+
process.exit(0);
|
|
185
|
+
}
|
|
103
186
|
const startTime = performance.now();
|
|
104
187
|
const result = await initCommand({
|
|
105
|
-
directory:
|
|
106
|
-
force:
|
|
107
|
-
endpoint:
|
|
108
|
-
output:
|
|
188
|
+
directory: argv.directory || argv.d || '.',
|
|
189
|
+
force: !!(argv.force || argv.f),
|
|
190
|
+
endpoint: argv.endpoint || argv.e,
|
|
191
|
+
output: argv.output || argv.o || './generated',
|
|
109
192
|
});
|
|
110
193
|
const duration = formatDuration(performance.now() - startTime);
|
|
111
194
|
if (result.success) {
|
|
@@ -115,62 +198,75 @@ program
|
|
|
115
198
|
console.error('x', result.message, `(${duration})`);
|
|
116
199
|
process.exit(1);
|
|
117
200
|
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
.option('-o, --output <dir>', 'Output directory (overrides config)')
|
|
128
|
-
.option('-a, --authorization <header>', 'Authorization header value')
|
|
129
|
-
.option('-v, --verbose', 'Verbose output', false)
|
|
130
|
-
.option('--dry-run', 'Dry run - show what would be generated without writing files', false)
|
|
131
|
-
.option('-w, --watch', 'Watch mode - poll endpoint for schema changes (in-memory)', false)
|
|
132
|
-
.option('--poll-interval <ms>', 'Polling interval in milliseconds (default: 3000)', parseInt)
|
|
133
|
-
.option('--debounce <ms>', 'Debounce delay before regenerating (default: 800)', parseInt)
|
|
134
|
-
.option('--touch <file>', 'File to touch on schema change')
|
|
135
|
-
.option('--no-clear', 'Do not clear terminal on regeneration')
|
|
136
|
-
.action(async (options) => {
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Generate command handler
|
|
204
|
+
*/
|
|
205
|
+
async function handleGenerate(argv) {
|
|
206
|
+
if (argv.help || argv.h) {
|
|
207
|
+
console.log(generateUsageText);
|
|
208
|
+
process.exit(0);
|
|
209
|
+
}
|
|
137
210
|
const startTime = performance.now();
|
|
138
|
-
|
|
139
|
-
|
|
211
|
+
const config = argv.config || argv.c;
|
|
212
|
+
const target = argv.target || argv.t;
|
|
213
|
+
const endpoint = argv.endpoint || argv.e;
|
|
214
|
+
const schema = argv.schema || argv.s;
|
|
215
|
+
const output = argv.output || argv.o;
|
|
216
|
+
const authorization = argv.authorization || argv.a;
|
|
217
|
+
const verbose = !!(argv.verbose || argv.v);
|
|
218
|
+
const dryRun = !!(argv['dry-run'] || argv.dryRun);
|
|
219
|
+
const watch = !!(argv.watch || argv.w);
|
|
220
|
+
const pollInterval = argv['poll-interval'] !== undefined
|
|
221
|
+
? parseInt(argv['poll-interval'], 10)
|
|
222
|
+
: undefined;
|
|
223
|
+
const debounce = argv.debounce !== undefined
|
|
224
|
+
? parseInt(argv.debounce, 10)
|
|
225
|
+
: undefined;
|
|
226
|
+
const touch = argv.touch;
|
|
227
|
+
const clear = argv.clear !== false;
|
|
228
|
+
if (endpoint && schema) {
|
|
140
229
|
console.error('x Cannot use both --endpoint and --schema. Choose one source.');
|
|
141
230
|
process.exit(1);
|
|
142
231
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (options.schema) {
|
|
232
|
+
if (watch) {
|
|
233
|
+
if (schema) {
|
|
146
234
|
console.error('x Watch mode is only supported with --endpoint, not --schema.');
|
|
147
235
|
process.exit(1);
|
|
148
236
|
}
|
|
149
|
-
const
|
|
150
|
-
|
|
237
|
+
const watchConfig = await loadWatchConfig({
|
|
238
|
+
config,
|
|
239
|
+
target,
|
|
240
|
+
endpoint,
|
|
241
|
+
output,
|
|
242
|
+
pollInterval,
|
|
243
|
+
debounce,
|
|
244
|
+
touch,
|
|
245
|
+
clear,
|
|
246
|
+
});
|
|
247
|
+
if (!watchConfig) {
|
|
151
248
|
process.exit(1);
|
|
152
249
|
}
|
|
153
250
|
await startWatch({
|
|
154
|
-
config,
|
|
251
|
+
config: watchConfig,
|
|
155
252
|
generatorType: 'generate',
|
|
156
|
-
verbose
|
|
157
|
-
authorization
|
|
158
|
-
configPath:
|
|
159
|
-
target
|
|
160
|
-
outputDir:
|
|
253
|
+
verbose,
|
|
254
|
+
authorization,
|
|
255
|
+
configPath: config,
|
|
256
|
+
target,
|
|
257
|
+
outputDir: output,
|
|
161
258
|
});
|
|
162
259
|
return;
|
|
163
260
|
}
|
|
164
|
-
// Normal one-shot generation
|
|
165
261
|
const result = await generateCommand({
|
|
166
|
-
config
|
|
167
|
-
target
|
|
168
|
-
endpoint
|
|
169
|
-
schema
|
|
170
|
-
output
|
|
171
|
-
authorization
|
|
172
|
-
verbose
|
|
173
|
-
dryRun
|
|
262
|
+
config,
|
|
263
|
+
target,
|
|
264
|
+
endpoint,
|
|
265
|
+
schema,
|
|
266
|
+
output,
|
|
267
|
+
authorization,
|
|
268
|
+
verbose,
|
|
269
|
+
dryRun,
|
|
174
270
|
});
|
|
175
271
|
const duration = formatDuration(performance.now() - startTime);
|
|
176
272
|
const targetResults = result.targets ?? [];
|
|
@@ -178,19 +274,19 @@ program
|
|
|
178
274
|
(targetResults.length > 1 || targetResults[0]?.name !== 'default');
|
|
179
275
|
if (hasNamedTargets) {
|
|
180
276
|
console.log(result.success ? '[ok]' : 'x', result.message);
|
|
181
|
-
targetResults.forEach((
|
|
182
|
-
const status =
|
|
183
|
-
console.log(`\n${status} ${
|
|
184
|
-
if (
|
|
277
|
+
targetResults.forEach((t) => {
|
|
278
|
+
const status = t.success ? '[ok]' : 'x';
|
|
279
|
+
console.log(`\n${status} ${t.message}`);
|
|
280
|
+
if (t.tables && t.tables.length > 0) {
|
|
185
281
|
console.log(' Tables:');
|
|
186
|
-
|
|
282
|
+
t.tables.forEach((table) => console.log(` - ${table}`));
|
|
187
283
|
}
|
|
188
|
-
if (
|
|
284
|
+
if (t.filesWritten && t.filesWritten.length > 0) {
|
|
189
285
|
console.log(' Files written:');
|
|
190
|
-
|
|
286
|
+
t.filesWritten.forEach((file) => console.log(` - ${file}`));
|
|
191
287
|
}
|
|
192
|
-
if (!
|
|
193
|
-
|
|
288
|
+
if (!t.success && t.errors) {
|
|
289
|
+
t.errors.forEach((error) => console.error(` - ${error}`));
|
|
194
290
|
}
|
|
195
291
|
});
|
|
196
292
|
if (!result.success) {
|
|
@@ -216,65 +312,78 @@ program
|
|
|
216
312
|
}
|
|
217
313
|
process.exit(1);
|
|
218
314
|
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
.option('-o, --output <dir>', 'Output directory (overrides config)')
|
|
229
|
-
.option('-a, --authorization <header>', 'Authorization header value')
|
|
230
|
-
.option('-v, --verbose', 'Verbose output', false)
|
|
231
|
-
.option('--dry-run', 'Dry run - show what would be generated without writing files', false)
|
|
232
|
-
.option('--skip-custom-operations', 'Skip custom operations (only generate table CRUD)', false)
|
|
233
|
-
.option('-w, --watch', 'Watch mode - poll endpoint for schema changes (in-memory)', false)
|
|
234
|
-
.option('--poll-interval <ms>', 'Polling interval in milliseconds (default: 3000)', parseInt)
|
|
235
|
-
.option('--debounce <ms>', 'Debounce delay before regenerating (default: 800)', parseInt)
|
|
236
|
-
.option('--touch <file>', 'File to touch on schema change')
|
|
237
|
-
.option('--no-clear', 'Do not clear terminal on regeneration')
|
|
238
|
-
.action(async (options) => {
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Generate ORM command handler
|
|
318
|
+
*/
|
|
319
|
+
async function handleGenerateOrm(argv) {
|
|
320
|
+
if (argv.help || argv.h) {
|
|
321
|
+
console.log(generateOrmUsageText);
|
|
322
|
+
process.exit(0);
|
|
323
|
+
}
|
|
239
324
|
const startTime = performance.now();
|
|
240
|
-
|
|
241
|
-
|
|
325
|
+
const config = argv.config || argv.c;
|
|
326
|
+
const target = argv.target || argv.t;
|
|
327
|
+
const endpoint = argv.endpoint || argv.e;
|
|
328
|
+
const schema = argv.schema || argv.s;
|
|
329
|
+
const output = argv.output || argv.o;
|
|
330
|
+
const authorization = argv.authorization || argv.a;
|
|
331
|
+
const verbose = !!(argv.verbose || argv.v);
|
|
332
|
+
const dryRun = !!(argv['dry-run'] || argv.dryRun);
|
|
333
|
+
const skipCustomOperations = !!(argv['skip-custom-operations'] || argv.skipCustomOperations);
|
|
334
|
+
const watch = !!(argv.watch || argv.w);
|
|
335
|
+
const pollInterval = argv['poll-interval'] !== undefined
|
|
336
|
+
? parseInt(argv['poll-interval'], 10)
|
|
337
|
+
: undefined;
|
|
338
|
+
const debounce = argv.debounce !== undefined
|
|
339
|
+
? parseInt(argv.debounce, 10)
|
|
340
|
+
: undefined;
|
|
341
|
+
const touch = argv.touch;
|
|
342
|
+
const clear = argv.clear !== false;
|
|
343
|
+
if (endpoint && schema) {
|
|
242
344
|
console.error('x Cannot use both --endpoint and --schema. Choose one source.');
|
|
243
345
|
process.exit(1);
|
|
244
346
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (options.schema) {
|
|
347
|
+
if (watch) {
|
|
348
|
+
if (schema) {
|
|
248
349
|
console.error('x Watch mode is only supported with --endpoint, not --schema.');
|
|
249
350
|
process.exit(1);
|
|
250
351
|
}
|
|
251
|
-
const
|
|
252
|
-
|
|
352
|
+
const watchConfig = await loadWatchConfig({
|
|
353
|
+
config,
|
|
354
|
+
target,
|
|
355
|
+
endpoint,
|
|
356
|
+
output,
|
|
357
|
+
pollInterval,
|
|
358
|
+
debounce,
|
|
359
|
+
touch,
|
|
360
|
+
clear,
|
|
361
|
+
});
|
|
362
|
+
if (!watchConfig) {
|
|
253
363
|
process.exit(1);
|
|
254
364
|
}
|
|
255
365
|
await startWatch({
|
|
256
|
-
config,
|
|
366
|
+
config: watchConfig,
|
|
257
367
|
generatorType: 'generate-orm',
|
|
258
|
-
verbose
|
|
259
|
-
authorization
|
|
260
|
-
configPath:
|
|
261
|
-
target
|
|
262
|
-
outputDir:
|
|
263
|
-
skipCustomOperations
|
|
368
|
+
verbose,
|
|
369
|
+
authorization,
|
|
370
|
+
configPath: config,
|
|
371
|
+
target,
|
|
372
|
+
outputDir: output,
|
|
373
|
+
skipCustomOperations,
|
|
264
374
|
});
|
|
265
375
|
return;
|
|
266
376
|
}
|
|
267
|
-
// Normal one-shot generation
|
|
268
377
|
const result = await generateOrmCommand({
|
|
269
|
-
config
|
|
270
|
-
target
|
|
271
|
-
endpoint
|
|
272
|
-
schema
|
|
273
|
-
output
|
|
274
|
-
authorization
|
|
275
|
-
verbose
|
|
276
|
-
dryRun
|
|
277
|
-
skipCustomOperations
|
|
378
|
+
config,
|
|
379
|
+
target,
|
|
380
|
+
endpoint,
|
|
381
|
+
schema,
|
|
382
|
+
output,
|
|
383
|
+
authorization,
|
|
384
|
+
verbose,
|
|
385
|
+
dryRun,
|
|
386
|
+
skipCustomOperations,
|
|
278
387
|
});
|
|
279
388
|
const duration = formatDuration(performance.now() - startTime);
|
|
280
389
|
const targetResults = result.targets ?? [];
|
|
@@ -282,27 +391,27 @@ program
|
|
|
282
391
|
(targetResults.length > 1 || targetResults[0]?.name !== 'default');
|
|
283
392
|
if (hasNamedTargets) {
|
|
284
393
|
console.log(result.success ? '[ok]' : 'x', result.message);
|
|
285
|
-
targetResults.forEach((
|
|
286
|
-
const status =
|
|
287
|
-
console.log(`\n${status} ${
|
|
288
|
-
if (
|
|
394
|
+
targetResults.forEach((t) => {
|
|
395
|
+
const status = t.success ? '[ok]' : 'x';
|
|
396
|
+
console.log(`\n${status} ${t.message}`);
|
|
397
|
+
if (t.tables && t.tables.length > 0) {
|
|
289
398
|
console.log(' Tables:');
|
|
290
|
-
|
|
399
|
+
t.tables.forEach((table) => console.log(` - ${table}`));
|
|
291
400
|
}
|
|
292
|
-
if (
|
|
401
|
+
if (t.customQueries && t.customQueries.length > 0) {
|
|
293
402
|
console.log(' Custom Queries:');
|
|
294
|
-
|
|
403
|
+
t.customQueries.forEach((query) => console.log(` - ${query}`));
|
|
295
404
|
}
|
|
296
|
-
if (
|
|
405
|
+
if (t.customMutations && t.customMutations.length > 0) {
|
|
297
406
|
console.log(' Custom Mutations:');
|
|
298
|
-
|
|
407
|
+
t.customMutations.forEach((mutation) => console.log(` - ${mutation}`));
|
|
299
408
|
}
|
|
300
|
-
if (
|
|
409
|
+
if (t.filesWritten && t.filesWritten.length > 0) {
|
|
301
410
|
console.log(' Files written:');
|
|
302
|
-
|
|
411
|
+
t.filesWritten.forEach((file) => console.log(` - ${file}`));
|
|
303
412
|
}
|
|
304
|
-
if (!
|
|
305
|
-
|
|
413
|
+
if (!t.success && t.errors) {
|
|
414
|
+
t.errors.forEach((error) => console.error(` - ${error}`));
|
|
306
415
|
}
|
|
307
416
|
});
|
|
308
417
|
if (!result.success) {
|
|
@@ -336,23 +445,25 @@ program
|
|
|
336
445
|
}
|
|
337
446
|
process.exit(1);
|
|
338
447
|
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
.
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
.action(async (options) => {
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Introspect command handler
|
|
451
|
+
*/
|
|
452
|
+
async function handleIntrospect(argv) {
|
|
453
|
+
if (argv.help || argv.h) {
|
|
454
|
+
console.log(introspectUsageText);
|
|
455
|
+
process.exit(0);
|
|
456
|
+
}
|
|
349
457
|
const startTime = performance.now();
|
|
350
|
-
|
|
351
|
-
|
|
458
|
+
const endpoint = argv.endpoint || argv.e;
|
|
459
|
+
const schema = argv.schema || argv.s;
|
|
460
|
+
const authorization = argv.authorization || argv.a;
|
|
461
|
+
const json = !!argv.json;
|
|
462
|
+
if (!endpoint && !schema) {
|
|
352
463
|
console.error('x Either --endpoint or --schema must be provided.');
|
|
353
464
|
process.exit(1);
|
|
354
465
|
}
|
|
355
|
-
if (
|
|
466
|
+
if (endpoint && schema) {
|
|
356
467
|
console.error('x Cannot use both --endpoint and --schema. Choose one source.');
|
|
357
468
|
process.exit(1);
|
|
358
469
|
}
|
|
@@ -360,15 +471,15 @@ program
|
|
|
360
471
|
const { inferTablesFromIntrospection } = await import('./introspect/infer-tables');
|
|
361
472
|
try {
|
|
362
473
|
const source = createSchemaSource({
|
|
363
|
-
endpoint
|
|
364
|
-
schema
|
|
365
|
-
authorization
|
|
474
|
+
endpoint,
|
|
475
|
+
schema,
|
|
476
|
+
authorization,
|
|
366
477
|
});
|
|
367
478
|
console.log('Fetching schema from', source.describe(), '...');
|
|
368
479
|
const { introspection } = await source.fetch();
|
|
369
480
|
const tables = inferTablesFromIntrospection(introspection);
|
|
370
481
|
const duration = formatDuration(performance.now() - startTime);
|
|
371
|
-
if (
|
|
482
|
+
if (json) {
|
|
372
483
|
console.log(JSON.stringify(tables, null, 2));
|
|
373
484
|
}
|
|
374
485
|
else {
|
|
@@ -388,5 +499,89 @@ program
|
|
|
388
499
|
console.error('x Failed to introspect schema:', err instanceof Error ? err.message : err, `(${duration})`);
|
|
389
500
|
process.exit(1);
|
|
390
501
|
}
|
|
391
|
-
}
|
|
392
|
-
|
|
502
|
+
}
|
|
503
|
+
const createCommandMap = () => {
|
|
504
|
+
return {
|
|
505
|
+
init: handleInit,
|
|
506
|
+
generate: handleGenerate,
|
|
507
|
+
'generate-orm': handleGenerateOrm,
|
|
508
|
+
introspect: handleIntrospect,
|
|
509
|
+
};
|
|
510
|
+
};
|
|
511
|
+
export const commands = async (argv, prompter, _options) => {
|
|
512
|
+
if (argv.version || argv.v) {
|
|
513
|
+
const pkg = getPackageJson(__dirname);
|
|
514
|
+
console.log(pkg.version);
|
|
515
|
+
process.exit(0);
|
|
516
|
+
}
|
|
517
|
+
const { first: command, newArgv } = extractFirst(argv);
|
|
518
|
+
if ((argv.help || argv.h) && !command) {
|
|
519
|
+
console.log(usageText);
|
|
520
|
+
process.exit(0);
|
|
521
|
+
}
|
|
522
|
+
if (command === 'help') {
|
|
523
|
+
console.log(usageText);
|
|
524
|
+
process.exit(0);
|
|
525
|
+
}
|
|
526
|
+
const commandMap = createCommandMap();
|
|
527
|
+
if (!command) {
|
|
528
|
+
const answer = await prompter.prompt(argv, [
|
|
529
|
+
{
|
|
530
|
+
type: 'autocomplete',
|
|
531
|
+
name: 'command',
|
|
532
|
+
message: 'What do you want to do?',
|
|
533
|
+
options: Object.keys(commandMap),
|
|
534
|
+
},
|
|
535
|
+
]);
|
|
536
|
+
const selectedCommand = answer.command;
|
|
537
|
+
const commandFn = commandMap[selectedCommand];
|
|
538
|
+
if (commandFn) {
|
|
539
|
+
await commandFn(newArgv);
|
|
540
|
+
}
|
|
541
|
+
prompter.close();
|
|
542
|
+
return argv;
|
|
543
|
+
}
|
|
544
|
+
const commandFn = commandMap[command];
|
|
545
|
+
if (!commandFn) {
|
|
546
|
+
console.log(usageText);
|
|
547
|
+
await cliExitWithError(`Unknown command: ${command}`);
|
|
548
|
+
}
|
|
549
|
+
await commandFn(newArgv);
|
|
550
|
+
prompter.close();
|
|
551
|
+
return argv;
|
|
552
|
+
};
|
|
553
|
+
export const options = {
|
|
554
|
+
minimistOpts: {
|
|
555
|
+
alias: {
|
|
556
|
+
v: 'version',
|
|
557
|
+
h: 'help',
|
|
558
|
+
c: 'config',
|
|
559
|
+
t: 'target',
|
|
560
|
+
e: 'endpoint',
|
|
561
|
+
s: 'schema',
|
|
562
|
+
o: 'output',
|
|
563
|
+
a: 'authorization',
|
|
564
|
+
d: 'directory',
|
|
565
|
+
f: 'force',
|
|
566
|
+
w: 'watch',
|
|
567
|
+
},
|
|
568
|
+
boolean: ['help', 'version', 'force', 'verbose', 'dry-run', 'watch', 'json', 'skip-custom-operations', 'clear'],
|
|
569
|
+
string: ['config', 'target', 'endpoint', 'schema', 'output', 'authorization', 'directory', 'touch', 'poll-interval', 'debounce'],
|
|
570
|
+
default: {
|
|
571
|
+
clear: true,
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
};
|
|
575
|
+
if (require.main === module) {
|
|
576
|
+
if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
577
|
+
const pkg = getPackageJson(__dirname);
|
|
578
|
+
console.log(pkg.version);
|
|
579
|
+
process.exit(0);
|
|
580
|
+
}
|
|
581
|
+
const app = new CLI(commands, options);
|
|
582
|
+
app.run().then(() => {
|
|
583
|
+
}).catch((error) => {
|
|
584
|
+
console.error('Unexpected error:', error);
|
|
585
|
+
process.exit(1);
|
|
586
|
+
});
|
|
587
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructive-io/graphql-codegen",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.28.0",
|
|
4
4
|
"description": "CLI-based GraphQL SDK generator for PostGraphile endpoints with React Query hooks",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphql",
|
|
@@ -54,11 +54,13 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@babel/generator": "^7.28.6",
|
|
56
56
|
"@babel/types": "^7.28.6",
|
|
57
|
+
"@inquirerer/utils": "^3.2.0",
|
|
57
58
|
"ajv": "^8.17.1",
|
|
58
|
-
"
|
|
59
|
+
"find-and-require-package-json": "^0.9.0",
|
|
59
60
|
"gql-ast": "^2.6.0",
|
|
60
61
|
"graphql": "15.10.1",
|
|
61
|
-
"inflekt": "^0.
|
|
62
|
+
"inflekt": "^0.3.0",
|
|
63
|
+
"inquirerer": "^4.4.0",
|
|
62
64
|
"jiti": "^2.6.1",
|
|
63
65
|
"oxfmt": "^0.13.0"
|
|
64
66
|
},
|
|
@@ -75,16 +77,16 @@
|
|
|
75
77
|
}
|
|
76
78
|
},
|
|
77
79
|
"devDependencies": {
|
|
78
|
-
"@tanstack/react-query": "^5.90.
|
|
80
|
+
"@tanstack/react-query": "^5.90.19",
|
|
79
81
|
"@types/babel__generator": "^7.27.0",
|
|
80
|
-
"@types/jest": "^
|
|
82
|
+
"@types/jest": "^30.0.0",
|
|
81
83
|
"@types/node": "^20.19.27",
|
|
82
|
-
"@types/react": "^19.2.
|
|
83
|
-
"jest": "^
|
|
84
|
+
"@types/react": "^19.2.8",
|
|
85
|
+
"jest": "^30.2.0",
|
|
84
86
|
"react": "^19.2.3",
|
|
85
87
|
"ts-jest": "^29.2.5",
|
|
86
88
|
"tsx": "^4.21.0",
|
|
87
89
|
"typescript": "^5.9.3"
|
|
88
90
|
},
|
|
89
|
-
"gitHead": "
|
|
91
|
+
"gitHead": "ec2b5f644c479626305ef85a05a6e7105c2c58cd"
|
|
90
92
|
}
|