@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.
- package/README.md +403 -279
- package/cli/codegen/babel-ast.d.ts +7 -0
- package/cli/codegen/babel-ast.js +15 -0
- package/cli/codegen/barrel.js +43 -14
- package/cli/codegen/custom-mutations.js +4 -4
- package/cli/codegen/custom-queries.js +12 -22
- package/cli/codegen/gql-ast.js +22 -1
- package/cli/codegen/index.js +1 -0
- package/cli/codegen/mutations.d.ts +2 -0
- package/cli/codegen/mutations.js +26 -13
- package/cli/codegen/orm/client-generator.js +475 -136
- package/cli/codegen/orm/custom-ops-generator.js +8 -3
- package/cli/codegen/orm/input-types-generator.js +22 -0
- package/cli/codegen/orm/model-generator.js +18 -5
- package/cli/codegen/orm/select-types.d.ts +33 -0
- package/cli/codegen/queries.d.ts +1 -1
- package/cli/codegen/queries.js +112 -35
- package/cli/codegen/utils.d.ts +6 -0
- package/cli/codegen/utils.js +19 -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/babel-ast.d.ts +7 -0
- package/esm/cli/codegen/babel-ast.js +14 -0
- package/esm/cli/codegen/barrel.js +44 -15
- package/esm/cli/codegen/custom-mutations.js +5 -5
- package/esm/cli/codegen/custom-queries.js +13 -23
- package/esm/cli/codegen/gql-ast.js +23 -2
- package/esm/cli/codegen/index.js +1 -0
- package/esm/cli/codegen/mutations.d.ts +2 -0
- package/esm/cli/codegen/mutations.js +27 -14
- 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/input-types-generator.js +22 -0
- package/esm/cli/codegen/orm/model-generator.js +18 -5
- package/esm/cli/codegen/orm/select-types.d.ts +33 -0
- package/esm/cli/codegen/queries.d.ts +1 -1
- package/esm/cli/codegen/queries.js +114 -37
- package/esm/cli/codegen/utils.d.ts +6 -0
- package/esm/cli/codegen/utils.js +18 -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
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import * as fs from 'node:fs';
|
|
10
10
|
import * as path from 'node:path';
|
|
11
|
-
import
|
|
12
|
-
import { resolveConfig } from '../../types/config';
|
|
11
|
+
import { execSync } from 'node:child_process';
|
|
12
|
+
import { isMultiConfig, mergeConfig, resolveConfig } from '../../types/config';
|
|
13
13
|
import { createSchemaSource, validateSourceOptions, } from '../introspect/source';
|
|
14
14
|
import { runCodegenPipeline, validateTablesFound } from './shared';
|
|
15
15
|
import { findConfigFile, loadConfigFile } from './init';
|
|
@@ -18,9 +18,9 @@ import { generate } from '../codegen';
|
|
|
18
18
|
* Execute the generate command
|
|
19
19
|
*/
|
|
20
20
|
export async function generateCommand(options = {}) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
if (options.verbose) {
|
|
22
|
+
console.log('Loading configuration...');
|
|
23
|
+
}
|
|
24
24
|
const configResult = await loadConfig(options);
|
|
25
25
|
if (!configResult.success) {
|
|
26
26
|
return {
|
|
@@ -28,24 +28,72 @@ export async function generateCommand(options = {}) {
|
|
|
28
28
|
message: configResult.error,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
const targets = configResult.targets ?? [];
|
|
32
|
+
if (targets.length === 0) {
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
message: 'No targets resolved from configuration.',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const isMultiTarget = configResult.isMulti ?? targets.length > 1;
|
|
39
|
+
const results = [];
|
|
40
|
+
for (const target of targets) {
|
|
41
|
+
const result = await generateForTarget(target, options, isMultiTarget);
|
|
42
|
+
results.push(result);
|
|
43
|
+
}
|
|
44
|
+
if (!isMultiTarget) {
|
|
45
|
+
const [result] = results;
|
|
46
|
+
return {
|
|
47
|
+
success: result.success,
|
|
48
|
+
message: result.message,
|
|
49
|
+
targets: results,
|
|
50
|
+
tables: result.tables,
|
|
51
|
+
customQueries: result.customQueries,
|
|
52
|
+
customMutations: result.customMutations,
|
|
53
|
+
filesWritten: result.filesWritten,
|
|
54
|
+
errors: result.errors,
|
|
55
|
+
};
|
|
35
56
|
}
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
const successCount = results.filter((result) => result.success).length;
|
|
58
|
+
const failedCount = results.length - successCount;
|
|
59
|
+
const summaryMessage = failedCount === 0
|
|
60
|
+
? `Generated SDK for ${results.length} targets.`
|
|
61
|
+
: `Generated SDK for ${successCount} of ${results.length} targets.`;
|
|
62
|
+
return {
|
|
63
|
+
success: failedCount === 0,
|
|
64
|
+
message: summaryMessage,
|
|
65
|
+
targets: results,
|
|
66
|
+
errors: failedCount > 0
|
|
67
|
+
? results.flatMap((result) => result.errors ?? [])
|
|
68
|
+
: undefined,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
async function generateForTarget(target, options, isMultiTarget) {
|
|
72
|
+
const config = target.config;
|
|
73
|
+
const prefix = isMultiTarget ? `[${target.name}] ` : '';
|
|
74
|
+
const log = options.verbose
|
|
75
|
+
? (message) => console.log(`${prefix}${message}`)
|
|
76
|
+
: () => { };
|
|
77
|
+
const formatMessage = (message) => isMultiTarget ? `Target "${target.name}": ${message}` : message;
|
|
78
|
+
if (isMultiTarget) {
|
|
79
|
+
console.log(`\nTarget "${target.name}"`);
|
|
80
|
+
const sourceLabel = config.schema
|
|
81
|
+
? `schema: ${config.schema}`
|
|
82
|
+
: `endpoint: ${config.endpoint}`;
|
|
83
|
+
console.log(` Source: ${sourceLabel}`);
|
|
84
|
+
console.log(` Output: ${config.output}`);
|
|
38
85
|
}
|
|
39
|
-
|
|
40
|
-
// 2. Create schema source
|
|
86
|
+
// 1. Validate source
|
|
41
87
|
const sourceValidation = validateSourceOptions({
|
|
42
88
|
endpoint: config.endpoint || undefined,
|
|
43
89
|
schema: config.schema || undefined,
|
|
44
90
|
});
|
|
45
91
|
if (!sourceValidation.valid) {
|
|
46
92
|
return {
|
|
93
|
+
name: target.name,
|
|
94
|
+
output: config.output,
|
|
47
95
|
success: false,
|
|
48
|
-
message: sourceValidation.error,
|
|
96
|
+
message: formatMessage(sourceValidation.error),
|
|
49
97
|
};
|
|
50
98
|
}
|
|
51
99
|
const source = createSchemaSource({
|
|
@@ -54,7 +102,7 @@ export async function generateCommand(options = {}) {
|
|
|
54
102
|
authorization: options.authorization || config.headers['Authorization'],
|
|
55
103
|
headers: config.headers,
|
|
56
104
|
});
|
|
57
|
-
//
|
|
105
|
+
// 2. Run the codegen pipeline
|
|
58
106
|
let pipelineResult;
|
|
59
107
|
try {
|
|
60
108
|
pipelineResult = await runCodegenPipeline({
|
|
@@ -66,21 +114,25 @@ export async function generateCommand(options = {}) {
|
|
|
66
114
|
}
|
|
67
115
|
catch (err) {
|
|
68
116
|
return {
|
|
117
|
+
name: target.name,
|
|
118
|
+
output: config.output,
|
|
69
119
|
success: false,
|
|
70
|
-
message: `Failed to fetch schema: ${err instanceof Error ? err.message : 'Unknown error'}
|
|
120
|
+
message: formatMessage(`Failed to fetch schema: ${err instanceof Error ? err.message : 'Unknown error'}`),
|
|
71
121
|
};
|
|
72
122
|
}
|
|
73
123
|
const { tables, customOperations, stats } = pipelineResult;
|
|
74
|
-
//
|
|
124
|
+
// 3. Validate tables found
|
|
75
125
|
const tablesValidation = validateTablesFound(tables);
|
|
76
126
|
if (!tablesValidation.valid) {
|
|
77
127
|
return {
|
|
128
|
+
name: target.name,
|
|
129
|
+
output: config.output,
|
|
78
130
|
success: false,
|
|
79
|
-
message: tablesValidation.error,
|
|
131
|
+
message: formatMessage(tablesValidation.error),
|
|
80
132
|
};
|
|
81
133
|
}
|
|
82
|
-
//
|
|
83
|
-
console.log(
|
|
134
|
+
// 4. Generate code
|
|
135
|
+
console.log(`${prefix}Generating code...`);
|
|
84
136
|
const { files: generatedFiles, stats: genStats } = generate({
|
|
85
137
|
tables,
|
|
86
138
|
customOperations: {
|
|
@@ -90,7 +142,7 @@ export async function generateCommand(options = {}) {
|
|
|
90
142
|
},
|
|
91
143
|
config,
|
|
92
144
|
});
|
|
93
|
-
console.log(
|
|
145
|
+
console.log(`${prefix}Generated ${genStats.totalFiles} files`);
|
|
94
146
|
log(` ${genStats.queryHooks} table query hooks`);
|
|
95
147
|
log(` ${genStats.mutationHooks} table mutation hooks`);
|
|
96
148
|
log(` ${genStats.customQueryHooks} custom query hooks`);
|
|
@@ -99,15 +151,17 @@ export async function generateCommand(options = {}) {
|
|
|
99
151
|
const customMutations = customOperations.mutations.map((m) => m.name);
|
|
100
152
|
if (options.dryRun) {
|
|
101
153
|
return {
|
|
154
|
+
name: target.name,
|
|
155
|
+
output: config.output,
|
|
102
156
|
success: true,
|
|
103
|
-
message: `Dry run complete. Would generate ${generatedFiles.length} files for ${tables.length} tables and ${stats.customQueries + stats.customMutations} custom operations
|
|
157
|
+
message: formatMessage(`Dry run complete. Would generate ${generatedFiles.length} files for ${tables.length} tables and ${stats.customQueries + stats.customMutations} custom operations.`),
|
|
104
158
|
tables: tables.map((t) => t.name),
|
|
105
159
|
customQueries,
|
|
106
160
|
customMutations,
|
|
107
161
|
filesWritten: generatedFiles.map((f) => f.path),
|
|
108
162
|
};
|
|
109
163
|
}
|
|
110
|
-
//
|
|
164
|
+
// 5. Write files
|
|
111
165
|
log('Writing files...');
|
|
112
166
|
const writeResult = await writeGeneratedFiles(generatedFiles, config.output, [
|
|
113
167
|
'queries',
|
|
@@ -115,23 +169,48 @@ export async function generateCommand(options = {}) {
|
|
|
115
169
|
]);
|
|
116
170
|
if (!writeResult.success) {
|
|
117
171
|
return {
|
|
172
|
+
name: target.name,
|
|
173
|
+
output: config.output,
|
|
118
174
|
success: false,
|
|
119
|
-
message: `Failed to write files: ${writeResult.errors?.join(', ')}
|
|
175
|
+
message: formatMessage(`Failed to write files: ${writeResult.errors?.join(', ')}`),
|
|
120
176
|
errors: writeResult.errors,
|
|
121
177
|
};
|
|
122
178
|
}
|
|
123
179
|
const totalOps = customQueries.length + customMutations.length;
|
|
124
180
|
const customOpsMsg = totalOps > 0 ? ` and ${totalOps} custom operations` : '';
|
|
125
181
|
return {
|
|
182
|
+
name: target.name,
|
|
183
|
+
output: config.output,
|
|
126
184
|
success: true,
|
|
127
|
-
message: `Generated SDK for ${tables.length} tables${customOpsMsg}. Files written to ${config.output}
|
|
185
|
+
message: formatMessage(`Generated SDK for ${tables.length} tables${customOpsMsg}. Files written to ${config.output}`),
|
|
128
186
|
tables: tables.map((t) => t.name),
|
|
129
187
|
customQueries,
|
|
130
188
|
customMutations,
|
|
131
189
|
filesWritten: writeResult.filesWritten,
|
|
132
190
|
};
|
|
133
191
|
}
|
|
192
|
+
function buildTargetOverrides(options) {
|
|
193
|
+
const overrides = {};
|
|
194
|
+
if (options.endpoint) {
|
|
195
|
+
overrides.endpoint = options.endpoint;
|
|
196
|
+
overrides.schema = undefined;
|
|
197
|
+
}
|
|
198
|
+
if (options.schema) {
|
|
199
|
+
overrides.schema = options.schema;
|
|
200
|
+
overrides.endpoint = undefined;
|
|
201
|
+
}
|
|
202
|
+
if (options.output) {
|
|
203
|
+
overrides.output = options.output;
|
|
204
|
+
}
|
|
205
|
+
return overrides;
|
|
206
|
+
}
|
|
134
207
|
async function loadConfig(options) {
|
|
208
|
+
if (options.endpoint && options.schema) {
|
|
209
|
+
return {
|
|
210
|
+
success: false,
|
|
211
|
+
error: 'Cannot use both --endpoint and --schema. Choose one source.',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
135
214
|
// Find config file
|
|
136
215
|
let configPath = options.config;
|
|
137
216
|
if (!configPath) {
|
|
@@ -145,31 +224,72 @@ async function loadConfig(options) {
|
|
|
145
224
|
}
|
|
146
225
|
baseConfig = loadResult.config;
|
|
147
226
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
227
|
+
const overrides = buildTargetOverrides(options);
|
|
228
|
+
if (isMultiConfig(baseConfig)) {
|
|
229
|
+
if (Object.keys(baseConfig.targets).length === 0) {
|
|
230
|
+
return {
|
|
231
|
+
success: false,
|
|
232
|
+
error: 'Config file defines no targets.',
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (!options.target &&
|
|
236
|
+
(options.endpoint || options.schema || options.output)) {
|
|
237
|
+
return {
|
|
238
|
+
success: false,
|
|
239
|
+
error: 'Multiple targets configured. Use --target with --endpoint, --schema, or --output.',
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
if (options.target && !baseConfig.targets[options.target]) {
|
|
243
|
+
return {
|
|
244
|
+
success: false,
|
|
245
|
+
error: `Target "${options.target}" not found in config file.`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const selectedTargets = options.target
|
|
249
|
+
? { [options.target]: baseConfig.targets[options.target] }
|
|
250
|
+
: baseConfig.targets;
|
|
251
|
+
const defaults = baseConfig.defaults ?? {};
|
|
252
|
+
const resolvedTargets = [];
|
|
253
|
+
for (const [name, target] of Object.entries(selectedTargets)) {
|
|
254
|
+
let mergedTarget = mergeConfig(defaults, target);
|
|
255
|
+
if (options.target && name === options.target) {
|
|
256
|
+
mergedTarget = mergeConfig(mergedTarget, overrides);
|
|
257
|
+
}
|
|
258
|
+
if (!mergedTarget.endpoint && !mergedTarget.schema) {
|
|
259
|
+
return {
|
|
260
|
+
success: false,
|
|
261
|
+
error: `Target "${name}" is missing an endpoint or schema.`,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
resolvedTargets.push({
|
|
265
|
+
name,
|
|
266
|
+
config: resolveConfig(mergedTarget),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
success: true,
|
|
271
|
+
targets: resolvedTargets,
|
|
272
|
+
isMulti: true,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
if (options.target) {
|
|
276
|
+
return {
|
|
277
|
+
success: false,
|
|
278
|
+
error: 'Config file does not define targets. Remove --target to continue.',
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const mergedConfig = mergeConfig(baseConfig, overrides);
|
|
164
282
|
if (!mergedConfig.endpoint && !mergedConfig.schema) {
|
|
165
283
|
return {
|
|
166
284
|
success: false,
|
|
167
285
|
error: 'No source specified. Use --endpoint or --schema, or create a config file with "graphql-codegen init".',
|
|
168
286
|
};
|
|
169
287
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
288
|
+
return {
|
|
289
|
+
success: true,
|
|
290
|
+
targets: [{ name: 'default', config: resolveConfig(mergedConfig) }],
|
|
291
|
+
isMulti: false,
|
|
292
|
+
};
|
|
173
293
|
}
|
|
174
294
|
export async function writeGeneratedFiles(files, outputDir, subdirs, options = {}) {
|
|
175
295
|
const { showProgress = true } = options;
|
|
@@ -225,9 +345,7 @@ export async function writeGeneratedFiles(files, outputDir, subdirs, options = {
|
|
|
225
345
|
// Ignore if already exists
|
|
226
346
|
}
|
|
227
347
|
try {
|
|
228
|
-
|
|
229
|
-
const formattedContent = await formatCode(file.content);
|
|
230
|
-
fs.writeFileSync(filePath, formattedContent, 'utf-8');
|
|
348
|
+
fs.writeFileSync(filePath, file.content, 'utf-8');
|
|
231
349
|
written.push(filePath);
|
|
232
350
|
}
|
|
233
351
|
catch (err) {
|
|
@@ -239,23 +357,44 @@ export async function writeGeneratedFiles(files, outputDir, subdirs, options = {
|
|
|
239
357
|
if (showProgress && isTTY) {
|
|
240
358
|
process.stdout.write('\r' + ' '.repeat(40) + '\r');
|
|
241
359
|
}
|
|
360
|
+
// Format all generated files with oxfmt
|
|
361
|
+
if (errors.length === 0) {
|
|
362
|
+
if (showProgress) {
|
|
363
|
+
console.log('Formatting generated files...');
|
|
364
|
+
}
|
|
365
|
+
const formatResult = formatOutput(outputDir);
|
|
366
|
+
if (!formatResult.success && showProgress) {
|
|
367
|
+
console.warn('Warning: Failed to format generated files:', formatResult.error);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
242
370
|
return {
|
|
243
371
|
success: errors.length === 0,
|
|
244
372
|
filesWritten: written,
|
|
245
373
|
errors: errors.length > 0 ? errors : undefined,
|
|
246
374
|
};
|
|
247
375
|
}
|
|
248
|
-
|
|
376
|
+
/**
|
|
377
|
+
* Format generated files using oxfmt
|
|
378
|
+
* Runs oxfmt on the output directory after all files are written
|
|
379
|
+
*/
|
|
380
|
+
export function formatOutput(outputDir) {
|
|
381
|
+
// Resolve to absolute path for reliable execution
|
|
382
|
+
const absoluteOutputDir = path.resolve(outputDir);
|
|
249
383
|
try {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
384
|
+
// Find oxfmt binary from this package's node_modules/.bin
|
|
385
|
+
// oxfmt is a dependency of @constructive-io/graphql-codegen
|
|
386
|
+
const oxfmtPkgPath = require.resolve('oxfmt/package.json');
|
|
387
|
+
const oxfmtDir = path.dirname(oxfmtPkgPath);
|
|
388
|
+
const oxfmtBin = path.join(oxfmtDir, 'bin', 'oxfmt');
|
|
389
|
+
execSync(`"${oxfmtBin}" "${absoluteOutputDir}"`, {
|
|
390
|
+
stdio: 'pipe',
|
|
391
|
+
encoding: 'utf-8',
|
|
255
392
|
});
|
|
393
|
+
return { success: true };
|
|
256
394
|
}
|
|
257
|
-
catch {
|
|
258
|
-
//
|
|
259
|
-
|
|
395
|
+
catch (err) {
|
|
396
|
+
// oxfmt may fail if files have syntax errors or if not installed
|
|
397
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
398
|
+
return { success: false, error: message };
|
|
260
399
|
}
|
|
261
400
|
}
|
package/esm/cli/commands/init.js
CHANGED
|
@@ -14,6 +14,15 @@ export default defineConfig({
|
|
|
14
14
|
// Output directory for generated files
|
|
15
15
|
output: '{{OUTPUT}}',
|
|
16
16
|
|
|
17
|
+
// Optional: Multi-target config (use instead of endpoint/output)
|
|
18
|
+
// defaults: {
|
|
19
|
+
// headers: { Authorization: 'Bearer YOUR_TOKEN' },
|
|
20
|
+
// },
|
|
21
|
+
// targets: {
|
|
22
|
+
// public: { endpoint: 'https://api.example.com/graphql', output: './generated/public' },
|
|
23
|
+
// admin: { schema: './admin.schema.graphql', output: './generated/admin' },
|
|
24
|
+
// },
|
|
25
|
+
|
|
17
26
|
// Optional: Tables to include/exclude (supports glob patterns)
|
|
18
27
|
// tables: {
|
|
19
28
|
// include: ['*'],
|
|
@@ -38,7 +47,7 @@ export default defineConfig({
|
|
|
38
47
|
* Execute the init command
|
|
39
48
|
*/
|
|
40
49
|
export async function initCommand(options = {}) {
|
|
41
|
-
const { directory = process.cwd(), force = false, endpoint = '', output = './generated' } = options;
|
|
50
|
+
const { directory = process.cwd(), force = false, endpoint = '', output = './generated', } = options;
|
|
42
51
|
const configPath = path.join(directory, CONFIG_FILENAME);
|
|
43
52
|
// Check if config already exists
|
|
44
53
|
if (fs.existsSync(configPath) && !force) {
|
|
@@ -48,9 +57,7 @@ export async function initCommand(options = {}) {
|
|
|
48
57
|
};
|
|
49
58
|
}
|
|
50
59
|
// Generate config content
|
|
51
|
-
const content = CONFIG_TEMPLATE
|
|
52
|
-
.replace('{{ENDPOINT}}', endpoint || 'http://localhost:5000/graphql')
|
|
53
|
-
.replace('{{OUTPUT}}', output);
|
|
60
|
+
const content = CONFIG_TEMPLATE.replace('{{ENDPOINT}}', endpoint || 'http://localhost:5000/graphql').replace('{{OUTPUT}}', output);
|
|
54
61
|
try {
|
|
55
62
|
// Ensure directory exists
|
|
56
63
|
fs.mkdirSync(directory, { recursive: true });
|
|
@@ -96,10 +103,11 @@ export function findConfigFile(startDir = process.cwd()) {
|
|
|
96
103
|
* tsx or ts-node installed.
|
|
97
104
|
*/
|
|
98
105
|
export async function loadConfigFile(configPath) {
|
|
99
|
-
|
|
106
|
+
const resolvedPath = path.resolve(configPath);
|
|
107
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
100
108
|
return {
|
|
101
109
|
success: false,
|
|
102
|
-
error: `Config file not found: ${
|
|
110
|
+
error: `Config file not found: ${resolvedPath}`,
|
|
103
111
|
};
|
|
104
112
|
}
|
|
105
113
|
try {
|
|
@@ -110,19 +118,31 @@ export async function loadConfigFile(configPath) {
|
|
|
110
118
|
debug: process.env.JITI_DEBUG === '1',
|
|
111
119
|
});
|
|
112
120
|
// jiti.import() with { default: true } returns mod?.default ?? mod
|
|
113
|
-
const config = await jiti.import(
|
|
121
|
+
const config = await jiti.import(resolvedPath, { default: true });
|
|
114
122
|
if (!config || typeof config !== 'object') {
|
|
115
123
|
return {
|
|
116
124
|
success: false,
|
|
117
125
|
error: 'Config file must export a configuration object',
|
|
118
126
|
};
|
|
119
127
|
}
|
|
120
|
-
|
|
128
|
+
const hasEndpoint = 'endpoint' in config;
|
|
129
|
+
const hasSchema = 'schema' in config;
|
|
130
|
+
const hasTargets = 'targets' in config;
|
|
131
|
+
if (!hasEndpoint && !hasSchema && !hasTargets) {
|
|
121
132
|
return {
|
|
122
133
|
success: false,
|
|
123
|
-
error: 'Config file
|
|
134
|
+
error: 'Config file must define "endpoint", "schema", or "targets".',
|
|
124
135
|
};
|
|
125
136
|
}
|
|
137
|
+
if (hasTargets) {
|
|
138
|
+
const targets = config.targets;
|
|
139
|
+
if (!targets || typeof targets !== 'object' || Array.isArray(targets)) {
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
error: 'Config file "targets" must be an object of named configs.',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
126
146
|
return {
|
|
127
147
|
success: true,
|
|
128
148
|
config,
|