diff --git a/.changeset/long-donkeys-bathe.md b/.changeset/long-donkeys-bathe.md new file mode 100644 index 000000000..ec40a158d --- /dev/null +++ b/.changeset/long-donkeys-bathe.md @@ -0,0 +1,5 @@ +--- +"@redocly/cli": patch +--- + +Fixed an issue where `join` would throw an error when a glob pattern was provided. diff --git a/packages/cli/src/__tests__/commands/join.test.ts b/packages/cli/src/__tests__/commands/join.test.ts index 8980be4cc..6d42a389b 100644 --- a/packages/cli/src/__tests__/commands/join.test.ts +++ b/packages/cli/src/__tests__/commands/join.test.ts @@ -1,7 +1,11 @@ import { yellow } from 'colorette'; import { detectSpec } from '@redocly/openapi-core'; import { handleJoin } from '../../commands/join'; -import { exitWithError, writeToFileByExtension } from '../../utils/miscellaneous'; +import { + exitWithError, + getFallbackApisOrExit, + writeToFileByExtension, +} from '../../utils/miscellaneous'; import { loadConfig } from '../../__mocks__/@redocly/openapi-core'; import { ConfigFixture } from '../fixtures/config'; @@ -15,7 +19,35 @@ describe('handleJoin', () => { it('should call exitWithError because only one entrypoint', async () => { await handleJoin({ argv: { apis: ['first.yaml'] }, config: {} as any, version: 'cli-version' }); - expect(exitWithError).toHaveBeenCalledWith(`At least 2 apis should be provided.`); + expect(exitWithError).toHaveBeenCalledWith(`At least 2 APIs should be provided.`); + }); + + it('should call exitWithError if glob expands to less than 2 APIs', async () => { + (getFallbackApisOrExit as jest.Mock).mockResolvedValueOnce([{ path: 'first.yaml' }]); + + await handleJoin({ + argv: { apis: ['*.yaml'] }, + config: {} as any, + version: 'cli-version', + }); + + expect(exitWithError).toHaveBeenCalledWith(`At least 2 APIs should be provided.`); + }); + + it('should proceed if glob expands to 2 or more APIs', async () => { + (detectSpec as jest.Mock).mockReturnValue('oas3_1'); + (getFallbackApisOrExit as jest.Mock).mockResolvedValueOnce([ + { path: 'first.yaml' }, + { path: 'second.yaml' }, + ]); + + await handleJoin({ + argv: { apis: ['*.yaml'] }, + config: ConfigFixture as any, + version: 'cli-version', + }); + + expect(exitWithError).not.toHaveBeenCalled(); }); it('should call exitWithError because passed all 3 options for tags', async () => { @@ -52,6 +84,7 @@ describe('handleJoin', () => { }); it('should call exitWithError because Only OpenAPI 3.0 and OpenAPI 3.1 are supported', async () => { + (detectSpec as jest.Mock).mockReturnValueOnce('oas2_0'); await handleJoin({ argv: { apis: ['first.yaml', 'second.yaml'], diff --git a/packages/cli/src/commands/join.ts b/packages/cli/src/commands/join.ts index 07d866e48..8671e0a35 100644 --- a/packages/cli/src/commands/join.ts +++ b/packages/cli/src/commands/join.ts @@ -64,18 +64,12 @@ export async function handleJoin({ }: CommandArgs) { const startedAt = performance.now(); - if (argv.apis.length < 2) { - return exitWithError(`At least 2 apis should be provided.`); - } - - const fileExtension = getAndValidateFileExtension(argv.output || argv.apis[0]); - const { 'prefix-components-with-info-prop': prefixComponentsWithInfoProp, 'prefix-tags-with-filename': prefixTagsWithFilename, 'prefix-tags-with-info-prop': prefixTagsWithInfoProp, 'without-x-tag-groups': withoutXTagGroups, - output: specFilename = `openapi.${fileExtension}`, + output, } = argv; const usedTagsOptions = [ @@ -91,6 +85,13 @@ export async function handleJoin({ } const apis = await getFallbackApisOrExit(argv.apis, config); + if (apis.length < 2) { + return exitWithError(`At least 2 APIs should be provided.`); + } + + const fileExtension = getAndValidateFileExtension(output || apis[0].path); + const specFilename = output || `openapi.${fileExtension}`; + const externalRefResolver = new BaseResolver(config.resolve); const documents = await Promise.all( apis.map(