diff --git a/.changeset/short-panthers-smoke.md b/.changeset/short-panthers-smoke.md new file mode 100644 index 000000000..d35f4b549 --- /dev/null +++ b/.changeset/short-panthers-smoke.md @@ -0,0 +1,5 @@ +--- +"@redocly/cli": minor +--- + +Added the `eject` and `translate` commands for use with the new Reunite-hosted product family. diff --git a/docs/commands/preview.md b/docs/commands/preview.md index cde7570fd..f2cb95239 100644 --- a/docs/commands/preview.md +++ b/docs/commands/preview.md @@ -14,19 +14,19 @@ This command is for our pre-release products, currently open for early access to redocly preview redocly preview --product=revel redocly preview --product=reef --plan=pro -redocly preview --product=reef --plan=pro --source-dir=./my-docs-project --port=4001 +redocly preview --product=reef --plan=pro --project-dir=./my-docs-project --port=4001 ``` ## Options -| Option | Type | Description | -| ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| --help | boolean | Show help. | -| --plan | string | Product plan to use in preview.
**Possible values:** `pro`, `enterprise`. The default value is `enterprise`. For more details, see [plans](https://redocly.com/pricing/). | -| --product | string | Name of a product to preview the project with.
**Possible values:** `redoc`, `revel`, `reef`, `realm`, `redoc-revel`, `redoc-reef`, `revel-reef`.
`redoc` is the flagship product for generating API documentation from OpenAPI specifications.
`revel` is a specialized product designed for external API applications.
`reef` is a specialized product designed for internal API needs.
`realm` is a balanced product combining `redoc`, `revel`, and `reef`.
`redoc-revel` is a blended product combining `redoc` and `revel`.
`redoc-reef` is a blended product combining `redoc` and `reef`.
`revel-reef` is a blended product combining `revel` and `reef`.
The default value is autodetected from the project's `package.json` or `realm` is used. | -| --source-dir, -d | string | Path to the project directory. The default value is `.` (current directory). | -| --port | number | The port to run the preview server on. The default value is `4000`. | -| --version | boolean | Show version number. | +| Option | Type | Description | +| ----------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| --help | boolean | Show help. | +| --plan | string | Product plan to use in preview.
**Possible values:** `pro`, `enterprise`. The default value is `enterprise`. For more details, see [plans](https://redocly.com/pricing/). | +| --product | string | Name of a product to preview the project with.
**Possible values:** `redoc`, `revel`, `reef`, `realm`, `redoc-revel`, `redoc-reef`, `revel-reef`.
`redoc` is the flagship product for generating API documentation from OpenAPI specifications.
`revel` is a specialized product designed for external API applications.
`reef` is a specialized product designed for internal API needs.
`realm` is a balanced product combining `redoc`, `revel`, and `reef`.
`redoc-revel` is a blended product combining `redoc` and `revel`.
`redoc-reef` is a blended product combining `redoc` and `reef`.
`revel-reef` is a blended product combining `revel` and `reef`.
The default value is autodetected from the project's `package.json` or `realm` is used. | +| --project-dir, -d | string | Path to the project directory. The default value is `.` (current directory). | +| --port, -p | number | The port to run the preview server on. The default value is `4000`. | +| --version | boolean | Show version number. | ## Examples @@ -50,10 +50,10 @@ redocly preview --plan=pro ### Specify project directory -By default, the preview command uses the current directory. To specify another directory, provide a path relative to the current directory using the `--source-dir` option: +By default, the preview command uses the current directory. To specify another directory, provide a path relative to the current directory using the `--project-dir` option: ```bash -redocly preview --source-dir=./path/to/my/docs/ +redocly preview --project-dir=./path/to/my/docs/ ``` ### Use custom port for preview diff --git a/packages/cli/src/cms/commands/push.ts b/packages/cli/src/cms/commands/push.ts index a3b43ee60..c723ff917 100644 --- a/packages/cli/src/cms/commands/push.ts +++ b/packages/cli/src/cms/commands/push.ts @@ -13,6 +13,7 @@ import { ReuniteApiClient, getDomain, getApiKeys } from '../api'; import type { OutputFormat } from '@redocly/openapi-core'; import type { CommandArgs } from '../../wrapper'; +import type { VerifyConfigOptions } from '../../types'; export type PushOptions = { apis?: string[]; @@ -33,13 +34,12 @@ export type PushOptions = { 'default-branch': string; domain?: string; - config?: string; 'wait-for-deployment'?: boolean; 'max-execution-time': number; 'continue-on-deploy-failures'?: boolean; verbose?: boolean; format?: Extract; -}; +} & VerifyConfigOptions; type FileToUpload = { name: string; path: string }; diff --git a/packages/cli/src/commands/build-docs/types.ts b/packages/cli/src/commands/build-docs/types.ts index fce09113b..fb180c67f 100644 --- a/packages/cli/src/commands/build-docs/types.ts +++ b/packages/cli/src/commands/build-docs/types.ts @@ -1,3 +1,5 @@ +import type { VerifyConfigOptions } from '../../types'; + export type BuildDocsOptions = { watch?: boolean; output?: string; @@ -20,5 +22,4 @@ export type BuildDocsArgv = { theme: { openapi: string | Record; }; - config?: string; -}; +} & VerifyConfigOptions; diff --git a/packages/cli/src/commands/bundle.ts b/packages/cli/src/commands/bundle.ts index ee32d7289..0c2c8e8c6 100644 --- a/packages/cli/src/commands/bundle.ts +++ b/packages/cli/src/commands/bundle.ts @@ -14,13 +14,12 @@ import { checkForDeprecatedOptions, } from '../utils/miscellaneous'; -import type { OutputExtensions, Skips, Totals } from '../types'; +import type { OutputExtensions, Skips, Totals, VerifyConfigOptions } from '../types'; import type { CommandArgs } from '../wrapper'; export type BundleOptions = { apis?: string[]; extends?: string[]; - config?: string; output?: string; ext: OutputExtensions; dereferenced?: boolean; @@ -28,7 +27,8 @@ export type BundleOptions = { metafile?: string; 'remove-unused-components'?: boolean; 'keep-url-references'?: boolean; -} & Skips; +} & Skips & + VerifyConfigOptions; export async function handleBundle({ argv, diff --git a/packages/cli/src/commands/eject.ts b/packages/cli/src/commands/eject.ts new file mode 100644 index 000000000..b02d4b879 --- /dev/null +++ b/packages/cli/src/commands/eject.ts @@ -0,0 +1,29 @@ +import { spawn } from 'child_process'; + +import type { CommandArgs } from '../wrapper'; +import type { VerifyConfigOptions } from '../types'; + +export type EjectOptions = { + type: 'component'; + path: string; + 'project-dir'?: string; + force: boolean; +} & VerifyConfigOptions; + +export const handleEject = async ({ argv }: CommandArgs) => { + process.stdout.write(`\nLaunching eject using NPX.\n\n`); + const npxExecutableName = process.platform === 'win32' ? 'npx.cmd' : 'npx'; + spawn( + npxExecutableName, + [ + '-y', + '@redocly/realm', + 'eject', + `${argv.type}`, + `${argv.path}`, + `-d=${argv['project-dir']}`, + argv.force ? `--force=${argv.force}` : '', + ], + { stdio: 'inherit' } + ); +}; diff --git a/packages/cli/src/commands/join.ts b/packages/cli/src/commands/join.ts index 043091933..07d866e48 100644 --- a/packages/cli/src/commands/join.ts +++ b/packages/cli/src/commands/join.ts @@ -23,13 +23,7 @@ import { isObject, isString, keysOf } from '../utils/js-utils'; import { COMPONENTS, OPENAPI3_METHOD } from './split/types'; import { crawl, startsWithComponents } from './split'; -import type { - Oas3Definition, - Document, - Oas3Tag, - Referenced, - RuleSeverity, -} from '@redocly/openapi-core'; +import type { Oas3Definition, Document, Oas3Tag, Referenced } from '@redocly/openapi-core'; import type { BundleResult } from '@redocly/openapi-core/lib/bundle'; import type { Oas3Parameter, @@ -38,6 +32,7 @@ import type { Oas3_1Definition, } from '@redocly/openapi-core/lib/typings/openapi'; import type { CommandArgs } from '../wrapper'; +import type { VerifyConfigOptions } from '../types'; const Tags = 'tags'; const xTagGroups = 'x-tagGroups'; @@ -60,9 +55,7 @@ export type JoinOptions = { 'prefix-components-with-info-prop'?: string; 'without-x-tag-groups'?: boolean; output?: string; - config?: string; - 'lint-config'?: RuleSeverity; -}; +} & VerifyConfigOptions; export async function handleJoin({ argv, diff --git a/packages/cli/src/commands/lint.ts b/packages/cli/src/commands/lint.ts index 8030b9a01..8f4a9bfc7 100644 --- a/packages/cli/src/commands/lint.ts +++ b/packages/cli/src/commands/lint.ts @@ -23,20 +23,19 @@ import { import { getCommandNameFromArgs } from '../utils/getCommandNameFromArgs'; import type { Arguments } from 'yargs'; -import type { OutputFormat, ProblemSeverity, RuleSeverity } from '@redocly/openapi-core'; +import type { OutputFormat, ProblemSeverity } from '@redocly/openapi-core'; import type { RawConfigProcessor } from '@redocly/openapi-core/lib/config'; -import type { CommandOptions, Skips, Totals } from '../types'; +import type { CommandOptions, Skips, Totals, VerifyConfigOptions } from '../types'; import type { CommandArgs } from '../wrapper'; export type LintOptions = { apis?: string[]; 'max-problems': number; extends?: string[]; - config?: string; format: OutputFormat; 'generate-ignore-file'?: boolean; - 'lint-config'?: RuleSeverity; -} & Omit; +} & Omit & + VerifyConfigOptions; export async function handleLint({ argv, diff --git a/packages/cli/src/commands/preview-docs/index.ts b/packages/cli/src/commands/preview-docs/index.ts index c075dd88e..7b0ec99e4 100644 --- a/packages/cli/src/commands/preview-docs/index.ts +++ b/packages/cli/src/commands/preview-docs/index.ts @@ -8,7 +8,7 @@ import { } from '../../utils/miscellaneous'; import startPreviewServer from './preview-server/preview-server'; -import type { Skips } from '../../types'; +import type { Skips, VerifyConfigOptions } from '../../types'; import type { CommandArgs } from '../../wrapper'; export type PreviewDocsOptions = { @@ -18,7 +18,8 @@ export type PreviewDocsOptions = { config?: string; api?: string; force?: boolean; -} & Omit; +} & Omit & + VerifyConfigOptions; export async function previewDocs({ argv, diff --git a/packages/cli/src/commands/preview-project/index.ts b/packages/cli/src/commands/preview-project/index.ts index cc32bf639..cedc6e501 100644 --- a/packages/cli/src/commands/preview-project/index.ts +++ b/packages/cli/src/commands/preview-project/index.ts @@ -8,19 +8,19 @@ import type { CommandArgs } from '../../wrapper'; export const previewProject = async ({ argv }: CommandArgs) => { const { plan, port } = argv; - const projectDir = argv['source-dir']; + const projectDir = argv['project-dir']; const product = argv.product || tryGetProductFromPackageJson(projectDir); if (!isValidProduct(product)) { - process.stderr.write(`Invalid product ${product}`); - throw new Error(`Project preview launch failed`); + process.stderr.write(`Invalid product ${product}.`); + throw new Error(`Project preview launch failed.`); } const productName = PRODUCT_NAMES[product]; const packageName = PRODUCT_PACKAGES[product]; - process.stdout.write(`\nLaunching preview of ${productName} ${plan} using NPX\n\n`); + process.stdout.write(`\nLaunching preview of ${productName} ${plan} using NPX.\n\n`); const npxExecutableName = process.platform === 'win32' ? 'npx.cmd' : 'npx'; diff --git a/packages/cli/src/commands/preview-project/types.ts b/packages/cli/src/commands/preview-project/types.ts index b561b0d39..34fdc9759 100644 --- a/packages/cli/src/commands/preview-project/types.ts +++ b/packages/cli/src/commands/preview-project/types.ts @@ -1,3 +1,4 @@ +import type { VerifyConfigOptions } from '../../types'; import type { PRODUCT_PACKAGES, PRODUCT_PLANS } from './constants'; export type Product = keyof typeof PRODUCT_PACKAGES; @@ -7,6 +8,5 @@ export type PreviewProjectOptions = { product?: Product | string; plan: ProductPlan | string; port?: number; - 'source-dir': string; - config?: string; -}; + 'project-dir': string; +} & VerifyConfigOptions; diff --git a/packages/cli/src/commands/push.ts b/packages/cli/src/commands/push.ts index ea98a257f..6b7a79425 100644 --- a/packages/cli/src/commands/push.ts +++ b/packages/cli/src/commands/push.ts @@ -25,6 +25,7 @@ import { handlePush as handleCMSPush } from '../cms/commands/push'; import type { Config, BundleOutputFormat, Region } from '@redocly/openapi-core'; import type { CommandArgs } from '../wrapper'; +import type { VerifyConfigOptions } from '../types'; const DEFAULT_VERSION = 'latest'; @@ -44,8 +45,7 @@ export type PushOptions = { public?: boolean; files?: string[]; organization?: string; - config?: string; -}; +} & VerifyConfigOptions; export function commonPushHandler({ project, diff --git a/packages/cli/src/commands/split/index.ts b/packages/cli/src/commands/split/index.ts index 49ea5749b..0c5881610 100644 --- a/packages/cli/src/commands/split/index.ts +++ b/packages/cli/src/commands/split/index.ts @@ -37,13 +37,13 @@ import type { Referenced, } from './types'; import type { CommandArgs } from '../../wrapper'; +import type { VerifyConfigOptions } from '../../types'; export type SplitOptions = { api: string; outDir: string; separator: string; - config?: string; -}; +} & VerifyConfigOptions; export async function handleSplit({ argv, collectSpecData }: CommandArgs) { const startedAt = performance.now(); diff --git a/packages/cli/src/commands/stats.ts b/packages/cli/src/commands/stats.ts index 1ade7b74a..c31249464 100755 --- a/packages/cli/src/commands/stats.ts +++ b/packages/cli/src/commands/stats.ts @@ -13,7 +13,6 @@ import { } from '@redocly/openapi-core'; import { getFallbackApisOrExit, printExecutionTime } from '../utils/miscellaneous'; -import type { CommandArgs } from '../wrapper'; import type { StatsAccumulator, StatsName, @@ -21,6 +20,8 @@ import type { OutputFormat, StyleguideConfig, } from '@redocly/openapi-core'; +import type { CommandArgs } from '../wrapper'; +import type { VerifyConfigOptions } from '../types'; const statsAccumulator: StatsAccumulator = { refs: { metric: '🚗 References', total: 0, color: 'red', items: new Set() }, @@ -89,8 +90,7 @@ function printStats( export type StatsOptions = { api?: string; format: OutputFormat; - config?: string; -}; +} & VerifyConfigOptions; export async function handleStats({ argv, config, collectSpecData }: CommandArgs) { const [{ path }] = await getFallbackApisOrExit(argv.api ? [argv.api] : [], config); diff --git a/packages/cli/src/commands/translations.ts b/packages/cli/src/commands/translations.ts new file mode 100644 index 000000000..1c7ca8b8b --- /dev/null +++ b/packages/cli/src/commands/translations.ts @@ -0,0 +1,19 @@ +import { spawn } from 'child_process'; + +import type { CommandArgs } from '../wrapper'; +import type { VerifyConfigOptions } from '../types'; + +export type TranslationsOptions = { + locale: string; + 'project-dir'?: string; +} & VerifyConfigOptions; + +export const handleTranslations = async ({ argv }: CommandArgs) => { + process.stdout.write(`\nLaunching translate using NPX.\n\n`); + const npxExecutableName = process.platform === 'win32' ? 'npx.cmd' : 'npx'; + spawn( + npxExecutableName, + ['-y', '@redocly/realm', 'translate', argv.locale, `-d=${argv['project-dir']}`], + { stdio: 'inherit' } + ); +}; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 0a95b428c..fa5a88270 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -21,6 +21,8 @@ import { } from './utils/update-version-notifier'; import { commandWrapper } from './wrapper'; import { previewProject } from './commands/preview-project'; +import { handleTranslations } from './commands/translations'; +import { handleEject } from './commands/eject'; import { PRODUCT_PLANS } from './commands/preview-project/constants'; import { commonPushHandler } from './commands/push'; @@ -29,6 +31,7 @@ import type { OutputFormat, RuleSeverity } from '@redocly/openapi-core'; import type { BuildDocsArgv } from './commands/build-docs/types'; import type { PushStatusOptions } from './cms/commands/push-status'; import type { PushArguments } from './types'; +import type { EjectOptions } from './commands/eject'; if (!('replaceAll' in String.prototype)) { require('core-js/actual/string/replace-all'); @@ -228,6 +231,11 @@ yargs type: 'boolean', default: false, }, + 'lint-config': { + description: 'Severity level for config file linting.', + choices: ['warn', 'error', 'off'] as ReadonlyArray, + default: 'warn' as RuleSeverity, + }, }), (argv) => { process.env.REDOCLY_CLI_COMMAND = 'push-status'; @@ -642,18 +650,32 @@ yargs default: 'enterprise', }, port: { + alias: 'p', type: 'number', description: 'Preview port.', default: 4000, }, - 'source-dir': { - alias: 'd', + 'project-dir': { + alias: ['d', 'source-dir'], type: 'string', - description: 'Project directory.', + description: + 'Specifies the project content directory. The default value is the directory where the command is executed.', default: '.', }, + 'lint-config': { + description: 'Severity level for config file linting.', + choices: ['warn', 'error', 'off'] as ReadonlyArray, + default: 'warn' as RuleSeverity, + }, }), (argv) => { + if (process.argv.some((arg) => arg.startsWith('--source-dir'))) { + process.stderr.write( + colors.red( + 'Option --source-dir is deprecated and will be removed soon. Use --project-dir instead.\n' + ) + ); + } commandWrapper(previewProject)(argv); } ) @@ -764,6 +786,77 @@ yargs commandWrapper(handlerBuildCommand)(argv as Arguments); } ) + .command( + 'translate ', + 'Creates or updates translations.yaml files and fills them with missing built-in translations and translations from the redocly.yaml and sidebars.yaml files.', + (yargs) => + yargs + .positional('locale', { + description: + 'Locale code to generate translations for, or `all` for all current project locales.', + type: 'string', + demandOption: true, + }) + .options({ + 'project-dir': { + alias: 'd', + type: 'string', + description: + 'Specifies the project content directory. The default value is the directory where the command is executed.', + default: '.', + }, + 'lint-config': { + description: 'Severity level for config file linting.', + choices: ['warn', 'error', 'off'] as ReadonlyArray, + default: 'warn' as RuleSeverity, + }, + }), + (argv) => { + process.env.REDOCLY_CLI_COMMAND = 'translate'; + commandWrapper(handleTranslations)(argv); + } + ) + .command( + 'eject ', + 'Helper function to eject project elements for customization.', + (yargs) => + yargs + .positional('type', { + description: + 'Specifies what type of project element to eject. Currently, it could be only `component`.', + demandOption: true, + choices: ['component'], + }) + .positional('path', { + description: 'Filepath to a component or filepath with glob pattern.', + type: 'string', + demandOption: true, + }) + .options({ + 'project-dir': { + alias: 'd', + type: 'string', + description: + 'Specifies the project content directory. The default value is the directory where the command is executed.', + default: '.', + }, + force: { + alias: 'f', + type: 'boolean', + description: + 'Skips the "overwrite existing" confirmation when ejecting a component that is already ejected in the destination.', + }, + 'lint-config': { + description: 'Severity level for config file linting.', + choices: ['warn', 'error', 'off'] as ReadonlyArray, + default: 'warn' as RuleSeverity, + }, + }), + (argv) => { + process.env.REDOCLY_CLI_COMMAND = 'eject'; + commandWrapper(handleEject)(argv as Arguments); + } + ) .completion('completion', 'Generate autocomplete script for `redocly` command.') .demandCommand(1) .middleware([notifyUpdateCliVersion]) diff --git a/packages/cli/src/types.ts b/packages/cli/src/types.ts index bce718cc7..45313b7fd 100644 --- a/packages/cli/src/types.ts +++ b/packages/cli/src/types.ts @@ -1,4 +1,4 @@ -import type { BundleOutputFormat, Region, Config } from '@redocly/openapi-core'; +import type { BundleOutputFormat, Region, Config, RuleSeverity } from '@redocly/openapi-core'; import type { ArgumentsCamelCase } from 'yargs'; import type { LintOptions } from './commands/lint'; import type { BundleOptions } from './commands/bundle'; @@ -12,6 +12,8 @@ import type { BuildDocsArgv } from './commands/build-docs/types'; import type { PushOptions as CMSPushOptions } from './cms/commands/push'; import type { PushStatusOptions } from './cms/commands/push-status'; import type { PreviewProjectOptions } from './commands/preview-project/types'; +import type { TranslationsOptions } from './commands/translations'; +import type { EjectOptions } from './commands/eject'; export type Totals = { errors: number; @@ -37,12 +39,13 @@ export type CommandOptions = | PreviewDocsOptions | BuildDocsArgv | PushStatusOptions - | VerifyConfigOptions - | PreviewProjectOptions; + | PreviewProjectOptions + | TranslationsOptions + | EjectOptions; export type VerifyConfigOptions = { config?: string; - 'lint-config'?: 'warning' | 'error' | 'off'; + 'lint-config'?: RuleSeverity; }; export type Skips = {