Skip to content

Commit 746e95a

Browse files
authored
Merge pull request #848 from constructive-io/devin/1773817039-schema-config-refactor
refactor: migrate schema output config from flat props to nested `schema` object
2 parents c18ef8e + 5514e7f commit 746e95a

10 files changed

Lines changed: 90 additions & 41 deletions

File tree

graphql/codegen/src/__tests__/codegen/schema-only.test.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const EXAMPLE_SCHEMA = path.resolve(
99
'../../../examples/example.schema.graphql',
1010
);
1111

12-
describe('generate() with schemaOnly', () => {
12+
describe('generate() with schema.enabled', () => {
1313
let tempDir: string;
1414

1515
beforeEach(() => {
@@ -23,8 +23,7 @@ describe('generate() with schemaOnly', () => {
2323
it('writes SDL to file from schemaFile source', async () => {
2424
const result = await generate({
2525
schemaFile: EXAMPLE_SCHEMA,
26-
schemaOnly: true,
27-
schemaOnlyOutput: tempDir,
26+
schema: { enabled: true, output: tempDir },
2827
});
2928

3029
expect(result.success).toBe(true);
@@ -38,12 +37,10 @@ describe('generate() with schemaOnly', () => {
3837
expect(sdl).toContain('type User');
3938
});
4039

41-
it('uses custom filename when schemaOnlyFilename is set', async () => {
40+
it('uses custom filename when schema.filename is set', async () => {
4241
const result = await generate({
4342
schemaFile: EXAMPLE_SCHEMA,
44-
schemaOnly: true,
45-
schemaOnlyOutput: tempDir,
46-
schemaOnlyFilename: 'app.graphql',
43+
schema: { enabled: true, output: tempDir, filename: 'app.graphql' },
4744
});
4845

4946
expect(result.success).toBe(true);
@@ -54,8 +51,7 @@ describe('generate() with schemaOnly', () => {
5451
it('succeeds without any generators enabled', async () => {
5552
const result = await generate({
5653
schemaFile: EXAMPLE_SCHEMA,
57-
schemaOnly: true,
58-
schemaOnlyOutput: tempDir,
54+
schema: { enabled: true, output: tempDir },
5955
});
6056

6157
expect(result.success).toBe(true);
@@ -64,8 +60,7 @@ describe('generate() with schemaOnly', () => {
6460

6561
it('fails when no source is specified', async () => {
6662
const result = await generate({
67-
schemaOnly: true,
68-
schemaOnlyOutput: tempDir,
63+
schema: { enabled: true, output: tempDir },
6964
});
7065

7166
expect(result.success).toBe(false);
@@ -77,8 +72,7 @@ describe('generate() with schemaOnly', () => {
7772

7873
const result = await generate({
7974
schemaFile: EXAMPLE_SCHEMA,
80-
schemaOnly: true,
81-
schemaOnlyOutput: nestedDir,
75+
schema: { enabled: true, output: nestedDir },
8276
});
8377

8478
expect(result.success).toBe(true);

graphql/codegen/src/cli/handler.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ export async function runCodegenHandler(
3131
): Promise<void> {
3232
const args = camelizeArgv(argv as Record<string, any>);
3333

34-
const schemaOnly = Boolean(args.schemaOnly);
34+
const schemaConfig = args.schemaEnabled
35+
? {
36+
enabled: true,
37+
...(args.schemaOutput ? { output: String(args.schemaOutput) } : {}),
38+
...(args.schemaFilename ? { filename: String(args.schemaFilename) } : {}),
39+
}
40+
: undefined;
3541

3642
const hasSourceFlags = Boolean(
3743
args.endpoint || args.schemaFile || args.schemaDir || args.schemas || args.apiNames
@@ -80,7 +86,7 @@ export async function runCodegenHandler(
8086
const { results, hasError } = await generateMulti({
8187
configs: selectedTargets,
8288
cliOverrides: cliOptions as Partial<GraphQLSDKConfigTarget>,
83-
schemaOnly,
89+
schema: schemaConfig,
8490
});
8591

8692
for (const { name, result } of results) {
@@ -107,7 +113,7 @@ export async function runCodegenHandler(
107113
if (expanded) {
108114
const { results, hasError } = await generateMulti({
109115
configs: expanded,
110-
schemaOnly,
116+
schema: schemaConfig,
111117
});
112118
for (const { name, result } of results) {
113119
console.log(`\n[${name}]`);
@@ -117,6 +123,9 @@ export async function runCodegenHandler(
117123
return;
118124
}
119125

120-
const result = await generate({ ...options, schemaOnly });
126+
const result = await generate({
127+
...options,
128+
...(schemaConfig ? { schema: schemaConfig } : {}),
129+
});
121130
printResult(result);
122131
}

graphql/codegen/src/cli/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ Generator Options:
3636
-v, --verbose Show detailed output
3737
3838
Schema Export:
39-
--schema-only Export GraphQL SDL instead of running full codegen.
39+
--schema-enabled Export GraphQL SDL instead of running full codegen.
4040
Works with any source (endpoint, file, database, PGPM).
4141
With multiple apiNames, writes one .graphql per API.
42+
--schema-output <dir> Output directory for the exported schema file
43+
--schema-filename <name> Filename for the exported schema (default: schema.graphql)
4244
4345
-h, --help Show this help message
4446
--version Show version number
@@ -77,12 +79,14 @@ export const options: Partial<CLIOptions> = {
7779
a: 'authorization',
7880
v: 'verbose',
7981
},
80-
boolean: ['schema-only'],
82+
boolean: ['schema-enabled'],
8183
string: [
8284
'config',
8385
'endpoint',
8486
'schema-file',
8587
'schema-dir',
88+
'schema-output',
89+
'schema-filename',
8690
'output',
8791
'target',
8892
'authorization',

graphql/codegen/src/core/generate.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { pgCache } from 'pg-cache';
1414
import { createEphemeralDb, type EphemeralDbResult } from 'pgsql-client';
1515
import { deployPgpm } from 'pgsql-seed';
1616

17-
import type { CliConfig, DbConfig, GraphQLSDKConfigTarget, PgpmConfig } from '../types/config';
17+
import type { CliConfig, DbConfig, GraphQLSDKConfigTarget, PgpmConfig, SchemaConfig } from '../types/config';
1818
import { getConfigOptions } from '../types/config';
1919
import type { CleanOperation, CleanTable, TypeRegistry } from '../types/schema';
2020
import { generate as generateReactQueryFiles } from './codegen';
@@ -64,9 +64,6 @@ export interface GenerateOptions extends GraphQLSDKConfigTarget {
6464
verbose?: boolean;
6565
dryRun?: boolean;
6666
skipCustomOperations?: boolean;
67-
schemaOnly?: boolean;
68-
schemaOnlyOutput?: string;
69-
schemaOnlyFilename?: string;
7067
}
7168

7269
export interface GenerateResult {
@@ -140,7 +137,9 @@ export async function generate(
140137
options.nodeHttpAdapter === true ||
141138
(runCli && options.nodeHttpAdapter !== false);
142139

143-
if (!options.schemaOnly && !runReactQuery && !runOrm && !runCli) {
140+
const schemaEnabled = !!options.schema?.enabled;
141+
142+
if (!schemaEnabled && !runReactQuery && !runOrm && !runCli) {
144143
return {
145144
success: false,
146145
message:
@@ -171,7 +170,7 @@ export async function generate(
171170
headers: config.headers,
172171
});
173172

174-
if (options.schemaOnly) {
173+
if (schemaEnabled && !runReactQuery && !runOrm && !runCli) {
175174
try {
176175
console.log(`Fetching schema from ${source.describe()}...`);
177176
const { introspection } = await source.fetch();
@@ -186,9 +185,9 @@ export async function generate(
186185
};
187186
}
188187

189-
const outDir = path.resolve(options.schemaOnlyOutput || outputRoot || '.');
188+
const outDir = path.resolve(options.schema?.output || outputRoot || '.');
190189
await fs.promises.mkdir(outDir, { recursive: true });
191-
const filename = options.schemaOnlyFilename || 'schema.graphql';
190+
const filename = options.schema?.filename || 'schema.graphql';
192191
const filePath = path.join(outDir, filename);
193192
await fs.promises.writeFile(filePath, sdl, 'utf-8');
194193

@@ -550,7 +549,7 @@ export interface GenerateMultiOptions {
550549
cliOverrides?: Partial<GraphQLSDKConfigTarget>;
551550
verbose?: boolean;
552551
dryRun?: boolean;
553-
schemaOnly?: boolean;
552+
schema?: SchemaConfig;
554553
unifiedCli?: CliConfig | boolean;
555554
}
556555

@@ -669,13 +668,14 @@ function applySharedPgpmDb(
669668
export async function generateMulti(
670669
options: GenerateMultiOptions,
671670
): Promise<GenerateMultiResult> {
672-
const { configs, cliOverrides, verbose, dryRun, schemaOnly, unifiedCli } = options;
671+
const { configs, cliOverrides, verbose, dryRun, schema, unifiedCli } = options;
673672
const names = Object.keys(configs);
674673
const results: Array<{ name: string; result: GenerateResult }> = [];
675674
let hasError = false;
676675

676+
const schemaEnabled = !!schema?.enabled;
677677
const targetInfos: RootRootReadmeTarget[] = [];
678-
const useUnifiedCli = !schemaOnly && !!unifiedCli && names.length > 1;
678+
const useUnifiedCli = !schemaEnabled && !!unifiedCli && names.length > 1;
679679

680680
const cliTargets: MultiTargetCliTarget[] = [];
681681

@@ -693,8 +693,9 @@ export async function generateMulti(
693693
...targetConfig,
694694
verbose,
695695
dryRun,
696-
schemaOnly,
697-
schemaOnlyFilename: schemaOnly ? `${name}.graphql` : undefined,
696+
schema: schemaEnabled
697+
? { ...schema, filename: schema?.filename ?? `${name}.graphql` }
698+
: targetConfig.schema,
698699
},
699700
useUnifiedCli ? { skipCli: true, targetName: name } : { targetName: name },
700701
);

graphql/codegen/src/types/config.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,32 @@ export interface DocsConfig {
173173
skills?: boolean;
174174
}
175175

176+
/**
177+
* Schema export configuration
178+
* Controls SDL schema export behavior.
179+
*/
180+
export interface SchemaConfig {
181+
/**
182+
* Enable schema SDL export
183+
* When true, fetches the schema and writes it as a .graphql SDL file.
184+
* If no generators are enabled (orm, reactQuery, cli), only the schema is exported.
185+
* @default false
186+
*/
187+
enabled?: boolean;
188+
189+
/**
190+
* Output directory for the exported schema file
191+
* @default same as the target's output directory
192+
*/
193+
output?: string;
194+
195+
/**
196+
* Filename for the exported schema file
197+
* @default 'schema.graphql'
198+
*/
199+
filename?: string;
200+
}
201+
176202
/**
177203
* Infrastructure command name overrides for collision handling.
178204
* When a target name collides with a default infra command name,
@@ -390,6 +416,13 @@ export interface GraphQLSDKConfigTarget {
390416
*/
391417
docs?: DocsConfig | boolean;
392418

419+
/**
420+
* Schema export configuration
421+
* When enabled, exports the GraphQL SDL to a file.
422+
* If no generators are also enabled, this acts as a schema-only export.
423+
*/
424+
schema?: SchemaConfig;
425+
393426
/**
394427
* Custom path for generated skill files.
395428
* When set, skills are written to this directory.

graphql/codegen/src/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export type {
4949
} from './selection';
5050

5151
// Config types
52-
export type { GraphQLSDKConfig, GraphQLSDKConfigTarget } from './config';
52+
export type { GraphQLSDKConfig, GraphQLSDKConfigTarget, SchemaConfig } from './config';
5353
export {
5454
DEFAULT_CONFIG,
5555
defineConfig,

packages/cli/AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The CLI provides GraphQL-focused commands:
1919

2020
- `packages/cli/src/commands/server.ts` – start the Constructive GraphQL server
2121
- `packages/cli/src/commands/explorer.ts` – start the Constructive GraphQL explorer
22-
- `packages/cli/src/commands/codegen.ts` – run GraphQL codegen (`@constructive-io/graphql-codegen`), including `--schema-only` for SDL export
22+
- `packages/cli/src/commands/codegen.ts` – run GraphQL codegen (`@constructive-io/graphql-codegen`), including `--schema-enabled` for SDL export
2323

2424
## Debugging Tips
2525

packages/cli/README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,26 +107,32 @@ cnc codegen --api-names my_api --output ./codegen --orm
107107
- `--dry-run` - Preview without writing files
108108
- `--verbose` - Verbose output
109109

110-
### `cnc codegen --schema-only`
110+
### `cnc codegen --schema-enabled`
111111

112112
Export GraphQL schema SDL without running full code generation. Works with any source (endpoint, file, database, PGPM).
113113

114114
```bash
115115
# From database schemas
116-
cnc codegen --schema-only --schemas myapp,public --output ./schemas
116+
cnc codegen --schema-enabled --schemas myapp,public --schema-output ./schemas
117117

118118
# From running server
119-
cnc codegen --schema-only --endpoint http://localhost:3000/graphql --output ./schemas
119+
cnc codegen --schema-enabled --endpoint http://localhost:3000/graphql --schema-output ./schemas
120120

121121
# From schema file (useful for converting/validating)
122-
cnc codegen --schema-only --schema-file ./input.graphql --output ./schemas
122+
cnc codegen --schema-enabled --schema-file ./input.graphql --schema-output ./schemas
123123

124124
# From a directory of .graphql files (multi-target)
125-
cnc codegen --schema-only --schema-dir ./schemas --output ./exported
125+
cnc codegen --schema-enabled --schema-dir ./schemas --schema-output ./exported
126+
127+
# Custom filename
128+
cnc codegen --schema-enabled --endpoint http://localhost:3000/graphql --schema-output ./schemas --schema-filename public.graphql
126129
```
127130

128131
**Options:**
129132

133+
- `--schema-enabled` - Enable schema SDL export
134+
- `--schema-output <dir>` - Output directory for the exported schema file
135+
- `--schema-filename <name>` - Filename for the exported schema (default: schema.graphql)
130136
- `--endpoint <url>` - GraphQL endpoint URL
131137
- `--schema-file <path>` - Path to GraphQL schema file
132138
- `--schemas <list>` - Comma-separated PostgreSQL schemas

packages/cli/src/commands/codegen.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ Generator Options:
2626
--verbose Verbose output
2727
2828
Schema Export:
29-
--schema-only Export GraphQL SDL instead of running full codegen.
29+
--schema-enabled Export GraphQL SDL instead of running full codegen.
3030
Works with any source (endpoint, file, database, PGPM).
3131
With multiple apiNames, writes one .graphql per API.
32+
--schema-output <dir> Output directory for the exported schema file
33+
--schema-filename <name> Filename for the exported schema (default: schema.graphql)
3234
3335
--help, -h Show this help message
3436
`;

packages/cli/src/utils/display.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const usageText = `
3333
cnc server --port 8080 Start server on custom port
3434
cnc explorer Launch GraphiQL explorer
3535
cnc codegen --schema schema.graphql Generate types from schema
36-
cnc codegen --schema-only --out schema.graphql Export schema SDL
36+
cnc codegen --schema-enabled --output ./schemas Export schema SDL
3737
cnc jobs up Start combined server (jobs runtime)
3838
3939
# Execution Engine

0 commit comments

Comments
 (0)