mirror of
https://github.com/LukeHagar/redocly-cli.git
synced 2025-12-06 12:47:48 +00:00
fix: bundling multiple files specified as CLI arguments (#1717)
This commit is contained in:
5
.changeset/late-readers-design.md
Normal file
5
.changeset/late-readers-design.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@redocly/cli": patch
|
||||
---
|
||||
|
||||
Fixed a bug where bundling multiple API description files specified as CLI arguments, along with the `--output` option, stored the result in a single file instead of a folder.
|
||||
@@ -23,18 +23,18 @@ redocly bundle --version
|
||||
## Options
|
||||
|
||||
| Option | Type | Description |
|
||||
| -------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| apis | [string] | List of API description root filenames or names assigned in the `apis` section of your Redocly configuration file. Default values are all names defined in the `apis` section within your configuration file. |
|
||||
| --config | string | Specify path to the [configuration file](#use-alternative-configuration-file). |
|
||||
| -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| apis | [string] | List of API description root filenames or names assigned in the `apis` section of your Redocly configuration file. Default values are names defined in the `apis` section of your configuration file. |
|
||||
| --config | string | Specify the path to the [configuration file](#use-alternative-configuration-file). |
|
||||
| --dereferenced, -d | boolean | Generate fully dereferenced bundle. |
|
||||
| --ext | string | Specify bundled file extension. Possible values are `json`, `yaml`, or `yml`. Default value is `yaml`. |
|
||||
| --extends | [string] | Can be used in combination with `--lint` to [extend a specific configuration](./lint.md#extend-configuration). Default values are taken from the Redocly configuration file. |
|
||||
| --force, -f | boolean | Generate bundle output even when errors occur. |
|
||||
| --ext | string | Specify the bundled file's extension. The possible values are `json`, `yaml`, or `yml`. Default value is `yaml`. |
|
||||
| --extends | [string] | Can be used in combination with `--lint` to [extend a specific configuration](./lint.md#extend-configuration). The default values are taken from the Redocly configuration file. |
|
||||
| --force, -f | boolean | Generate a bundle output even when errors occur. |
|
||||
| --help | boolean | Show help. |
|
||||
| --keep-url-references, -k | boolean | Keep absolute url references. |
|
||||
| --keep-url-references, -k | boolean | Preserve absolute URL references. |
|
||||
| --lint-config | string | Specify the severity level for the configuration file. <br/> **Possible values:** `warn`, `error`, `off`. Default value is `warn`. |
|
||||
| --metafile | string | Path for the bundle metadata file. |
|
||||
| --output, -o | string | Name or folder for the bundle file. If you don't specify the file extension, `.yaml` is used by default. If the specified folder doesn't exist, it's created automatically. **If the file specified as the bundler's output already exists, it's overwritten.** Use this option when bundling a single API only; otherwise use the `output` option in per-API configuration. |
|
||||
| --output, -o | string | Name or folder for the bundle file. If you don't specify the file extension, `.yaml` is used by default. If the specified folder doesn't exist, it's created automatically. **Overwrites existing bundler output file.** |
|
||||
| --remove-unused-components | boolean | Remove unused components from the `bundle` output. |
|
||||
| --skip-decorator | [string] | Ignore certain decorators. See the [Skip preprocessor, rule, or decorator section](#skip-preprocessor-rule-or-decorator). |
|
||||
| --skip-preprocessor | [string] | Ignore certain preprocessors. See the [Skip preprocessor, rule, or decorator section](#skip-preprocessor-rule-or-decorator). |
|
||||
|
||||
@@ -127,6 +127,7 @@ describe('bundle', () => {
|
||||
expect(handleError).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
describe('per api output', () => {
|
||||
it('should store bundled API descriptions in the output files described in the apis section of config IF no positional apis provided AND output is specified for both apis', async () => {
|
||||
const apis = {
|
||||
foo: {
|
||||
@@ -208,7 +209,6 @@ describe('bundle', () => {
|
||||
expect(process.stdout.write).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
describe('per api output', () => {
|
||||
it('should NOT store bundled API descriptions in the output files described in the apis section of config IF no there is a positional api provided', async () => {
|
||||
const apis = {
|
||||
foo: {
|
||||
@@ -240,5 +240,46 @@ describe('bundle', () => {
|
||||
expect(saveBundle).toBeCalledTimes(0);
|
||||
expect(process.stdout.write).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should store bundled API descriptions in the directory specified in argv IF multiple positional apis provided AND --output specified', async () => {
|
||||
const apis = {
|
||||
foo: {
|
||||
root: 'foo.yaml',
|
||||
output: 'output/foo.yaml',
|
||||
},
|
||||
bar: {
|
||||
root: 'bar.yaml',
|
||||
output: 'output/bar.json',
|
||||
},
|
||||
};
|
||||
const config = {
|
||||
apis,
|
||||
styleguide: {
|
||||
skipPreprocessors: jest.fn(),
|
||||
skipDecorators: jest.fn(),
|
||||
},
|
||||
} as unknown as Config;
|
||||
// @ts-ignore
|
||||
getFallbackApisOrExit = jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce(
|
||||
Object.entries(apis).map(([alias, { root, ...api }]) => ({ ...api, path: root, alias }))
|
||||
);
|
||||
(getTotals as jest.Mock).mockReturnValue({
|
||||
errors: 0,
|
||||
warnings: 0,
|
||||
ignored: 0,
|
||||
});
|
||||
|
||||
await handleBundle({
|
||||
argv: { apis: ['foo.yaml', 'bar.yaml'], output: 'dist' }, // cli options
|
||||
version: 'test',
|
||||
config,
|
||||
});
|
||||
|
||||
expect(saveBundle).toBeCalledTimes(2);
|
||||
expect(saveBundle).toHaveBeenNthCalledWith(1, 'dist/foo.yaml', expect.any(String));
|
||||
expect(saveBundle).toHaveBeenNthCalledWith(2, 'dist/bar.yaml', expect.any(String));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -70,7 +70,13 @@ export async function handleBundle({
|
||||
});
|
||||
|
||||
const fileTotals = getTotals(problems);
|
||||
const { outputFile, ext } = getOutputFileName(path, output || argv.output, argv.ext);
|
||||
const { outputFile, ext } = getOutputFileName({
|
||||
entrypoint: path,
|
||||
output,
|
||||
argvOutput: argv.output,
|
||||
ext: argv.ext,
|
||||
entries: argv?.apis?.length || 0,
|
||||
});
|
||||
|
||||
if (fileTotals.errors === 0 || argv.force) {
|
||||
if (!outputFile) {
|
||||
|
||||
@@ -363,20 +363,40 @@ export function printConfigLintTotals(totals: Totals, command?: string | number)
|
||||
}
|
||||
}
|
||||
|
||||
export function getOutputFileName(entrypoint: string, output?: string, ext?: BundleOutputFormat) {
|
||||
let outputFile = output;
|
||||
export function getOutputFileName({
|
||||
entrypoint,
|
||||
output,
|
||||
argvOutput,
|
||||
ext,
|
||||
entries,
|
||||
}: {
|
||||
entrypoint: string;
|
||||
output?: string;
|
||||
argvOutput?: string;
|
||||
ext?: BundleOutputFormat;
|
||||
entries: number;
|
||||
}) {
|
||||
let outputFile = output || argvOutput;
|
||||
if (!outputFile) {
|
||||
return { ext: ext || 'yaml' };
|
||||
}
|
||||
|
||||
if (outputFile) {
|
||||
ext = ext || (extname(outputFile).substring(1) as BundleOutputFormat);
|
||||
}
|
||||
if (entries > 1 && argvOutput) {
|
||||
ext = ext || (extname(entrypoint).substring(1) as BundleOutputFormat);
|
||||
if (!outputExtensions.includes(ext)) {
|
||||
throw new Error(`Invalid file extension: ${ext}.`);
|
||||
}
|
||||
outputFile = join(argvOutput, basename(entrypoint, extname(entrypoint))) + '.' + ext;
|
||||
} else {
|
||||
ext =
|
||||
ext ||
|
||||
(extname(outputFile).substring(1) as BundleOutputFormat) ||
|
||||
(extname(entrypoint).substring(1) as BundleOutputFormat);
|
||||
if (!outputExtensions.includes(ext)) {
|
||||
throw new Error(`Invalid file extension: ${ext}.`);
|
||||
}
|
||||
outputFile = join(dirname(outputFile), basename(outputFile, extname(outputFile))) + '.' + ext;
|
||||
}
|
||||
return { outputFile, ext };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user