diff --git a/.changeset/eight-fireants-provide.md b/.changeset/eight-fireants-provide.md new file mode 100644 index 0000000000..a7bae94cbe --- /dev/null +++ b/.changeset/eight-fireants-provide.md @@ -0,0 +1,5 @@ +--- +"@redocly/cli": minor +--- + +Removed additional operations from the `join` command; use `lint` and/or `bundle` for operations such as `lint` and `decorate`. diff --git a/__tests__/commands.test.ts b/__tests__/commands.test.ts index e7d5b60a45..2747027849 100644 --- a/__tests__/commands.test.ts +++ b/__tests__/commands.test.ts @@ -291,7 +291,6 @@ describe('E2E', () => { { name: 'prefix-tags-with-filename', value: true }, { name: 'without-x-tag-groups', value: true }, { name: 'prefix-components-with-info-prop', value: 'title' }, - { name: 'decorate', value: true }, ]; test.each(options)('test with option: %s', (option) => { diff --git a/__tests__/join/decorate/bar.yaml b/__tests__/join/decorate/bar.yaml deleted file mode 100644 index 0dd3ac3673..0000000000 --- a/__tests__/join/decorate/bar.yaml +++ /dev/null @@ -1,23 +0,0 @@ -openapi: 3.0.0 -info: - title: Bar Example API - description: This is an example API. - version: 1.0.0 -servers: - - url: https://redocly-example.com/api -paths: - /users/{userId}: - parameters: - - name: userId - in: path - description: ID of the user - required: true - schema: - type: integer - get: - summary: Get user by ID - responses: - '200': - description: OK - '404': - description: Not found diff --git a/__tests__/join/decorate/foo.yaml b/__tests__/join/decorate/foo.yaml deleted file mode 100644 index cc19d4ecca..0000000000 --- a/__tests__/join/decorate/foo.yaml +++ /dev/null @@ -1,30 +0,0 @@ -openapi: 3.0.0 -info: - title: Foo Example API - description: This is an example API. - version: 1.0.0 -servers: - - url: https://redocly-example.com/api -paths: - /users/{userId}/orders/{orderId}: - parameters: - - name: userId - in: path - description: ID of the user - required: true - schema: - type: integer - - name: orderId - in: path - description: ID of the order - required: true - schema: - type: integer - get: - x-private: true - summary: Get an order by ID for a specific user - responses: - '200': - description: OK - '404': - description: Not found diff --git a/__tests__/join/decorate/redocly.yaml b/__tests__/join/decorate/redocly.yaml deleted file mode 100644 index 8b75159e1a..0000000000 --- a/__tests__/join/decorate/redocly.yaml +++ /dev/null @@ -1,3 +0,0 @@ -decorators: - remove-x-internal: - internalFlagProperty: 'x-private' diff --git a/__tests__/join/decorate/snapshot.js b/__tests__/join/decorate/snapshot.js deleted file mode 100644 index 80f09e8bba..0000000000 --- a/__tests__/join/decorate/snapshot.js +++ /dev/null @@ -1,56 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`E2E join with options test with option: { name: 'decorate', value: true } 1`] = ` - -openapi: 3.0.0 -info: - title: Foo Example API - description: This is an example API. - version: 1.0.0 -servers: - - url: https://redocly-example.com/api -tags: - - name: bar_other - x-displayName: other -paths: - /users/{userId}/orders/{orderId}: - parameters: - - name: userId - in: path - description: ID of the user - required: true - schema: - type: integer - - name: orderId - in: path - description: ID of the order - required: true - schema: - type: integer - /users/{userId}: - parameters: - - name: userId - in: path - description: ID of the user - required: true - schema: - type: integer - get: - summary: Get user by ID - responses: - '200': - description: OK - '404': - description: Not found - tags: - - bar_other -components: {} -x-tagGroups: - - name: Bar Example API - tags: - - bar_other - -openapi.yaml: join processed in ms - - -`; diff --git a/docs/commands/join.md b/docs/commands/join.md index e19cc7d624..c432012fc8 100644 --- a/docs/commands/join.md +++ b/docs/commands/join.md @@ -18,12 +18,16 @@ The `join` command accepts both YAML and JSON files, which you can mix in the re Apart from providing individual API description files as the input, you can also specify the path to a folder that contains multiple API description files and match them with a wildcard (for example, `myproject/openapi/*.(yaml/json)`). The `join` command collects all matching files and combines them into one file. +{% admonition type="info" name="Use join with other commands" %} +We recommend running [`lint`](./lint.md) before joining API descriptions to ensure that they are valid and meet the expected standards. +You may also want to use [decorators](./../decorators.md) to add any filtering or transformation needed for your API descriptions, either before or after bundling. +{% /admonition %} + ### Usage ```bash redocly join ... redocly join ... -o -redocly join / [--lint] redocly join [--help] [--prefix-components-with-info-prop] [--prefix-tags-with-info-prop] [--prefix-tags-with-filename] redocly join first-api.yaml second-api.yaml @@ -35,24 +39,16 @@ redocly join --version ## Options -{% admonition type="warning" name="Important" %} -The `--lint` option is deprecated and is marked for removal in future releases. -Use the [lint command](./lint.md) separately to lint your APIs before joining. -{% /admonition %} - | Option | Type | Description | | ---------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | apis | [string] | **REQUIRED.** 1. Array of paths to API description files that you want to join. At least two input files are required.
2. A wildcard pattern to match API description files within a specific folder. | | --config | string | Specify path to the [config file](../configuration/index.md). | -| --decorate | boolean | Run decorators. | | --help | boolean | Show help. | -| --lint (**Deprecated**) | boolean | Lint API description files. | | --lint-config | string | Specify the severity level for the configuration file.
**Possible values:** `warn`, `error`, `off`. Default value is `warn`. | | --output, -o | string | Name for the joined output file. Defaults to `openapi.yaml` or `openapi.json` (Depends on the extension of the first input file). **If the file already exists, it's overwritten.** | | --prefix-components-with-info-prop | string | Prefix components with property value from info object. See the [prefix-components-with-info-prop section](#prefix-components-with-info-prop) below. | | --prefix-tags-with-filename | string | Prefix tags with property value from file name. See the [prefix-tags-with-filename section](#prefix-tags-with-filename) below. | | --prefix-tags-with-info-prop | boolean | Prefix tags with property value from info object. See the [prefix-tags-with-info-prop](#prefix-tags-with-info-prop) section. | -| --preprocess | boolean | Run preprocessors. | | --version | boolean | Show version number. | | --without-x-tag-groups | boolean | Skip automated `x-tagGroups` creation. See the [without-x-tag-groups](#without-x-tag-groups) section. | diff --git a/packages/cli/src/__tests__/commands/join.test.ts b/packages/cli/src/__tests__/commands/join.test.ts index 23f5c56e2a..81a0970771 100644 --- a/packages/cli/src/__tests__/commands/join.test.ts +++ b/packages/cli/src/__tests__/commands/join.test.ts @@ -165,23 +165,6 @@ describe('handleJoin', () => { expect(config.styleguide.skipPreprocessors).toHaveBeenCalled(); }); - it('should not call skipDecorators and skipPreprocessors', async () => { - (detectSpec as jest.Mock).mockReturnValue('oas3_0'); - await handleJoin( - { - apis: ['first.yaml', 'second.yaml'], - decorate: true, - preprocess: true, - }, - ConfigFixture as any, - 'cli-version' - ); - - const config = loadConfig(); - expect(config.styleguide.skipDecorators).not.toHaveBeenCalled(); - expect(config.styleguide.skipPreprocessors).not.toHaveBeenCalled(); - }); - it('should handle join with prefix-components-with-info-prop and null values', async () => { (detectSpec as jest.Mock).mockReturnValue('oas3_0'); diff --git a/packages/cli/src/commands/join.ts b/packages/cli/src/commands/join.ts index a0a4bd3ec3..8123c674d0 100644 --- a/packages/cli/src/commands/join.ts +++ b/packages/cli/src/commands/join.ts @@ -6,10 +6,8 @@ import { Config, SpecVersion, BaseResolver, - StyleguideConfig, formatProblems, getTotals, - lintDocument, detectSpec, bundleDocument, isRef, @@ -17,13 +15,10 @@ import { import { getFallbackApisOrExit, printExecutionTime, - handleError, - printLintTotals, exitWithError, sortTopLevelKeysForOas, getAndValidateFileExtension, writeToFileByExtension, - checkForDeprecatedOptions, } from '../utils/miscellaneous'; import { isObject, isString, keysOf } from '../utils/js-utils'; import { COMPONENTS, OPENAPI3_METHOD } from './split/types'; @@ -60,9 +55,6 @@ type JoinDocumentContext = { export type JoinOptions = { apis: string[]; - lint?: boolean; - decorate?: boolean; - preprocess?: boolean; 'prefix-tags-with-info-prop'?: string; 'prefix-tags-with-filename'?: boolean; 'prefix-components-with-info-prop'?: string; @@ -79,8 +71,6 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi return exitWithError(`At least 2 apis should be provided. \n\n`); } - checkForDeprecatedOptions(argv, ['lint'] as Array); - const fileExtension = getAndValidateFileExtension(argv.output || argv.apis[0]); const { @@ -111,23 +101,19 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi ) ); - if (!argv.decorate) { - const decorators = new Set([ - ...Object.keys(config.styleguide.decorators.oas3_0), - ...Object.keys(config.styleguide.decorators.oas3_1), - ...Object.keys(config.styleguide.decorators.oas2), - ]); - config.styleguide.skipDecorators(Array.from(decorators)); - } + const decorators = new Set([ + ...Object.keys(config.styleguide.decorators.oas3_0), + ...Object.keys(config.styleguide.decorators.oas3_1), + ...Object.keys(config.styleguide.decorators.oas2), + ]); + config.styleguide.skipDecorators(Array.from(decorators)); - if (!argv.preprocess) { - const preprocessors = new Set([ - ...Object.keys(config.styleguide.preprocessors.oas3_0), - ...Object.keys(config.styleguide.preprocessors.oas3_1), - ...Object.keys(config.styleguide.preprocessors.oas2), - ]); - config.styleguide.skipPreprocessors(Array.from(preprocessors)); - } + const preprocessors = new Set([ + ...Object.keys(config.styleguide.preprocessors.oas3_0), + ...Object.keys(config.styleguide.preprocessors.oas3_1), + ...Object.keys(config.styleguide.preprocessors.oas2), + ]); + config.styleguide.skipPreprocessors(Array.from(preprocessors)); const bundleResults = await Promise.all( documents.map((document) => @@ -146,7 +132,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi if (fileTotals.errors) { formatProblems(problems, { totals: fileTotals, - version: document.parsed.version, + version: packageVersion, }); exitWithError( `❌ Errors encountered while bundling ${blue( @@ -179,12 +165,6 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi } } - if (argv.lint) { - for (const document of documents) { - await validateApi(document, config.styleguide, externalRefResolver, packageVersion); - } - } - const joinedDef: any = {}; const potentialConflicts = { tags: {}, @@ -787,22 +767,6 @@ function getInfoPrefix(info: any, prefixArg: string | undefined, type: string) { return info[prefixArg].replaceAll(/\s/g, '_'); } -async function validateApi( - document: Document, - config: StyleguideConfig, - externalRefResolver: BaseResolver, - packageVersion: string -) { - try { - const results = await lintDocument({ document, config, externalRefResolver }); - const fileTotals = getTotals(results); - formatProblems(results, { format: 'stylish', totals: fileTotals, version: packageVersion }); - printLintTotals(fileTotals, 2); - } catch (err) { - handleError(err, document.parsed); - } -} - function replace$Refs(obj: unknown, componentsPrefix: string) { crawl(obj, (node: Record) => { if (node.$ref && typeof node.$ref === 'string' && startsWithComponents(node.$ref)) { diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 0ee034ad39..e690a5e466 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -105,9 +105,6 @@ yargs demandOption: true, }) .option({ - lint: { description: 'Lint descriptions', type: 'boolean', default: false, hidden: true }, - decorate: { description: 'Run decorators', type: 'boolean', default: false }, - preprocess: { description: 'Run preprocessors', type: 'boolean', default: false }, 'prefix-tags-with-info-prop': { description: 'Prefix tags with property value from info object.', requiresArg: true, @@ -142,8 +139,47 @@ yargs choices: ['warn', 'error', 'off'] as ReadonlyArray, default: 'warn' as RuleSeverity, }, + lint: { + hidden: true, + deprecated: true, + }, + decorate: { + hidden: true, + deprecated: true, + }, + preprocess: { + hidden: true, + deprecated: true, + }, }), (argv) => { + const DEPRECATED_OPTIONS = ['lint', 'preprocess', 'decorate']; + const DECORATORS_DOCUMENTATION_LINK = 'https://redocly.com/docs/cli/decorators/#decorators'; + const JOIN_COMMAND_DOCUMENTATION_LINK = 'https://redocly.com/docs/cli/commands/join/#join'; + + DEPRECATED_OPTIONS.forEach((option) => { + if (argv[option]) { + process.stdout.write( + `${colors.red( + `Option --${option} is no longer supported. Please review join command documentation ${JOIN_COMMAND_DOCUMENTATION_LINK}.` + )}` + ); + process.stdout.write('\n\n'); + + if (['preprocess', 'decorate'].includes(option)) { + process.stdout.write( + `${colors.red( + `If you are looking for decorators, please review the decorators documentation ${DECORATORS_DOCUMENTATION_LINK}.` + )}` + ); + process.stdout.write('\n\n'); + } + + yargs.showHelp(); + process.exit(1); + } + }); + process.env.REDOCLY_CLI_COMMAND = 'join'; commandWrapper(handleJoin)(argv); }