Skip to content

Commit 595f0ea

Browse files
committed
refactor(graphql-codegen): add database/pgpm fields to config types and use Babel AST for barrel exports
- Add database, pgpmModulePath, pgpmWorkspacePath, pgpmModuleName, schemas, apiNames, keepDb fields to GraphQLSDKConfigTarget and ResolvedConfig types - Remove all (config as any) type casts now that fields are first-class citizens - Simplify buildTargetOverrides by removing redundant undefined assignments (validation happens in loadAndResolveConfig) - Update generateSharedBarrel in shared/index.ts to use Babel AST instead of string concatenation - Update generateUnifiedBarrel in generate-unified.ts to use Babel AST instead of string concatenation - Update resolveMultiTargetConfig to check for all source types (database, pgpm, etc.)
1 parent 1d8eacb commit 595f0ea

File tree

4 files changed

+195
-118
lines changed

4 files changed

+195
-118
lines changed

graphql/codegen/src/cli/commands/generate-unified.ts

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
* The internal generator functions are not exported from the package.
1010
*/
1111
import path from 'path';
12+
import * as t from '@babel/types';
13+
import { generateCode, addJSDocComment } from '../../core/codegen/babel-ast';
1214
import type { ResolvedTargetConfig } from '../../types/config';
1315
import {
1416
loadAndResolveConfig,
@@ -249,14 +251,14 @@ async function runPipelineForTarget(
249251
const formatMessage = (message: string) =>
250252
isMultiTarget ? `Target "${target.name}": ${message}` : message;
251253

252-
// Extract extended options if present (attached by config resolver)
253-
const database = (config as any).database as string | undefined;
254-
const schemas = (config as any).schemas as string[] | undefined;
255-
const apiNames = (config as any).apiNames as string[] | undefined;
256-
const pgpmModulePath = (config as any).pgpmModulePath as string | undefined;
257-
const pgpmWorkspacePath = (config as any).pgpmWorkspacePath as string | undefined;
258-
const pgpmModuleName = (config as any).pgpmModuleName as string | undefined;
259-
const keepDb = (config as any).keepDb as boolean | undefined;
254+
// Extract source options from config (now first-class citizens in ResolvedConfig)
255+
const database = config.database ?? undefined;
256+
const schemas = config.schemas.length > 0 ? config.schemas : undefined;
257+
const apiNames = config.apiNames.length > 0 ? config.apiNames : undefined;
258+
const pgpmModulePath = config.pgpmModulePath ?? undefined;
259+
const pgpmWorkspacePath = config.pgpmWorkspacePath ?? undefined;
260+
const pgpmModuleName = config.pgpmModuleName ?? undefined;
261+
const keepDb = config.keepDb;
260262

261263
if (isMultiTarget) {
262264
console.log(`\nTarget "${target.name}"`);
@@ -430,31 +432,45 @@ async function generateUnifiedOutput(
430432
}
431433

432434
/**
433-
* Generate the unified barrel export for the root output directory
435+
* Helper to create export * from './module' statement
436+
*/
437+
function exportAllFrom(modulePath: string): t.ExportAllDeclaration {
438+
return t.exportAllDeclaration(t.stringLiteral(modulePath));
439+
}
440+
441+
/**
442+
* Generate the unified barrel export for the root output directory using Babel AST
434443
*/
435444
function generateUnifiedBarrel(hasSchemaTypes: boolean): string {
436-
const lines = [
437-
'/**',
438-
' * Generated SDK - auto-generated, do not edit',
439-
' */',
440-
'',
441-
'// Shared types',
442-
"export * from './types';",
443-
];
445+
const statements: t.Statement[] = [];
446+
447+
// Shared types
448+
statements.push(exportAllFrom('./types'));
444449

445450
if (hasSchemaTypes) {
446-
lines.push("export * from './schema-types';");
451+
statements.push(exportAllFrom('./schema-types'));
447452
}
448453

449-
lines.push('');
450-
lines.push('// React Query SDK');
451-
lines.push("export * from './react-query';");
452-
lines.push('');
453-
lines.push('// ORM Client');
454-
lines.push("export * from './orm';");
455-
lines.push('');
454+
// React Query SDK
455+
statements.push(exportAllFrom('./react-query'));
456+
457+
// ORM Client
458+
statements.push(exportAllFrom('./orm'));
459+
460+
// Add file header as leading comment on first statement
461+
if (statements.length > 0) {
462+
addJSDocComment(statements[0], [
463+
'Generated SDK - auto-generated, do not edit',
464+
'@generated by @constructive-io/graphql-codegen',
465+
'',
466+
'Exports:',
467+
'- Shared types (types.ts, schema-types.ts)',
468+
'- React Query SDK (react-query/)',
469+
'- ORM Client (orm/)',
470+
]);
471+
}
456472

457-
return lines.join('\n');
473+
return generateCode(statements);
458474
}
459475

460476
async function generateReactQueryForTarget(

graphql/codegen/src/core/codegen/shared/index.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,19 @@
1313
*/
1414
import type { CleanTable, CleanOperation, TypeRegistry } from '../../../types/schema';
1515
import type { ResolvedConfig } from '../../../types/config';
16+
import * as t from '@babel/types';
17+
import { generateCode, addJSDocComment } from '../babel-ast';
1618
import { generateTypesFile } from '../types';
1719
import { generateSchemaTypesFile } from '../schema-types-generator';
1820
import { getTableNames } from '../utils';
1921

22+
/**
23+
* Helper to create export * from './module' statement
24+
*/
25+
function exportAllFrom(modulePath: string): t.ExportAllDeclaration {
26+
return t.exportAllDeclaration(t.stringLiteral(modulePath));
27+
}
28+
2029
export interface GeneratedFile {
2130
path: string;
2231
content: string;
@@ -92,23 +101,28 @@ export function generateSharedTypes(options: GenerateSharedOptions): GenerateSha
92101
}
93102

94103
/**
95-
* Generate the barrel export for shared types
104+
* Generate the barrel export for shared types using Babel AST
96105
*/
97106
function generateSharedBarrel(hasSchemaTypes: boolean): string {
98-
const lines = [
99-
'/**',
100-
' * Shared types - auto-generated, do not edit',
101-
' */',
102-
'',
103-
"export * from './types';",
104-
];
107+
const statements: t.Statement[] = [];
105108

109+
// Export types
110+
statements.push(exportAllFrom('./types'));
111+
112+
// Export schema types if present
106113
if (hasSchemaTypes) {
107-
lines.push("export * from './schema-types';");
114+
statements.push(exportAllFrom('./schema-types'));
115+
}
116+
117+
// Add file header as leading comment on first statement
118+
if (statements.length > 0) {
119+
addJSDocComment(statements[0], [
120+
'Shared types - auto-generated, do not edit',
121+
'@generated by @constructive-io/graphql-codegen',
122+
]);
108123
}
109124

110-
lines.push('');
111-
return lines.join('\n');
125+
return generateCode(statements);
112126
}
113127

114128
export { generateTypesFile } from '../types';

graphql/codegen/src/core/config/resolver.ts

Lines changed: 37 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -55,77 +55,50 @@ export interface LoadConfigResult {
5555
}
5656

5757
/**
58-
* Extended overrides type that includes database and PGPM module options
59-
*/
60-
export interface ExtendedTargetOverrides extends GraphQLSDKConfigTarget {
61-
database?: string;
62-
schemas?: string[];
63-
apiNames?: string[];
64-
pgpmModulePath?: string;
65-
pgpmWorkspacePath?: string;
66-
pgpmModuleName?: string;
67-
keepDb?: boolean;
68-
}
69-
70-
/**
71-
* Build target overrides from options
58+
* Build target overrides from CLI options
59+
*
60+
* Note: Validation that only one source is specified happens in loadAndResolveConfig,
61+
* so we don't need to clear other source fields here.
7262
*/
7363
export function buildTargetOverrides(
7464
options: ConfigOverrideOptions
75-
): ExtendedTargetOverrides {
76-
const overrides: ExtendedTargetOverrides = {};
65+
): GraphQLSDKConfigTarget {
66+
const overrides: GraphQLSDKConfigTarget = {};
7767

7868
if (options.endpoint) {
7969
overrides.endpoint = options.endpoint;
80-
overrides.schema = undefined;
81-
overrides.database = undefined;
82-
overrides.pgpmModulePath = undefined;
83-
overrides.pgpmWorkspacePath = undefined;
84-
overrides.pgpmModuleName = undefined;
8570
}
8671

8772
if (options.schema) {
8873
overrides.schema = options.schema;
89-
overrides.endpoint = undefined;
90-
overrides.database = undefined;
91-
overrides.pgpmModulePath = undefined;
92-
overrides.pgpmWorkspacePath = undefined;
93-
overrides.pgpmModuleName = undefined;
9474
}
9575

9676
if (options.database) {
9777
overrides.database = options.database;
98-
overrides.schemas = options.schemas;
99-
overrides.apiNames = options.apiNames;
100-
overrides.endpoint = undefined;
101-
overrides.schema = undefined;
102-
overrides.pgpmModulePath = undefined;
103-
overrides.pgpmWorkspacePath = undefined;
104-
overrides.pgpmModuleName = undefined;
10578
}
10679

10780
if (options.pgpmModulePath) {
10881
overrides.pgpmModulePath = options.pgpmModulePath;
109-
overrides.schemas = options.schemas;
110-
overrides.apiNames = options.apiNames;
111-
overrides.keepDb = options.keepDb;
112-
overrides.endpoint = undefined;
113-
overrides.schema = undefined;
114-
overrides.database = undefined;
115-
overrides.pgpmWorkspacePath = undefined;
116-
overrides.pgpmModuleName = undefined;
11782
}
11883

119-
if (options.pgpmWorkspacePath && options.pgpmModuleName) {
84+
if (options.pgpmWorkspacePath) {
12085
overrides.pgpmWorkspacePath = options.pgpmWorkspacePath;
86+
}
87+
88+
if (options.pgpmModuleName) {
12189
overrides.pgpmModuleName = options.pgpmModuleName;
90+
}
91+
92+
if (options.schemas) {
12293
overrides.schemas = options.schemas;
94+
}
95+
96+
if (options.apiNames) {
12397
overrides.apiNames = options.apiNames;
98+
}
99+
100+
if (options.keepDb !== undefined) {
124101
overrides.keepDb = options.keepDb;
125-
overrides.endpoint = undefined;
126-
overrides.schema = undefined;
127-
overrides.database = undefined;
128-
overrides.pgpmModulePath = undefined;
129102
}
130103

131104
if (options.output) {
@@ -208,12 +181,13 @@ function resolveMultiTargetConfig(
208181

209182
if (
210183
!options.target &&
211-
(options.endpoint || options.schema || options.output)
184+
(options.endpoint || options.schema || options.database ||
185+
options.pgpmModulePath || options.pgpmWorkspacePath || options.output)
212186
) {
213187
return {
214188
success: false,
215189
error:
216-
'Multiple targets configured. Use --target with --endpoint, --schema, or --output.',
190+
'Multiple targets configured. Use --target with source or output overrides.',
217191
};
218192
}
219193

@@ -236,10 +210,17 @@ function resolveMultiTargetConfig(
236210
mergedTarget = mergeConfig(mergedTarget, overrides);
237211
}
238212

239-
if (!mergedTarget.endpoint && !mergedTarget.schema) {
213+
const hasSource =
214+
mergedTarget.endpoint ||
215+
mergedTarget.schema ||
216+
mergedTarget.database ||
217+
mergedTarget.pgpmModulePath ||
218+
(mergedTarget.pgpmWorkspacePath && mergedTarget.pgpmModuleName);
219+
220+
if (!hasSource) {
240221
return {
241222
success: false,
242-
error: `Target "${name}" is missing an endpoint or schema.`,
223+
error: `Target "${name}" is missing a source (endpoint, schema, database, or pgpm module).`,
243224
};
244225
}
245226

@@ -262,7 +243,7 @@ function resolveMultiTargetConfig(
262243
function resolveSingleTargetConfig(
263244
baseConfig: GraphQLSDKConfigTarget,
264245
options: ConfigOverrideOptions,
265-
overrides: ExtendedTargetOverrides
246+
overrides: GraphQLSDKConfigTarget
266247
): LoadConfigResult {
267248
if (options.target) {
268249
return {
@@ -278,9 +259,9 @@ function resolveSingleTargetConfig(
278259
const hasSource =
279260
mergedConfig.endpoint ||
280261
mergedConfig.schema ||
281-
overrides.database ||
282-
overrides.pgpmModulePath ||
283-
(overrides.pgpmWorkspacePath && overrides.pgpmModuleName);
262+
mergedConfig.database ||
263+
mergedConfig.pgpmModulePath ||
264+
(mergedConfig.pgpmWorkspacePath && mergedConfig.pgpmModuleName);
284265

285266
if (!hasSource) {
286267
return {
@@ -290,31 +271,10 @@ function resolveSingleTargetConfig(
290271
};
291272
}
292273

293-
// For database mode, we need to pass the database info through to the resolved config
274+
// All source options are now first-class citizens in the config types,
275+
// so resolveConfig handles them properly without any casts
294276
const resolvedConfig = resolveConfig(mergedConfig);
295277

296-
// Attach extended options if present (they're not part of the standard config type)
297-
if (overrides.database) {
298-
(resolvedConfig as any).database = overrides.database;
299-
(resolvedConfig as any).schemas = overrides.schemas;
300-
(resolvedConfig as any).apiNames = overrides.apiNames;
301-
}
302-
303-
if (overrides.pgpmModulePath) {
304-
(resolvedConfig as any).pgpmModulePath = overrides.pgpmModulePath;
305-
(resolvedConfig as any).schemas = overrides.schemas;
306-
(resolvedConfig as any).apiNames = overrides.apiNames;
307-
(resolvedConfig as any).keepDb = overrides.keepDb;
308-
}
309-
310-
if (overrides.pgpmWorkspacePath && overrides.pgpmModuleName) {
311-
(resolvedConfig as any).pgpmWorkspacePath = overrides.pgpmWorkspacePath;
312-
(resolvedConfig as any).pgpmModuleName = overrides.pgpmModuleName;
313-
(resolvedConfig as any).schemas = overrides.schemas;
314-
(resolvedConfig as any).apiNames = overrides.apiNames;
315-
(resolvedConfig as any).keepDb = overrides.keepDb;
316-
}
317-
318278
return {
319279
success: true,
320280
targets: [{ name: 'default', config: resolvedConfig }],

0 commit comments

Comments
 (0)