From e5091e670f91b37a911cd088aa63dba477bc50fa Mon Sep 17 00:00:00 2001 From: ghou9khub Date: Mon, 12 Jan 2026 01:35:56 -0800 Subject: [PATCH 1/3] Resolve bin path --- packages/cli/__tests__/codegen.test.ts | 20 ++++++++++++++++++++ packages/cli/src/commands/codegen.ts | 23 +++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/cli/__tests__/codegen.test.ts b/packages/cli/__tests__/codegen.test.ts index 43addb2d4..cec12592c 100644 --- a/packages/cli/__tests__/codegen.test.ts +++ b/packages/cli/__tests__/codegen.test.ts @@ -64,6 +64,26 @@ describe('codegen command', () => { expect(args).toEqual(expect.arrayContaining(['-o', 'graphql/codegen/dist'])) }) + it('resolves graphql-codegen CLI from package bin by default', async () => { + const child = require('child_process') + delete process.env.CONSTRUCTIVE_CODEGEN_BIN + + const argv: Partial = { + out: 'graphql/codegen/dist' + } + + await codegenCommand(argv, {} as any, {} as any) + + const pkgPath = require.resolve('@constructive-io/graphql-codegen/package.json') + const pkg = require(pkgPath) + const binField = pkg.bin + const rel = typeof binField === 'string' ? binField : (binField['graphql-codegen'] || Object.values(binField)[0]) + const expected = require('path').join(require('path').dirname(pkgPath), rel) + + const args = (child.spawnSync as jest.Mock).mock.calls[0][1] as string[] + expect(args[0]).toEqual(expected) + }) + it('exits with non-zero when underlying CLI fails', async () => { const child = require('child_process'); (child.spawnSync as jest.Mock).mockReturnValueOnce({ status: 1 }) diff --git a/packages/cli/src/commands/codegen.ts b/packages/cli/src/commands/codegen.ts index 1a5682add..5a46f0960 100644 --- a/packages/cli/src/commands/codegen.ts +++ b/packages/cli/src/commands/codegen.ts @@ -1,7 +1,7 @@ import { CLIOptions, Inquirerer } from 'inquirerer' import { ParsedArgs } from 'minimist' import { spawnSync } from 'child_process' -import { join } from 'path' +import { join, dirname } from 'path' const usage = ` Constructive GraphQL Codegen: @@ -37,7 +37,26 @@ export default async ( const verbose = !!(argv.verbose || argv.v) const envBin = process.env.CONSTRUCTIVE_CODEGEN_BIN - const bin = envBin || require.resolve('@constructive-io/graphql-codegen/bin/graphql-codegen.js') + let bin = envBin || '' + if (!bin) { + try { + const pkgPath = require.resolve('@constructive-io/graphql-codegen/package.json') + const pkg = require(pkgPath) + const binField = pkg.bin + let rel: string | undefined + if (typeof binField === 'string') { + rel = binField + } else if (binField && typeof binField === 'object') { + rel = binField['graphql-codegen'] || Object.values(binField)[0] + } + if (rel) bin = join(dirname(pkgPath), rel) + } catch {} + } + if (!bin) { + try { + bin = require.resolve('@constructive-io/graphql-codegen/dist/cli/index.js') + } catch {} + } const args: string[] = ['generate'] if (configPath) args.push('-c', configPath) if (endpoint) args.push('-e', endpoint) From 239fd2d312b0add5e396337f1ab6d1f71430fc48 Mon Sep 17 00:00:00 2001 From: ghou9khub Date: Mon, 12 Jan 2026 05:48:07 -0800 Subject: [PATCH 2/3] Updated cli for codegen --- packages/cli/__tests__/codegen.test.ts | 59 +++++++++----------------- packages/cli/src/commands/codegen.ts | 48 ++++++--------------- 2 files changed, 34 insertions(+), 73 deletions(-) diff --git a/packages/cli/__tests__/codegen.test.ts b/packages/cli/__tests__/codegen.test.ts index cec12592c..5283d2060 100644 --- a/packages/cli/__tests__/codegen.test.ts +++ b/packages/cli/__tests__/codegen.test.ts @@ -1,13 +1,13 @@ import type { ParsedArgs } from 'minimist' import codegenCommand from '../src/commands/codegen' -jest.mock('child_process', () => ({ - spawnSync: jest.fn(() => ({ status: 0 })) +const generateMock = jest.fn(async (_opts?: any) => ({ success: true, message: 'ok' })) +jest.mock('@constructive-io/graphql-codegen/cli/commands/generate', () => ({ + generateCommand: (opts: any) => generateMock(opts) })) describe('codegen command', () => { beforeEach(() => { - process.env.CONSTRUCTIVE_CODEGEN_BIN = '/fake/bin/graphql-codegen.js' jest.clearAllMocks() }) @@ -26,9 +26,7 @@ describe('codegen command', () => { spyExit.mockRestore() }) - it('invokes graphql-codegen CLI with endpoint, out, auth, and flags', async () => { - const child = require('child_process') - + it('passes endpoint, out, auth, and flags to generateCommand', async () => { const argv: Partial = { endpoint: 'http://localhost:3000/graphql', auth: 'Bearer testtoken', @@ -39,19 +37,16 @@ describe('codegen command', () => { await codegenCommand(argv, {} as any, {} as any) - expect(child.spawnSync).toHaveBeenCalled() - const args = (child.spawnSync as jest.Mock).mock.calls[0][1] as string[] - expect(args).toEqual(expect.arrayContaining(['generate'])) - expect(args).toEqual(expect.arrayContaining(['-e', 'http://localhost:3000/graphql'])) - expect(args).toEqual(expect.arrayContaining(['-o', 'graphql/codegen/dist'])) - expect(args).toEqual(expect.arrayContaining(['-a', 'Bearer testtoken'])) - expect(args).toEqual(expect.arrayContaining(['--dry-run'])) - expect(args).toEqual(expect.arrayContaining(['-v'])) + expect(generateMock).toHaveBeenCalled() + const opts = (generateMock as jest.Mock).mock.calls[0][0] + expect(opts.endpoint).toBe('http://localhost:3000/graphql') + expect(opts.output).toBe('graphql/codegen/dist') + expect(opts.authorization).toBe('Bearer testtoken') + expect(opts.verbose).toBe(true) + expect(opts.dryRun).toBe(true) }) - it('passes config path and out directory through to CLI', async () => { - const child = require('child_process') - + it('passes config path and out directory to generateCommand', async () => { const argv: Partial = { config: '/tmp/codegen.json', out: 'graphql/codegen/dist' @@ -59,34 +54,20 @@ describe('codegen command', () => { await codegenCommand(argv, {} as any, {} as any) - const args = (child.spawnSync as jest.Mock).mock.calls[0][1] as string[] - expect(args).toEqual(expect.arrayContaining(['-c', '/tmp/codegen.json'])) - expect(args).toEqual(expect.arrayContaining(['-o', 'graphql/codegen/dist'])) + const opts = (generateMock as jest.Mock).mock.calls[0][0] + expect(opts.config).toBe('/tmp/codegen.json') + expect(opts.output).toBe('graphql/codegen/dist') }) - it('resolves graphql-codegen CLI from package bin by default', async () => { - const child = require('child_process') + it('does not depend on process.env.CONSTRUCTIVE_CODEGEN_BIN', async () => { delete process.env.CONSTRUCTIVE_CODEGEN_BIN - - const argv: Partial = { - out: 'graphql/codegen/dist' - } - + const argv: Partial = { out: 'graphql/codegen/dist' } await codegenCommand(argv, {} as any, {} as any) - - const pkgPath = require.resolve('@constructive-io/graphql-codegen/package.json') - const pkg = require(pkgPath) - const binField = pkg.bin - const rel = typeof binField === 'string' ? binField : (binField['graphql-codegen'] || Object.values(binField)[0]) - const expected = require('path').join(require('path').dirname(pkgPath), rel) - - const args = (child.spawnSync as jest.Mock).mock.calls[0][1] as string[] - expect(args[0]).toEqual(expected) + expect(generateMock).toHaveBeenCalled() }) - it('exits with non-zero when underlying CLI fails', async () => { - const child = require('child_process'); - (child.spawnSync as jest.Mock).mockReturnValueOnce({ status: 1 }) + it('exits with non-zero when generateCommand fails', async () => { + generateMock.mockResolvedValueOnce({ success: false, message: 'fail', errors: ['e1'] } as any) const spyExit = jest.spyOn(process, 'exit').mockImplementation(((code?: number) => { throw new Error('exit:' + code) }) as any) const argv: Partial = { diff --git a/packages/cli/src/commands/codegen.ts b/packages/cli/src/commands/codegen.ts index 5a46f0960..57dca3e18 100644 --- a/packages/cli/src/commands/codegen.ts +++ b/packages/cli/src/commands/codegen.ts @@ -1,7 +1,6 @@ import { CLIOptions, Inquirerer } from 'inquirerer' import { ParsedArgs } from 'minimist' -import { spawnSync } from 'child_process' -import { join, dirname } from 'path' +import { generateCommand } from '@constructive-io/graphql-codegen/cli/commands/generate' const usage = ` Constructive GraphQL Codegen: @@ -28,43 +27,24 @@ export default async ( process.exit(0) } - const cwd = (argv.cwd as string) || process.cwd() const endpoint = (argv.endpoint as string) || '' - const outDir = (argv.out as string) || 'graphql/codegen/dist' + const outDir = (argv.out as string) || 'codegen' const auth = (argv.auth as string) || '' const configPath = (argv.config as string) || '' const dryRun = !!(argv['dry-run'] || argv.dryRun) const verbose = !!(argv.verbose || argv.v) - const envBin = process.env.CONSTRUCTIVE_CODEGEN_BIN - let bin = envBin || '' - if (!bin) { - try { - const pkgPath = require.resolve('@constructive-io/graphql-codegen/package.json') - const pkg = require(pkgPath) - const binField = pkg.bin - let rel: string | undefined - if (typeof binField === 'string') { - rel = binField - } else if (binField && typeof binField === 'object') { - rel = binField['graphql-codegen'] || Object.values(binField)[0] - } - if (rel) bin = join(dirname(pkgPath), rel) - } catch {} + const result = await generateCommand({ + config: configPath, + endpoint, + output: outDir, + authorization: auth, + verbose, + dryRun, + }) + if (!result.success) { + console.error('x', result.message) + process.exit(1) } - if (!bin) { - try { - bin = require.resolve('@constructive-io/graphql-codegen/dist/cli/index.js') - } catch {} - } - const args: string[] = ['generate'] - if (configPath) args.push('-c', configPath) - if (endpoint) args.push('-e', endpoint) - if (outDir) args.push('-o', outDir) - if (auth) args.push('-a', auth) - if (dryRun) args.push('--dry-run') - if (verbose) args.push('-v') - - const res = spawnSync(process.execPath, [bin, ...args], { cwd, stdio: 'inherit' }) - if ((res.status ?? 0) !== 0) process.exit(res.status ?? 1) + console.log('[ok]', result.message) } From 9f491f1403f2d48878be42e8da0c5f336bff6992 Mon Sep 17 00:00:00 2001 From: ghou9khub Date: Mon, 12 Jan 2026 18:51:00 -0800 Subject: [PATCH 3/3] Fix cli test --- packages/cli/__tests__/cli.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/cli/__tests__/cli.test.ts b/packages/cli/__tests__/cli.test.ts index 83fc8ac0b..46e64f65f 100644 --- a/packages/cli/__tests__/cli.test.ts +++ b/packages/cli/__tests__/cli.test.ts @@ -1,3 +1,6 @@ +jest.mock('@constructive-io/graphql-codegen/cli/commands/generate', () => ({ + generateCommand: async () => ({ success: true, message: 'ok' }) +})); import { Inquirerer, Question } from 'inquirerer'; import { KEY_SEQUENCES, setupTests, TestEnvironment } from '../test-utils';