Skip to content
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
7 changes: 7 additions & 0 deletions .changeset/silent-facts-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@redocly/cli": patch
---

Fixed the `split` command to properly handle root-level paths.
Previously, the root path `/` was converted to an empty string as a filename, leading to incorrect file structure and broken links.
Now, it correctly maps to the specified path separator.
18 changes: 9 additions & 9 deletions docs/@v2/commands/split.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ redocly split --version

## Options

| Option | Type | Description |
| ------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| api | string | **REQUIRED.** Path to the API description file that you want to split into a multi-file structure. |
| --config | string | Specify path to the [configuration file](../configuration/index.md). |
| --help | boolean | Show help. |
| --lint-config | string | Specify the severity level for the configuration file. <br/> **Possible values:** `warn`, `error`, `off`. Default value is `warn`. |
| --outDir | string | **REQUIRED.** Path to the directory where you want to save the split files. If the specified directory doesn't exist, it is created automatically. |
| --separator | string | File path separator used while splitting. The default value is `_`. This controls the file names generated in the `paths` folder (e.g. `/users/create` path becomes `user_create.yaml`). |
| --version | boolean | Show version number. |
| Option | Type | Description |
| ------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| api | string | **REQUIRED.** Path to the API description file that you want to split into a multi-file structure. |
| --config | string | Specify path to the [configuration file](../configuration/index.md). |
| --help | boolean | Show help. |
| --lint-config | string | Specify the severity level for the configuration file. <br/> **Possible values:** `warn`, `error`, `off`. Default value is `warn`. |
| --outDir | string | **REQUIRED.** Path to the directory where you want to save the split files. If the specified directory doesn't exist, it is created automatically. |
| --separator | string | File path separator used while splitting. The default value is `_`. This controls the file names generated in the `paths` folder (e.g. `/users/create` path becomes `user_create.yaml`, root level path `/` becomes `_.yaml`, and so on). |
| --version | boolean | Show version number. |

## Examples

Expand Down
36 changes: 0 additions & 36 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions packages/cli/src/utils/miscellaneous.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,15 @@ export function printExecutionTime(commandName: string, startedAt: number, api:
}

export function pathToFilename(path: string, pathSeparator: string) {
if (path === '/') {
return pathSeparator;
}

return path
.replace(/~1/g, '/')
.replace(/~0/g, '~')
.replaceAll('~1', '/')
.replaceAll('~0', '~')
.replace(/^\//, '')
.replace(/\//g, pathSeparator);
.replaceAll('/', pathSeparator);
Comment on lines +129 to +132
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No behaviour changes here, simply improving readability.

}

export function escapeLanguageName(lang: string) {
Expand Down
39 changes: 39 additions & 0 deletions tests/e2e/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,45 @@ describe('E2E', () => {
const result = getCommandOutput(args, { testPath });
await expect(cleanupOutput(result)).toMatchFileSnapshot(join(testPath, 'snapshot.txt'));
});

test('root endpoint correctly split', async () => {
const testPath = join(__dirname, `split/root-endpoint`);

const args = getParams(indexEntryPoint, ['split', 'openapi.yaml', '--outDir=output']);
const result = getCommandOutput(args, { testPath });
await expect(cleanupOutput(result)).toMatchFileSnapshot(join(testPath, 'snapshot.txt'));
});

test('root endpoint split and bundle again to the content', async () => {
const testPath = join(__dirname, `split/root-endpoint`);

let args = getParams(indexEntryPoint, ['split', 'openapi.yaml', '--outDir=output']);
spawnSync('node', args, {
cwd: testPath,
env: {
...process.env,
NODE_ENV: 'production',
NO_COLOR: 'TRUE',
},
});

args = getParams(indexEntryPoint, [
'bundle',
'output/openapi.yaml',
'-o=output/bundled.yaml',
]);
getCommandOutput(args, { testPath });

const expected = readFileSync(join(testPath, 'openapi.yaml'), 'utf8');
const actual = readFileSync(join(testPath, 'output/bundled.yaml'), 'utf8');

// Clean up output folder after splitting so the produced files do not interfere with other tests
spawnSync('rm', ['-rf', 'output'], {
cwd: testPath,
});

expect(actual).toEqual(expected);
});
});

describe('join', () => {
Expand Down
111 changes: 111 additions & 0 deletions tests/e2e/split/root-endpoint/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
openapi: 3.0.0
info:
version: 1.0.0
title: Test
license:
name: MIT
servers:
- url: http://localhost/v1
paths:
/:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/Pets'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: '#/components/schemas/Pet'
114 changes: 114 additions & 0 deletions tests/e2e/split/root-endpoint/snapshot.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@

type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
type: array
items:
$ref: ./Pet.yaml
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/Pets'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
openapi: 3.0.0
info:
version: 1.0.0
title: Test
license:
name: MIT
servers:
- url: http://localhost/v1
paths:
/:
$ref: paths/_.yaml
/pets/{petId}:
$ref: paths/pets_{petId}.yaml
🪓 Document: openapi.yaml is successfully split
and all related files are saved to the directory: output

openapi.yaml: split processed in <test>ms

Loading