Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: restrict bundling with --output but without inline apis #1799

Merged
merged 4 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fresh-spies-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@redocly/cli": patch
---

Clarified usage of the `--output` option in the `bundle` command.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apis:
main:
root: ./test.yaml
rules:
no-invalid-media-type-examples:
severity: error
allowAdditionalProperties: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`E2E bundle bundle should NOT be invoked IF no positional apis provided AND --output specified 1`] = `

index.ts bundle [apis...]

Bundle a multi-file API description to a single file.

Positionals:
apis [array] [default: []]

Options:
--version Show version number. [boolean]
--help Show help. [boolean]
-o, --output Output file or folder for inline APIs.[string]
--ext Bundle file extension.
[choices: "json", "yaml", "yml"]
--skip-preprocessor Ignore certain preprocessors. [array]
--skip-decorator Ignore certain decorators. [array]
-d, --dereferenced Produce a fully dereferenced bundle. [boolean]
-f, --force Produce bundle output even when errors occur.
[boolean]
--config Path to the config file. [string]
--metafile Produce metadata about the bundle [string]
--remove-unused-components Remove unused components.
[boolean] [default: false]
-k, --keep-url-references Keep absolute url references. [boolean]
--lint-config Severity level for config file linting.
[choices: "warn", "error", "off"] [default: "warn"]

At least one inline API must be specified when using --output.

`;
25 changes: 25 additions & 0 deletions __tests__/bundle/bundle-no-output-without-inline-apis/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
openapi: 3.1.0
paths:
/test-api:
get:
responses:
'200':
description: success
content:
application/json:
schema:
$defs:
main_data:
$anchor: main_data
type: object
properties:
foo:
type: string
type: object
oneOf:
- properties:
wrapper:
$ref: '#main_data'
- $ref: '#main_data'
example:
foo: TEST
8 changes: 8 additions & 0 deletions __tests__/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ describe('E2E', () => {
'bundle-remove-unused-components',
'bundle-remove-unused-components-from-config',
'bundle-arazzo-valid-test-description',
'bundle-no-output-without-inline-apis',
];
const folderPath = join(__dirname, 'bundle');
const contents = readdirSync(folderPath).filter((folder) => !excludeFolders.includes(folder));
Expand Down Expand Up @@ -478,6 +479,13 @@ describe('E2E', () => {
const result = getCommandOutput(args, testPath);
(<any>expect(cleanupOutput(result))).toMatchSpecificSnapshot(join(testPath, 'snapshot.js'));
});

test('bundle should NOT be invoked IF no positional apis provided AND --output specified', () => {
const testPath = join(folderPath, 'bundle-no-output-without-inline-apis');
const args = getParams('../../../packages/cli/src/index.ts', 'bundle', ['--output=dist']);
const result = getCommandOutput(args, testPath);
(<any>expect(cleanupOutput(result))).toMatchSpecificSnapshot(join(testPath, 'snapshot.js'));
});
});

describe('bundle with option: remove-unused-components', () => {
Expand Down
37 changes: 19 additions & 18 deletions docs/commands/bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ 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 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 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 | 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. **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). |
| --version | boolean | Show version number. |
| 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 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 the bundled file's extension. The possible values are `json`, `yaml`, or `yml`. The 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 | Preserve absolute URL references. |
| --lint-config | string | Specify the severity level for the configuration file. <br/> **Possible values:** `warn`, `error`, `off`. The default value is `warn`. |
| --metafile | string | Path for the bundle metadata file. |
| --output, -o | string | Name or folder for the bundle file specified using the command line. 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). |
| --version | boolean | Show version number. |

## Examples

Expand All @@ -65,7 +65,7 @@ dist/openapi.json
dist/museum.json
</pre>

You can specify the default `output` location for a bundled API in the `apis` section of your Redocly configuration file.
Alternatively, you can specify the default `output` location for a bundled API in the `apis` section of your Redocly configuration file.
This is especially useful when bundling multiple APIs.

```yaml
Expand All @@ -85,6 +85,7 @@ redocly bundle
```

Please note, that providing an API to the `bundle` command results in the command bundling only the specified API.
Additionally, the `--output` option is only meaningful when used with APIs specified in the command line.

### Create a fully dereferenced bundle

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/__tests__/commands/bundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ describe('bundle', () => {
expect(process.stdout.write).toHaveBeenCalledTimes(1);
});

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 () => {
it('should NOT store bundled API descriptions in the output files described in the apis section of config IF there is a positional api provided', async () => {
const apis = {
foo: {
root: 'foo.yaml',
Expand Down
170 changes: 89 additions & 81 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,86 +473,94 @@ yargs
'bundle [apis...]',
'Bundle a multi-file API description to a single file.',
(yargs) =>
yargs.positional('apis', { array: true, type: 'string', demandOption: true }).options({
output: {
type: 'string',
description: 'Output file.',
alias: 'o',
},
ext: {
description: 'Bundle file extension.',
requiresArg: true,
choices: outputExtensions,
},
'skip-preprocessor': {
description: 'Ignore certain preprocessors.',
array: true,
type: 'string',
},
'skip-decorator': {
description: 'Ignore certain decorators.',
array: true,
type: 'string',
},
dereferenced: {
alias: 'd',
type: 'boolean',
description: 'Produce a fully dereferenced bundle.',
},
force: {
alias: 'f',
type: 'boolean',
description: 'Produce bundle output even when errors occur.',
},
config: {
description: 'Path to the config file.',
type: 'string',
},
metafile: {
description: 'Produce metadata about the bundle',
type: 'string',
},
extends: {
description: 'Override extends configurations (defaults or config file settings).',
requiresArg: true,
array: true,
type: 'string',
hidden: true,
},
'remove-unused-components': {
description: 'Remove unused components.',
type: 'boolean',
default: false,
},
'keep-url-references': {
description: 'Keep absolute url references.',
type: 'boolean',
alias: 'k',
},
'lint-config': {
description: 'Severity level for config file linting.',
choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
default: 'warn' as RuleSeverity,
},
format: {
hidden: true,
deprecated: true,
},
lint: {
hidden: true,
deprecated: true,
},
'skip-rule': {
hidden: true,
deprecated: true,
array: true,
type: 'string',
},
'max-problems': {
hidden: true,
deprecated: true,
},
}),
yargs
.positional('apis', { array: true, type: 'string', demandOption: true })
.options({
output: {
type: 'string',
description: 'Output file or folder for inline APIs.',
alias: 'o',
},
ext: {
description: 'Bundle file extension.',
requiresArg: true,
choices: outputExtensions,
},
'skip-preprocessor': {
description: 'Ignore certain preprocessors.',
array: true,
type: 'string',
},
'skip-decorator': {
description: 'Ignore certain decorators.',
array: true,
type: 'string',
},
dereferenced: {
alias: 'd',
type: 'boolean',
description: 'Produce a fully dereferenced bundle.',
},
force: {
alias: 'f',
type: 'boolean',
description: 'Produce bundle output even when errors occur.',
},
config: {
description: 'Path to the config file.',
type: 'string',
},
metafile: {
description: 'Produce metadata about the bundle',
type: 'string',
},
extends: {
description: 'Override extends configurations (defaults or config file settings).',
requiresArg: true,
array: true,
type: 'string',
hidden: true,
},
'remove-unused-components': {
description: 'Remove unused components.',
type: 'boolean',
default: false,
},
'keep-url-references': {
description: 'Keep absolute url references.',
type: 'boolean',
alias: 'k',
},
'lint-config': {
description: 'Severity level for config file linting.',
choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
default: 'warn' as RuleSeverity,
},
format: {
hidden: true,
deprecated: true,
},
lint: {
hidden: true,
deprecated: true,
},
'skip-rule': {
hidden: true,
deprecated: true,
array: true,
type: 'string',
},
'max-problems': {
hidden: true,
deprecated: true,
},
})
.check((argv) => {
if (argv.output && (!argv.apis || argv.apis.length === 0)) {
throw new Error('At least one inline API must be specified when using --output.');
}
return true;
}),
(argv) => {
const DEPRECATED_OPTIONS = ['lint', 'format', 'skip-rule', 'max-problems'];
const LINT_AND_BUNDLE_DOCUMENTATION_LINK =
Expand Down Expand Up @@ -778,7 +786,7 @@ yargs
})
.check((argv: any) => {
if (argv.theme && !argv.theme?.openapi)
throw Error('Invalid option: theme.openapi not set');
throw Error('Invalid option: theme.openapi not set.');
return true;
}),
async (argv) => {
Expand Down
Loading