From 5514e7f4c2c69174ea6d6603d56c861f3b39089e Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Wed, 18 Mar 2026 07:02:36 +0000 Subject: [PATCH] refactor: migrate schema output config from flat props to nested schema object Replaces schemaOnly/schemaOnlyOutput/schemaOnlyFilename with: schema: { enabled?: boolean, output?: string, filename?: string } This aligns schema export config with the existing nested object paradigm used by tables, queries, mutations, codegen, hooks, docs, etc. CLI flags updated: --schema-enabled, --schema-output, --schema-filename --- .../src/__tests__/codegen/schema-only.test.ts | 20 ++++------- graphql/codegen/src/cli/handler.ts | 17 +++++++--- graphql/codegen/src/cli/index.ts | 8 +++-- graphql/codegen/src/core/generate.ts | 27 +++++++-------- graphql/codegen/src/types/config.ts | 33 +++++++++++++++++++ graphql/codegen/src/types/index.ts | 2 +- packages/cli/AGENTS.md | 2 +- packages/cli/README.md | 16 ++++++--- packages/cli/src/commands/codegen.ts | 4 ++- packages/cli/src/utils/display.ts | 2 +- 10 files changed, 90 insertions(+), 41 deletions(-) diff --git a/graphql/codegen/src/__tests__/codegen/schema-only.test.ts b/graphql/codegen/src/__tests__/codegen/schema-only.test.ts index 7761e9a85..87cd7191f 100644 --- a/graphql/codegen/src/__tests__/codegen/schema-only.test.ts +++ b/graphql/codegen/src/__tests__/codegen/schema-only.test.ts @@ -9,7 +9,7 @@ const EXAMPLE_SCHEMA = path.resolve( '../../../examples/example.schema.graphql', ); -describe('generate() with schemaOnly', () => { +describe('generate() with schema.enabled', () => { let tempDir: string; beforeEach(() => { @@ -23,8 +23,7 @@ describe('generate() with schemaOnly', () => { it('writes SDL to file from schemaFile source', async () => { const result = await generate({ schemaFile: EXAMPLE_SCHEMA, - schemaOnly: true, - schemaOnlyOutput: tempDir, + schema: { enabled: true, output: tempDir }, }); expect(result.success).toBe(true); @@ -38,12 +37,10 @@ describe('generate() with schemaOnly', () => { expect(sdl).toContain('type User'); }); - it('uses custom filename when schemaOnlyFilename is set', async () => { + it('uses custom filename when schema.filename is set', async () => { const result = await generate({ schemaFile: EXAMPLE_SCHEMA, - schemaOnly: true, - schemaOnlyOutput: tempDir, - schemaOnlyFilename: 'app.graphql', + schema: { enabled: true, output: tempDir, filename: 'app.graphql' }, }); expect(result.success).toBe(true); @@ -54,8 +51,7 @@ describe('generate() with schemaOnly', () => { it('succeeds without any generators enabled', async () => { const result = await generate({ schemaFile: EXAMPLE_SCHEMA, - schemaOnly: true, - schemaOnlyOutput: tempDir, + schema: { enabled: true, output: tempDir }, }); expect(result.success).toBe(true); @@ -64,8 +60,7 @@ describe('generate() with schemaOnly', () => { it('fails when no source is specified', async () => { const result = await generate({ - schemaOnly: true, - schemaOnlyOutput: tempDir, + schema: { enabled: true, output: tempDir }, }); expect(result.success).toBe(false); @@ -77,8 +72,7 @@ describe('generate() with schemaOnly', () => { const result = await generate({ schemaFile: EXAMPLE_SCHEMA, - schemaOnly: true, - schemaOnlyOutput: nestedDir, + schema: { enabled: true, output: nestedDir }, }); expect(result.success).toBe(true); diff --git a/graphql/codegen/src/cli/handler.ts b/graphql/codegen/src/cli/handler.ts index 110e5c259..3a8d3573f 100644 --- a/graphql/codegen/src/cli/handler.ts +++ b/graphql/codegen/src/cli/handler.ts @@ -31,7 +31,13 @@ export async function runCodegenHandler( ): Promise { const args = camelizeArgv(argv as Record); - const schemaOnly = Boolean(args.schemaOnly); + const schemaConfig = args.schemaEnabled + ? { + enabled: true, + ...(args.schemaOutput ? { output: String(args.schemaOutput) } : {}), + ...(args.schemaFilename ? { filename: String(args.schemaFilename) } : {}), + } + : undefined; const hasSourceFlags = Boolean( args.endpoint || args.schemaFile || args.schemaDir || args.schemas || args.apiNames @@ -80,7 +86,7 @@ export async function runCodegenHandler( const { results, hasError } = await generateMulti({ configs: selectedTargets, cliOverrides: cliOptions as Partial, - schemaOnly, + schema: schemaConfig, }); for (const { name, result } of results) { @@ -107,7 +113,7 @@ export async function runCodegenHandler( if (expanded) { const { results, hasError } = await generateMulti({ configs: expanded, - schemaOnly, + schema: schemaConfig, }); for (const { name, result } of results) { console.log(`\n[${name}]`); @@ -117,6 +123,9 @@ export async function runCodegenHandler( return; } - const result = await generate({ ...options, schemaOnly }); + const result = await generate({ + ...options, + ...(schemaConfig ? { schema: schemaConfig } : {}), + }); printResult(result); } diff --git a/graphql/codegen/src/cli/index.ts b/graphql/codegen/src/cli/index.ts index 6f448a6ed..c0889131f 100644 --- a/graphql/codegen/src/cli/index.ts +++ b/graphql/codegen/src/cli/index.ts @@ -36,9 +36,11 @@ Generator Options: -v, --verbose Show detailed output Schema Export: - --schema-only Export GraphQL SDL instead of running full codegen. + --schema-enabled Export GraphQL SDL instead of running full codegen. Works with any source (endpoint, file, database, PGPM). With multiple apiNames, writes one .graphql per API. + --schema-output Output directory for the exported schema file + --schema-filename Filename for the exported schema (default: schema.graphql) -h, --help Show this help message --version Show version number @@ -77,12 +79,14 @@ export const options: Partial = { a: 'authorization', v: 'verbose', }, - boolean: ['schema-only'], + boolean: ['schema-enabled'], string: [ 'config', 'endpoint', 'schema-file', 'schema-dir', + 'schema-output', + 'schema-filename', 'output', 'target', 'authorization', diff --git a/graphql/codegen/src/core/generate.ts b/graphql/codegen/src/core/generate.ts index dd08f0a63..bdd4f9fdb 100644 --- a/graphql/codegen/src/core/generate.ts +++ b/graphql/codegen/src/core/generate.ts @@ -14,7 +14,7 @@ import { pgCache } from 'pg-cache'; import { createEphemeralDb, type EphemeralDbResult } from 'pgsql-client'; import { deployPgpm } from 'pgsql-seed'; -import type { CliConfig, DbConfig, GraphQLSDKConfigTarget, PgpmConfig } from '../types/config'; +import type { CliConfig, DbConfig, GraphQLSDKConfigTarget, PgpmConfig, SchemaConfig } from '../types/config'; import { getConfigOptions } from '../types/config'; import type { CleanOperation, CleanTable, TypeRegistry } from '../types/schema'; import { generate as generateReactQueryFiles } from './codegen'; @@ -64,9 +64,6 @@ export interface GenerateOptions extends GraphQLSDKConfigTarget { verbose?: boolean; dryRun?: boolean; skipCustomOperations?: boolean; - schemaOnly?: boolean; - schemaOnlyOutput?: string; - schemaOnlyFilename?: string; } export interface GenerateResult { @@ -140,7 +137,9 @@ export async function generate( options.nodeHttpAdapter === true || (runCli && options.nodeHttpAdapter !== false); - if (!options.schemaOnly && !runReactQuery && !runOrm && !runCli) { + const schemaEnabled = !!options.schema?.enabled; + + if (!schemaEnabled && !runReactQuery && !runOrm && !runCli) { return { success: false, message: @@ -171,7 +170,7 @@ export async function generate( headers: config.headers, }); - if (options.schemaOnly) { + if (schemaEnabled && !runReactQuery && !runOrm && !runCli) { try { console.log(`Fetching schema from ${source.describe()}...`); const { introspection } = await source.fetch(); @@ -186,9 +185,9 @@ export async function generate( }; } - const outDir = path.resolve(options.schemaOnlyOutput || outputRoot || '.'); + const outDir = path.resolve(options.schema?.output || outputRoot || '.'); await fs.promises.mkdir(outDir, { recursive: true }); - const filename = options.schemaOnlyFilename || 'schema.graphql'; + const filename = options.schema?.filename || 'schema.graphql'; const filePath = path.join(outDir, filename); await fs.promises.writeFile(filePath, sdl, 'utf-8'); @@ -550,7 +549,7 @@ export interface GenerateMultiOptions { cliOverrides?: Partial; verbose?: boolean; dryRun?: boolean; - schemaOnly?: boolean; + schema?: SchemaConfig; unifiedCli?: CliConfig | boolean; } @@ -669,13 +668,14 @@ function applySharedPgpmDb( export async function generateMulti( options: GenerateMultiOptions, ): Promise { - const { configs, cliOverrides, verbose, dryRun, schemaOnly, unifiedCli } = options; + const { configs, cliOverrides, verbose, dryRun, schema, unifiedCli } = options; const names = Object.keys(configs); const results: Array<{ name: string; result: GenerateResult }> = []; let hasError = false; + const schemaEnabled = !!schema?.enabled; const targetInfos: RootRootReadmeTarget[] = []; - const useUnifiedCli = !schemaOnly && !!unifiedCli && names.length > 1; + const useUnifiedCli = !schemaEnabled && !!unifiedCli && names.length > 1; const cliTargets: MultiTargetCliTarget[] = []; @@ -693,8 +693,9 @@ export async function generateMulti( ...targetConfig, verbose, dryRun, - schemaOnly, - schemaOnlyFilename: schemaOnly ? `${name}.graphql` : undefined, + schema: schemaEnabled + ? { ...schema, filename: schema?.filename ?? `${name}.graphql` } + : targetConfig.schema, }, useUnifiedCli ? { skipCli: true, targetName: name } : { targetName: name }, ); diff --git a/graphql/codegen/src/types/config.ts b/graphql/codegen/src/types/config.ts index aec8e2df6..4d1bdf018 100644 --- a/graphql/codegen/src/types/config.ts +++ b/graphql/codegen/src/types/config.ts @@ -173,6 +173,32 @@ export interface DocsConfig { skills?: boolean; } +/** + * Schema export configuration + * Controls SDL schema export behavior. + */ +export interface SchemaConfig { + /** + * Enable schema SDL export + * When true, fetches the schema and writes it as a .graphql SDL file. + * If no generators are enabled (orm, reactQuery, cli), only the schema is exported. + * @default false + */ + enabled?: boolean; + + /** + * Output directory for the exported schema file + * @default same as the target's output directory + */ + output?: string; + + /** + * Filename for the exported schema file + * @default 'schema.graphql' + */ + filename?: string; +} + /** * Infrastructure command name overrides for collision handling. * When a target name collides with a default infra command name, @@ -390,6 +416,13 @@ export interface GraphQLSDKConfigTarget { */ docs?: DocsConfig | boolean; + /** + * Schema export configuration + * When enabled, exports the GraphQL SDL to a file. + * If no generators are also enabled, this acts as a schema-only export. + */ + schema?: SchemaConfig; + /** * Custom path for generated skill files. * When set, skills are written to this directory. diff --git a/graphql/codegen/src/types/index.ts b/graphql/codegen/src/types/index.ts index 35dbbbfc6..6d9fd42f4 100644 --- a/graphql/codegen/src/types/index.ts +++ b/graphql/codegen/src/types/index.ts @@ -49,7 +49,7 @@ export type { } from './selection'; // Config types -export type { GraphQLSDKConfig, GraphQLSDKConfigTarget } from './config'; +export type { GraphQLSDKConfig, GraphQLSDKConfigTarget, SchemaConfig } from './config'; export { DEFAULT_CONFIG, defineConfig, diff --git a/packages/cli/AGENTS.md b/packages/cli/AGENTS.md index 23d63e7b8..842f29f83 100644 --- a/packages/cli/AGENTS.md +++ b/packages/cli/AGENTS.md @@ -19,7 +19,7 @@ The CLI provides GraphQL-focused commands: - `packages/cli/src/commands/server.ts` – start the Constructive GraphQL server - `packages/cli/src/commands/explorer.ts` – start the Constructive GraphQL explorer -- `packages/cli/src/commands/codegen.ts` – run GraphQL codegen (`@constructive-io/graphql-codegen`), including `--schema-only` for SDL export +- `packages/cli/src/commands/codegen.ts` – run GraphQL codegen (`@constructive-io/graphql-codegen`), including `--schema-enabled` for SDL export ## Debugging Tips diff --git a/packages/cli/README.md b/packages/cli/README.md index f6533e489..7835f7275 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -107,26 +107,32 @@ cnc codegen --api-names my_api --output ./codegen --orm - `--dry-run` - Preview without writing files - `--verbose` - Verbose output -### `cnc codegen --schema-only` +### `cnc codegen --schema-enabled` Export GraphQL schema SDL without running full code generation. Works with any source (endpoint, file, database, PGPM). ```bash # From database schemas -cnc codegen --schema-only --schemas myapp,public --output ./schemas +cnc codegen --schema-enabled --schemas myapp,public --schema-output ./schemas # From running server -cnc codegen --schema-only --endpoint http://localhost:3000/graphql --output ./schemas +cnc codegen --schema-enabled --endpoint http://localhost:3000/graphql --schema-output ./schemas # From schema file (useful for converting/validating) -cnc codegen --schema-only --schema-file ./input.graphql --output ./schemas +cnc codegen --schema-enabled --schema-file ./input.graphql --schema-output ./schemas # From a directory of .graphql files (multi-target) -cnc codegen --schema-only --schema-dir ./schemas --output ./exported +cnc codegen --schema-enabled --schema-dir ./schemas --schema-output ./exported + +# Custom filename +cnc codegen --schema-enabled --endpoint http://localhost:3000/graphql --schema-output ./schemas --schema-filename public.graphql ``` **Options:** +- `--schema-enabled` - Enable schema SDL export +- `--schema-output ` - Output directory for the exported schema file +- `--schema-filename ` - Filename for the exported schema (default: schema.graphql) - `--endpoint ` - GraphQL endpoint URL - `--schema-file ` - Path to GraphQL schema file - `--schemas ` - Comma-separated PostgreSQL schemas diff --git a/packages/cli/src/commands/codegen.ts b/packages/cli/src/commands/codegen.ts index a96649050..062656f64 100644 --- a/packages/cli/src/commands/codegen.ts +++ b/packages/cli/src/commands/codegen.ts @@ -26,9 +26,11 @@ Generator Options: --verbose Verbose output Schema Export: - --schema-only Export GraphQL SDL instead of running full codegen. + --schema-enabled Export GraphQL SDL instead of running full codegen. Works with any source (endpoint, file, database, PGPM). With multiple apiNames, writes one .graphql per API. + --schema-output Output directory for the exported schema file + --schema-filename Filename for the exported schema (default: schema.graphql) --help, -h Show this help message `; diff --git a/packages/cli/src/utils/display.ts b/packages/cli/src/utils/display.ts index e84b4f988..83e5f05ee 100644 --- a/packages/cli/src/utils/display.ts +++ b/packages/cli/src/utils/display.ts @@ -33,7 +33,7 @@ export const usageText = ` cnc server --port 8080 Start server on custom port cnc explorer Launch GraphiQL explorer cnc codegen --schema schema.graphql Generate types from schema - cnc codegen --schema-only --out schema.graphql Export schema SDL + cnc codegen --schema-enabled --output ./schemas Export schema SDL cnc jobs up Start combined server (jobs runtime) # Execution Engine