mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-03 20:00:20 +03:00
Merge tag 'v2.0.0-rc.68' into sections-at-the-end
This commit is contained in:
commit
eb08635e56
2
.github/CODEOWNERS
vendored
Normal file
2
.github/CODEOWNERS
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
* @Redocly/keyboard-warriors
|
||||
/docs/ @Redocly/technical-writers
|
34
.github/workflows/publish.yml
vendored
34
.github/workflows/publish.yml
vendored
|
@ -98,3 +98,37 @@ jobs:
|
|||
run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
dockerhub:
|
||||
needs: [publish]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: crazy-max/ghaction-docker-meta@v1
|
||||
with:
|
||||
images: redocly/redoc
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./config/docker/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||
|
|
58
CHANGELOG.md
58
CHANGELOG.md
|
@ -1,3 +1,58 @@
|
|||
# [2.0.0-rc.68](https://github.com/Redocly/redoc/compare/v2.0.0-rc.67...v2.0.0-rc.68) (2022-05-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* examples in json schema object([5b9aa27](https://github.com/Redocly/redoc/commit/5b9aa27af03a1c4616f7e0195afeba47d1deeaa0))
|
||||
* handle error when definition load fails ([#1979](https://github.com/Redocly/redoc/issues/1979)) ([508ebd5](https://github.com/Redocly/redoc/commit/508ebd58a3d66f2337e9641852322458a1bd9e6b))
|
||||
* large text in examples value ([#1974](https://github.com/Redocly/redoc/issues/1974)) ([60bc603](https://github.com/Redocly/redoc/commit/60bc603e9bb85a0c9c7ac38f7014876d397f0191))
|
||||
* not show scopes if keys empty or not exist ([#1975](https://github.com/Redocly/redoc/issues/1975)) ([4e793f0](https://github.com/Redocly/redoc/commit/4e793f07a81fa8bcd4ad384d1f87b3e6c290edb7))
|
||||
* remove dropdown-aria and use native select ([#1954](https://github.com/Redocly/redoc/issues/1954)) ([186f5a9](https://github.com/Redocly/redoc/commit/186f5a98bd466b1820121aadb865291bef8c6755))
|
||||
* make Redoc lib compatible with Webpack 5 ([#1982](https://github.com/Redocly/redoc/issues/1982)) ([867861](https://github.com/Redocly/redoc/commit/8678615a0e19c9484b4cd495d70293b542d196a5))
|
||||
|
||||
### Features
|
||||
|
||||
* implement configurable minimum characer length to init search ([#1402](https://github.com/Redocly/redoc/issues/1402)) ([0fa08fa](https://github.com/Redocly/redoc/commit/0fa08faab1c176a4bfc5a553e8e8f8b07aca659f))
|
||||
* support OAS 3.1 unevaluatedProperties ([#1978](https://github.com/Redocly/redoc/issues/1978)) ([0755ac6](https://github.com/Redocly/redoc/commit/0755ac6f04514eb0c08f90afceeda7858206b435))
|
||||
* publish dockerhub ([#1971](https://github.com/Redocly/redoc/issues/1971)) ([7e01a0](https://github.com/Redocly/redoc/commit/7e01a0cfe2ad8d06075bfc66ef3860edbef033f8))
|
||||
|
||||
|
||||
# [2.0.0-rc.67](https://github.com/Redocly/redoc/compare/v2.0.0-rc.66...v2.0.0-rc.67) (2022-04-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Expand/Collapse all buttons disappears for flat structures ([#1424](https://github.com/Redocly/redoc/issues/1424)) ([2ca8e08](https://github.com/Redocly/redoc/commit/2ca8e081baea6996eb01b5df27b8cd88331d5c96))
|
||||
* improve markdown render with CRLF ([#1953](https://github.com/Redocly/redoc/issues/1953)) ([aba2d1a](https://github.com/Redocly/redoc/commit/aba2d1ad2d8dda9f52055c36ebde1323457dfd3e))
|
||||
* issue with navigation when operationId contains backslash or quotes ([#1513](https://github.com/Redocly/redoc/issues/1513)) ([8f7e56c](https://github.com/Redocly/redoc/commit/8f7e56c747d88be5c5eb5c4bbaee0ff69e9cb2ec))
|
||||
* prefix operation ids with parent id ([#1245](https://github.com/Redocly/redoc/issues/1245)) ([fd8917e](https://github.com/Redocly/redoc/commit/fd8917e5c109840c1bfa4c2c0902b6dcec200286))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add optional BASE_PATH to Docker config ([#1378](https://github.com/Redocly/redoc/issues/1378)) ([90f71c0](https://github.com/Redocly/redoc/commit/90f71c0d77719871910cfba883a32ad131bef059))
|
||||
* theme add sidebar activeBackgroundColor and activeTextColor ([#1600](https://github.com/Redocly/redoc/issues/1600)) ([6716b08](https://github.com/Redocly/redoc/commit/6716b08e8871d95880e9f5a6c5491038002754e8))
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.66](https://github.com/Redocly/redoc/compare/v2.0.0-rc.65...v2.0.0-rc.66) (2022-03-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add handle local files for serve command ([#1810](https://github.com/Redocly/redoc/issues/1810)) ([117071e](https://github.com/Redocly/redoc/commit/117071ee83a32d9b3350d8afe2bdb6365a44e2ec))
|
||||
* move comma out of code block in SecurityRequirement.tsx ([#1924](https://github.com/Redocly/redoc/issues/1924)) ([ab3e8a8](https://github.com/Redocly/redoc/commit/ab3e8a8f80f453066c5495e73ac932a8fef0830a))
|
||||
* rename bandle command and add deprecate notice ([#1935](https://github.com/Redocly/redoc/issues/1935)) ([eb096b6](https://github.com/Redocly/redoc/commit/eb096b69be52568fc581027161c7d0c4b26c56c1))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for displaying operationId in the sidebar ([#1927](https://github.com/Redocly/redoc/issues/1927)) ([09786f2](https://github.com/Redocly/redoc/commit/09786f2a5ade6303ea00512483b172347721ca70))
|
||||
* add nonce support ([#1566](https://github.com/Redocly/redoc/issues/1566)) ([c75ac9c](https://github.com/Redocly/redoc/commit/c75ac9cf70012e2d539b379aab2f0974d088db07))
|
||||
* h2 set color form theme.colors.text.primary ([#1491](https://github.com/Redocly/redoc/pull/1491)) ([25be93](https://github.com/Redocly/redoc/commit/25be934bb184d7b2b6b47d004b3c83ce4d16a2c6))
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.65](https://github.com/Redocly/redoc/compare/v2.0.0-rc.64...v2.0.0-rc.65) (2022-03-15)
|
||||
|
||||
|
||||
|
@ -6,7 +61,8 @@
|
|||
* auth link scroll for Firerox ([#1922](https://github.com/Redocly/redoc/issues/1922)) ([fe67e9c](https://github.com/Redocly/redoc/commit/fe67e9c332fee716582a00d60fdf34767bff22d4))
|
||||
* improve customization fab ([#1891](https://github.com/Redocly/redoc/issues/1891)) ([635f379](https://github.com/Redocly/redoc/commit/635f379eb086268c91eef715148eca8f080cfb86))
|
||||
* sanitize array of items ([#1920](https://github.com/Redocly/redoc/issues/1920)) ([059bd80](https://github.com/Redocly/redoc/commit/059bd8000e5fd65753d5ca9e0c47940394e0c79b))
|
||||
* use x-displayName in securityDefinitions [#1444](https://github.com/Redocly/redoc/pull/1444)) ([ac6fb4](https://github.com/Redocly/redoc/commit/
|
||||
* use x-displayName in securityDefinitions ([#1444](https://github.com/Redocly/redoc/pull/1444)) ([ac6fb4](https://github.com/Redocly/redoc/commit/ac6fb458a4eee8d0da4b63f9bafc7669adc8af03))
|
||||
* deprecated badge on one of any of buttons ([#1930](https://github.com/Redocly/redoc/pull/1930)) ([f60b47](https://github.com/Redocly/redoc/commit/f60b4758330dd756d670309827da60d3465b672a))
|
||||
|
||||
|
||||
|
||||
|
|
32
README.md
32
README.md
|
@ -131,11 +131,11 @@ Additionally, all the 1.x releases are hosted on our GitHub Pages-based CDN **(d
|
|||
|
||||
## Lint OpenAPI definitions
|
||||
|
||||
Redocly's OpenAPI CLI is an open source command-line tool that you can use to lint
|
||||
Redocly's CLI is an [open source command-line tool](https://github.com/Redocly/redocly-cli) that you can use to lint
|
||||
your OpenAPI definition. Linting helps you to catch errors and inconsistencies in your
|
||||
OpenAPI definition before publishing.
|
||||
|
||||
Refer to [Lint configuration](https://redoc.ly/docs/cli/guides/lint/) in the OpenAPI documentation for more information.
|
||||
Refer to [Redocly configuration](https://redocly.com/docs/cli/configuration/) in the OpenAPI documentation for more information.
|
||||
|
||||
## Deployment
|
||||
|
||||
|
@ -174,14 +174,11 @@ replace the `spec-url` attribute with the url or local file address to your defi
|
|||
|
||||
For step-by-step instructions for how to get started using Redoc
|
||||
to render your OpenAPI definition, refer to the
|
||||
[**Redoc quickstart guide**](https://redoc.ly/docs/redoc/quickstart/intro/).
|
||||
|
||||
See [**IE11 Support Notes**](docs/usage-with-ie11.md) for information on
|
||||
IE support for Redoc.
|
||||
[**Redoc quickstart guide**](https://redocly.com/docs/redoc/quickstart/) and [**How to use the HTML element**](https://redocly.com/docs/redoc/deployment/html/).
|
||||
|
||||
## Redoc CLI
|
||||
For more information on Redoc's commmand-line interface, refer to
|
||||
[**Using the Redoc CLI**](https://redoc.ly/docs/redoc/quickstart/cli/).
|
||||
[**Using the Redoc CLI**](https://redocly.com/docs/redoc/deployment/cli/).
|
||||
|
||||
|
||||
## Configuration
|
||||
|
@ -191,7 +188,7 @@ You can inject the Security Definitions widget into any place in your definition
|
|||
For more information, refer to [Security definitions injection](docs/security-definitions-injection.md).
|
||||
|
||||
### OpenAPI specification extensions
|
||||
Redoc uses the following [specification extensions](https://swagger.io/specification/#specificationExtensions):
|
||||
Redoc uses the following [specification extensions](https://redocly.com/docs/api-reference-docs/spec-extensions/):
|
||||
* [`x-logo`](docs/redoc-vendor-extensions.md#x-logo) - is used to specify API logo
|
||||
* [`x-traitTag`](docs/redoc-vendor-extensions.md#x-traitTag) - useful for handling out common things like Pagination, Rate-Limits, etc
|
||||
* [`x-codeSamples`](docs/redoc-vendor-extensions.md#x-codeSamples) - specify operation code samples
|
||||
|
@ -210,6 +207,7 @@ Redoc uses the following [specification extensions](https://swagger.io/specifica
|
|||
You can use all of the following options with the standalone version of the <redoc> tag by kebab-casing them. For example, `scrollYOffset` becomes `scroll-y-offset`, and `expandResponses` becomes `expand-responses`.
|
||||
|
||||
* `disableSearch` - disable search indexing and search box.
|
||||
* `minCharacterLengthToInitSearch` - set minimal characters length to init search, default `3`, minimal `1`.
|
||||
* `expandDefaultServerVariables` - enable expanding default server variables, default `false`.
|
||||
* `expandResponses` - specify which responses to expand by default by response codes. Values should be passed as comma-separated list without spaces e.g. `expandResponses="200,201"`. Special value `"all"` expands all responses by default. Be careful: this option can slow-down documentation rendering time.
|
||||
* `generatedPayloadSamplesMaxDepth` - set the maximum render depth for JSON payload samples (responses and request body). The default value is `10`.
|
||||
|
@ -246,9 +244,11 @@ You can use all of the following options with the standalone version of the <red
|
|||
* `payloadSampleIdx` - if set, payload sample will be inserted at this index or last. Indexes start from 0.
|
||||
* `theme` - ReDoc theme. For details check [theme docs](#redoc-theme-object).
|
||||
* `untrustedSpec` - if set, the spec is considered untrusted and all HTML/markdown is sanitized to prevent XSS. **Disabled by default** for performance reasons. **Enable this option if you work with untrusted user data!**
|
||||
* `nonce` - if set, the provided value will be injected in every injected HTML element in the `nonce` attribute. Useful when using CSP, see https://webpack.js.org/guides/csp/.
|
||||
* `sideNavStyle` - can be specified in various ways:
|
||||
* **summary-only**: displays a summary in the sidebar navigation item. (**default**)
|
||||
* **path-only**: displays a path in the sidebar navigation item.
|
||||
* **id-only**: displays the operation id with a fallback to the path in the sidebar navigation item.
|
||||
|
||||
### `<redoc>` theme object
|
||||
* `spacing`
|
||||
|
@ -286,21 +286,25 @@ You can use all of the following options with the standalone version of the <red
|
|||
* `color`: # COMPUTED: colors.primary.main
|
||||
* `visited`: # COMPUTED: typography.links.color
|
||||
* `hover`: # COMPUTED: lighten(0.2 typography.links.color)
|
||||
* `menu`
|
||||
* `sidebar`
|
||||
* `width`: '260px'
|
||||
* `backgroundColor`: '#fafafa'
|
||||
* `textColor`: '#333333'
|
||||
* `activeTextColor`: # COMPUTED: theme.menu.textColor (if set by user) or theme.colors.primary.main
|
||||
* `activeTextColor`: # COMPUTED: theme.sidebar.textColor (if set by user) or theme.colors.primary.main
|
||||
* `groupItems` # Group headings
|
||||
* `activeBackgroundColor`: # COMPUTED: theme.sidebar.backgroundColor
|
||||
* `activeTextColor`: # COMPUTED: theme.sidebar.activeTextColor
|
||||
* `textTransform`: 'uppercase'
|
||||
* `level1Items` # Level 1 items like tags or section 1st level items
|
||||
* `activeBackgroundColor`: # COMPUTED: theme.sidebar.backgroundColor
|
||||
* `activeTextColor`: # COMPUTED: theme.sidebar.activeTextColor
|
||||
* `textTransform`: 'none'
|
||||
* `arrow` # menu arrow
|
||||
* `arrow` # sidebar arrow
|
||||
* `size`: '1.5em'
|
||||
* `color`: # COMPUTED: theme.menu.textColor
|
||||
* `color`: # COMPUTED: theme.sidebar.textColor
|
||||
* `logo`
|
||||
* `maxHeight`: # COMPUTED: menu.width
|
||||
* `maxWidth`: # COMPUTED: menu.width
|
||||
* `maxHeight`: # COMPUTED: sidebar.width
|
||||
* `maxWidth`: # COMPUTED: sidebar.width
|
||||
* `gutter`: '2px' # logo image padding
|
||||
* `rightPanel`
|
||||
* `backgroundColor`: '#263238'
|
||||
|
|
|
@ -12,20 +12,23 @@ or using [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runn
|
|||
The two following commands are available:
|
||||
|
||||
- `redoc-cli serve [spec]` - starts the server with `spec` rendered with ReDoc.
|
||||
Supports a server-side rendering mode (`--ssr`),
|
||||
and can watch the spec (`--watch`) to automatically reload the page whenever it changes.
|
||||
- `redoc-cli bundle [spec]` - bundles `spec` and ReDoc into a **zero-dependency** HTML file.
|
||||
Supports a server-side rendering mode (`--ssr`)
|
||||
and can watch the spec (`--watch`) to automatically reload the page whenever it changes.\
|
||||
Deprecated. Use `npx @redocly/openapi-cli preview-docs [spec]`
|
||||
- `redoc-cli bundle [spec]` - bundles `spec` and Redoc into a **zero-dependency** HTML file.\
|
||||
Deprecated. Use Use "build" command instead.
|
||||
- `redoc-cli build [spec]` - build `spec` and Redoc into a **zero-dependency** HTML file.
|
||||
|
||||
Some examples:
|
||||
|
||||
- Bundle with the main color changed to `orange`:<br/>
|
||||
`$ redoc-cli bundle [spec] --options.theme.colors.primary.main=orange`
|
||||
`$ redoc-cli build [spec] --options.theme.colors.primary.main=orange`
|
||||
- Serve with the `nativeScrollbars` option set to true:<br/>
|
||||
`$ redoc-cli serve [spec] --options.nativeScrollbars`
|
||||
- Bundle using a custom [Handlebars](https://handlebarsjs.com/) template
|
||||
(check the [default template](https://github.com/Redocly/redoc/blob/master/cli/template.hbs) for an example):<br/>
|
||||
`$ redoc-cli bundle [spec] -t custom.hbs`
|
||||
`$ redoc-cli build [spec] -t custom.hbs`
|
||||
- Bundle using a custom template and add custom `templateOptions`:<br/>
|
||||
`$ redoc-cli bundle [spec] -t custom.hbs --templateOptions.metaDescription "Page meta description"`
|
||||
`$ redoc-cli build [spec] -t custom.hbs --templateOptions.metaDescription "Page meta description"`
|
||||
|
||||
For more details, run `redoc-cli --help`.
|
||||
|
|
155
cli/index.ts
155
cli/index.ts
|
@ -6,7 +6,7 @@ import { ServerStyleSheet } from 'styled-components';
|
|||
|
||||
import { compile } from 'handlebars';
|
||||
import { createServer, IncomingMessage, ServerResponse } from 'http';
|
||||
import { dirname, join, resolve } from 'path';
|
||||
import { dirname, join, resolve, extname as getExtName } from 'path';
|
||||
|
||||
import * as zlib from 'zlib';
|
||||
|
||||
|
@ -39,9 +39,78 @@ interface Options {
|
|||
redocOptions?: any;
|
||||
}
|
||||
|
||||
export const mimeTypes = {
|
||||
'.html': 'text/html',
|
||||
'.js': 'text/javascript',
|
||||
'.css': 'text/css',
|
||||
'.json': 'application/json',
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpg',
|
||||
'.gif': 'image/gif',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.wav': 'audio/wav',
|
||||
'.mp4': 'video/mp4',
|
||||
'.woff': 'application/font-woff',
|
||||
'.ttf': 'application/font-ttf',
|
||||
'.eot': 'application/vnd.ms-fontobject',
|
||||
'.otf': 'application/font-otf',
|
||||
'.wasm': 'application/wasm',
|
||||
};
|
||||
|
||||
const BUNDLES_DIR = dirname(require.resolve('redoc'));
|
||||
|
||||
/* tslint:disable-next-line */
|
||||
const builderForBuildCommand = yargs => {
|
||||
yargs.positional('spec', {
|
||||
describe: 'path or URL to your spec',
|
||||
});
|
||||
|
||||
yargs.option('o', {
|
||||
describe: 'Output file',
|
||||
alias: 'output',
|
||||
type: 'string',
|
||||
default: 'redoc-static.html',
|
||||
});
|
||||
|
||||
yargs.options('title', {
|
||||
describe: 'Page Title',
|
||||
type: 'string',
|
||||
});
|
||||
|
||||
yargs.options('disableGoogleFont', {
|
||||
describe: 'Disable Google Font',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
});
|
||||
|
||||
yargs.option('cdn', {
|
||||
describe: 'Do not include ReDoc source code into html page, use link to CDN instead',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
});
|
||||
|
||||
yargs.demandOption('spec');
|
||||
return yargs;
|
||||
};
|
||||
|
||||
const handlerForBuildCommand = async (argv: any) => {
|
||||
const config = {
|
||||
ssr: true,
|
||||
output: argv.o as string,
|
||||
cdn: argv.cdn as boolean,
|
||||
title: argv.title as string,
|
||||
disableGoogleFont: argv.disableGoogleFont as boolean,
|
||||
templateFileName: argv.template as string,
|
||||
templateOptions: argv.templateOptions || {},
|
||||
redocOptions: getObjectOrJSON(argv.options),
|
||||
};
|
||||
|
||||
try {
|
||||
await bundle(argv.spec, config);
|
||||
} catch (e) {
|
||||
handleError(e);
|
||||
}
|
||||
};
|
||||
|
||||
YargsParser.command(
|
||||
'serve <spec>',
|
||||
'start the server',
|
||||
|
@ -104,60 +173,32 @@ YargsParser.command(
|
|||
handleError(e);
|
||||
}
|
||||
},
|
||||
[
|
||||
res => {
|
||||
console.log(
|
||||
`\n⚠️ This command is deprecated. Use "npx @redocly/openapi-cli preview-docs petstore.yaml"\n`,
|
||||
);
|
||||
return res;
|
||||
},
|
||||
],
|
||||
)
|
||||
.command(
|
||||
'build <spec>',
|
||||
'build definition into zero-dependency HTML-file',
|
||||
builderForBuildCommand,
|
||||
handlerForBuildCommand,
|
||||
)
|
||||
.command(
|
||||
'bundle <spec>',
|
||||
'bundle spec into zero-dependency HTML-file',
|
||||
yargs => {
|
||||
yargs.positional('spec', {
|
||||
describe: 'path or URL to your spec',
|
||||
});
|
||||
|
||||
yargs.option('o', {
|
||||
describe: 'Output file',
|
||||
alias: 'output',
|
||||
type: 'string',
|
||||
default: 'redoc-static.html',
|
||||
});
|
||||
|
||||
yargs.options('title', {
|
||||
describe: 'Page Title',
|
||||
type: 'string',
|
||||
});
|
||||
|
||||
yargs.options('disableGoogleFont', {
|
||||
describe: 'Disable Google Font',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
});
|
||||
|
||||
yargs.option('cdn', {
|
||||
describe: 'Do not include ReDoc source code into html page, use link to CDN instead',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
});
|
||||
|
||||
yargs.demandOption('spec');
|
||||
return yargs;
|
||||
},
|
||||
async (argv: any) => {
|
||||
const config = {
|
||||
ssr: true,
|
||||
output: argv.o as string,
|
||||
cdn: argv.cdn as boolean,
|
||||
title: argv.title as string,
|
||||
disableGoogleFont: argv.disableGoogleFont as boolean,
|
||||
templateFileName: argv.template as string,
|
||||
templateOptions: argv.templateOptions || {},
|
||||
redocOptions: getObjectOrJSON(argv.options),
|
||||
};
|
||||
|
||||
try {
|
||||
await bundle(argv.spec, config);
|
||||
} catch (e) {
|
||||
handleError(e);
|
||||
}
|
||||
'bundle spec into zero-dependency HTML-file [deprecated]',
|
||||
builderForBuildCommand,
|
||||
handlerForBuildCommand,
|
||||
[
|
||||
res => {
|
||||
console.log(`\n⚠️ This command is deprecated. Use "build" command instead.\n`);
|
||||
return res;
|
||||
},
|
||||
],
|
||||
)
|
||||
.demandCommand()
|
||||
.options('t', {
|
||||
|
@ -197,10 +238,20 @@ async function serve(host: string, port: number, pathToSpec: string, options: Op
|
|||
'Content-Type': 'application/json',
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
const filePath = join(dirname(pathToSpec), request.url || '');
|
||||
const extname = String(getExtName(filePath)).toLowerCase() as keyof typeof mimeTypes;
|
||||
|
||||
const contentType = mimeTypes[extname] || 'application/octet-stream';
|
||||
respondWithGzip(createReadStream(filePath), request, response, {
|
||||
'Content-Type': contentType,
|
||||
});
|
||||
} catch (e) {
|
||||
response.writeHead(404);
|
||||
response.write('Not found');
|
||||
response.end();
|
||||
}
|
||||
}
|
||||
|
||||
console.timeEnd('GET ' + request.url);
|
||||
});
|
||||
|
|
146
cli/npm-shrinkwrap.json
generated
146
cli/npm-shrinkwrap.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "redoc-cli",
|
||||
"version": "0.13.8",
|
||||
"version": "0.13.11",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "redoc-cli",
|
||||
"version": "0.13.8",
|
||||
"version": "0.13.11",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.1",
|
||||
|
@ -17,9 +17,9 @@
|
|||
"node-libs-browser": "^2.2.1",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"redoc": "2.0.0-rc.64",
|
||||
"redoc": "2.0.0-rc.67",
|
||||
"styled-components": "^5.3.0",
|
||||
"yargs": "^17.0.1"
|
||||
"yargs": "^17.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"redoc-cli": "index.js"
|
||||
|
@ -216,9 +216,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@redocly/openapi-core": {
|
||||
"version": "1.0.0-beta.80",
|
||||
"resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.80.tgz",
|
||||
"integrity": "sha512-IAQECLt/fDxjlfNdLGnJszt40BaiA6b78+zB6+7Rk8ums0HHLfwWFJPMTzh1bzJ5f+sZ4zDBi4gaIJ1n4XGCHg==",
|
||||
"version": "1.0.0-beta.91",
|
||||
"resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.91.tgz",
|
||||
"integrity": "sha512-8RhZGn5jSoy3oZE0sAdXxhPPHrqKgy2JVJzLqjgX9LDjNf7cXOTYOXkXIkjv1tfZHFBV/H7c08rRLEdxnzn0dg==",
|
||||
"dependencies": {
|
||||
"@redocly/ajv": "^8.6.4",
|
||||
"@types/node": "^14.11.8",
|
||||
|
@ -236,9 +236,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@redocly/openapi-core/node_modules/@types/node": {
|
||||
"version": "14.18.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz",
|
||||
"integrity": "sha512-j11XSuRuAlft6vLDEX4RvhqC0KxNxx6QIyMXNb0vHHSNPXTPeiy3algESWmOOIzEtiEL0qiowPU3ewW9hHVa7Q=="
|
||||
"version": "14.18.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz",
|
||||
"integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A=="
|
||||
},
|
||||
"node_modules/@redocly/react-dropdown-aria": {
|
||||
"version": "2.0.12",
|
||||
|
@ -1273,9 +1273,7 @@
|
|||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"os": ["darwin"],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
|
@ -1698,9 +1696,9 @@
|
|||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
|
@ -1709,9 +1707,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
|
@ -2219,11 +2217,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/redoc": {
|
||||
"version": "2.0.0-rc.64",
|
||||
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.64.tgz",
|
||||
"integrity": "sha512-zrM/vcONpbmyDUOpk7Ai7R/yZrT7W1+8ANJUB3b5kzaLQUx4VbrDoT4D6HHHquOnKx+5We4nOPPAi8fi/cqa8g==",
|
||||
"version": "2.0.0-rc.67",
|
||||
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.67.tgz",
|
||||
"integrity": "sha512-u6rEKB0LylSisN+mFa3flj7zf+prXDB+G02foqC9BOlcXkUYXHFDZM4L3BTBL/DstyGTgjhe2dA9csAjIVti/g==",
|
||||
"dependencies": {
|
||||
"@redocly/openapi-core": "^1.0.0-beta.54",
|
||||
"@redocly/openapi-core": "^1.0.0-beta.88",
|
||||
"@redocly/react-dropdown-aria": "^2.0.11",
|
||||
"classnames": "^2.3.1",
|
||||
"decko": "^1.2.0",
|
||||
|
@ -2238,7 +2236,7 @@
|
|||
"path-browserify": "^1.0.1",
|
||||
"perfect-scrollbar": "^1.5.1",
|
||||
"polished": "^4.1.3",
|
||||
"prismjs": "^1.24.1",
|
||||
"prismjs": "^1.27.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-tabs": "^3.2.2",
|
||||
"slugify": "~1.4.7",
|
||||
|
@ -2493,24 +2491,24 @@
|
|||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
|
||||
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
@ -2953,28 +2951,28 @@
|
|||
"integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A=="
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz",
|
||||
"integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==",
|
||||
"version": "17.3.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz",
|
||||
"integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==",
|
||||
"dependencies": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.0",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
"yargs-parser": "^21.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "20.2.7",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz",
|
||||
"integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==",
|
||||
"version": "21.0.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
|
||||
"integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=12"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -3148,9 +3146,9 @@
|
|||
}
|
||||
},
|
||||
"@redocly/openapi-core": {
|
||||
"version": "1.0.0-beta.80",
|
||||
"resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.80.tgz",
|
||||
"integrity": "sha512-IAQECLt/fDxjlfNdLGnJszt40BaiA6b78+zB6+7Rk8ums0HHLfwWFJPMTzh1bzJ5f+sZ4zDBi4gaIJ1n4XGCHg==",
|
||||
"version": "1.0.0-beta.91",
|
||||
"resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.91.tgz",
|
||||
"integrity": "sha512-8RhZGn5jSoy3oZE0sAdXxhPPHrqKgy2JVJzLqjgX9LDjNf7cXOTYOXkXIkjv1tfZHFBV/H7c08rRLEdxnzn0dg==",
|
||||
"requires": {
|
||||
"@redocly/ajv": "^8.6.4",
|
||||
"@types/node": "^14.11.8",
|
||||
|
@ -3165,9 +3163,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "14.18.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz",
|
||||
"integrity": "sha512-j11XSuRuAlft6vLDEX4RvhqC0KxNxx6QIyMXNb0vHHSNPXTPeiy3algESWmOOIzEtiEL0qiowPU3ewW9hHVa7Q=="
|
||||
"version": "14.18.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz",
|
||||
"integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -4415,17 +4413,17 @@
|
|||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "1.0.4",
|
||||
|
@ -4824,11 +4822,11 @@
|
|||
}
|
||||
},
|
||||
"redoc": {
|
||||
"version": "2.0.0-rc.64",
|
||||
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.64.tgz",
|
||||
"integrity": "sha512-zrM/vcONpbmyDUOpk7Ai7R/yZrT7W1+8ANJUB3b5kzaLQUx4VbrDoT4D6HHHquOnKx+5We4nOPPAi8fi/cqa8g==",
|
||||
"version": "2.0.0-rc.67",
|
||||
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.67.tgz",
|
||||
"integrity": "sha512-u6rEKB0LylSisN+mFa3flj7zf+prXDB+G02foqC9BOlcXkUYXHFDZM4L3BTBL/DstyGTgjhe2dA9csAjIVti/g==",
|
||||
"requires": {
|
||||
"@redocly/openapi-core": "^1.0.0-beta.54",
|
||||
"@redocly/openapi-core": "^1.0.0-beta.88",
|
||||
"@redocly/react-dropdown-aria": "^2.0.11",
|
||||
"classnames": "^2.3.1",
|
||||
"decko": "^1.2.0",
|
||||
|
@ -4843,7 +4841,7 @@
|
|||
"path-browserify": "^1.0.1",
|
||||
"perfect-scrollbar": "^1.5.1",
|
||||
"polished": "^4.1.3",
|
||||
"prismjs": "^1.24.1",
|
||||
"prismjs": "^1.27.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-tabs": "^3.2.2",
|
||||
"slugify": "~1.4.7",
|
||||
|
@ -5050,21 +5048,21 @@
|
|||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
|
||||
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
"strip-ansi": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
"ansi-regex": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"style-loader": {
|
||||
|
@ -5378,23 +5376,23 @@
|
|||
"integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz",
|
||||
"integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==",
|
||||
"version": "17.3.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz",
|
||||
"integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==",
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.0",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
"yargs-parser": "^21.0.0"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "20.2.7",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz",
|
||||
"integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw=="
|
||||
"version": "21.0.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
|
||||
"integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "redoc-cli",
|
||||
"version": "0.13.8",
|
||||
"version": "0.13.11",
|
||||
"description": "ReDoc's Command Line Interface",
|
||||
"main": "index.js",
|
||||
"bin": "index.js",
|
||||
|
@ -19,9 +19,9 @@
|
|||
"node-libs-browser": "^2.2.1",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"redoc": "2.0.0-rc.64",
|
||||
"redoc": "2.0.0-rc.67",
|
||||
"styled-components": "^5.3.0",
|
||||
"yargs": "^17.0.1"
|
||||
"yargs": "^17.3.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
@ -26,6 +26,7 @@ FROM nginx:alpine
|
|||
|
||||
ENV PAGE_TITLE="ReDoc"
|
||||
ENV PAGE_FAVICON="favicon.png"
|
||||
ENV BASE_PATH=
|
||||
ENV SPEC_URL="http://petstore.swagger.io/v2/swagger.json"
|
||||
ENV PORT=80
|
||||
ENV REDOC_OPTIONS=
|
||||
|
|
|
@ -45,9 +45,10 @@ Another issue with OpenShift is that the default exposed port `80` cannot be use
|
|||
|
||||
- `PAGE_TITLE` (default `"ReDoc"`) - page title
|
||||
- `PAGE_FAVICON` (default `"favicon.png"`) - URL to page favicon
|
||||
- `BASE_PATH` (optional) - prepend favicon & standalone bundle with this path
|
||||
- `SPEC_URL` (default `"http://petstore.swagger.io/v2/swagger.json"`) - URL to spec
|
||||
- `PORT` (default `80`) - nginx port
|
||||
- `REDOC_OPTIONS` - [`<redoc>` tag attributes](https://github.com/Redocly/redoc#redoc-tag-attributes)
|
||||
- `REDOC_OPTIONS` (optional) - [`<redoc>` tag attributes](https://github.com/Redocly/redoc#redoc-tag-attributes)
|
||||
|
||||
## Build
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ set -e
|
|||
|
||||
sed -i -e "s|%PAGE_TITLE%|$PAGE_TITLE|g" /usr/share/nginx/html/index.html
|
||||
sed -i -e "s|%PAGE_FAVICON%|$PAGE_FAVICON|g" /usr/share/nginx/html/index.html
|
||||
sed -i -e "s|%BASE_PATH%|$BASE_PATH|g" /usr/share/nginx/html/index.html
|
||||
sed -i -e "s|%SPEC_URL%|$SPEC_URL|g" /usr/share/nginx/html/index.html
|
||||
sed -i -e "s|%REDOC_OPTIONS%|${REDOC_OPTIONS}|g" /usr/share/nginx/html/index.html
|
||||
sed -i -e "s|\(listen\s*\) [0-9]*|\1 ${PORT}|g" /etc/nginx/nginx.conf
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>%PAGE_TITLE%</title>
|
||||
<link rel="icon" href="%PAGE_FAVICON%" />
|
||||
<link rel="icon" href="%BASE_PATH%%PAGE_FAVICON%" />
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
|
@ -23,6 +23,6 @@
|
|||
|
||||
<body>
|
||||
<redoc spec-url="%SPEC_URL%" %REDOC_OPTIONS%></redoc>
|
||||
<script src="redoc.standalone.js"></script>
|
||||
<script src="%BASE_PATH%redoc.standalone.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import styled from 'styled-components';
|
||||
import { resolve as urlResolve } from 'url';
|
||||
import { RedocStandalone } from '../src';
|
||||
import ComboBox from './ComboBox';
|
||||
import FileInput from './components/FileInput';
|
||||
|
@ -87,7 +86,7 @@ class DemoApp extends React.Component<
|
|||
let proxiedUrl = specUrl;
|
||||
if (specUrl !== DEFAULT_SPEC) {
|
||||
proxiedUrl = cors
|
||||
? '\\\\cors.redoc.ly/' + urlResolve(window.location.href, specUrl)
|
||||
? '\\\\cors.redoc.ly/' + new URL(specUrl, window.location.href).href
|
||||
: specUrl;
|
||||
}
|
||||
return (
|
||||
|
|
|
@ -93,7 +93,7 @@ paths:
|
|||
parameters:
|
||||
- name: Accept-Language
|
||||
in: header
|
||||
description: "The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US"
|
||||
description: 'The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US'
|
||||
example: en-US
|
||||
required: false
|
||||
schema:
|
||||
|
@ -182,6 +182,16 @@ paths:
|
|||
}
|
||||
requestBody:
|
||||
$ref: '#/components/requestBodies/Pet'
|
||||
delete:
|
||||
tags:
|
||||
- pet
|
||||
summary: OperationId with quotes
|
||||
operationId: deletePetBy"Id
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: OperationId with backslash
|
||||
operationId: delete\PetById
|
||||
'/pet/{petId}':
|
||||
get:
|
||||
tags:
|
||||
|
@ -259,7 +269,7 @@ paths:
|
|||
required: false
|
||||
schema:
|
||||
type: string
|
||||
example: "Bearer <TOKEN>"
|
||||
example: 'Bearer <TOKEN>'
|
||||
- name: petId
|
||||
in: path
|
||||
description: Pet id to delete
|
||||
|
@ -295,6 +305,9 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
unevaluatedProperties:
|
||||
type: integer
|
||||
format: int32
|
||||
$ref: '#/components/schemas/ApiResponse'
|
||||
security:
|
||||
- petstore_auth:
|
||||
|
@ -432,7 +445,7 @@ paths:
|
|||
application/json:
|
||||
example:
|
||||
status: 400
|
||||
message: "Invalid Order"
|
||||
message: 'Invalid Order'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
|
@ -925,7 +938,7 @@ components:
|
|||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Cat"
|
||||
$ref: '#/components/schemas/Cat'
|
||||
responses:
|
||||
'200':
|
||||
description: update Cat details
|
||||
|
@ -940,7 +953,7 @@ components:
|
|||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Cat"
|
||||
$ref: '#/components/schemas/Cat'
|
||||
responses:
|
||||
'200':
|
||||
description: create Cat details
|
||||
|
@ -1073,8 +1086,8 @@ components:
|
|||
properties:
|
||||
id:
|
||||
externalDocs:
|
||||
description: "Find more info here"
|
||||
url: "https://example.com"
|
||||
description: 'Find more info here'
|
||||
url: 'https://example.com'
|
||||
description: Pet ID
|
||||
$ref: '#/components/schemas/Id'
|
||||
category:
|
||||
|
@ -1251,9 +1264,9 @@ webhooks:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
$ref: '#/components/schemas/Pet'
|
||||
responses:
|
||||
"200":
|
||||
'200':
|
||||
description: Return a 200 status to indicate that the data was received successfully
|
||||
myWebhook:
|
||||
$ref: '#/components/pathItems/webhooks'
|
||||
|
|
|
@ -46,12 +46,12 @@ section in the documentation.
|
|||
|
||||
If you want to view your Redoc output locally, you can simulate an HTTP server.
|
||||
|
||||
#### Redocly OpenAPI CLI
|
||||
#### Redocly CLI
|
||||
|
||||
Redocly OpenAPI CLI is an open source command-line tool that includes a command
|
||||
Redocly CLI is an open source command-line tool that includes a command
|
||||
for simulating an HTTP server to provide a preview of your OpenAPI definition locally.
|
||||
|
||||
If you have [OpenAPI CLI](https://redocly.com/docs/cli/#installation-and-usage) installed, `cd` into your
|
||||
If you have [Redocly CLI](https://redocly.com/docs/cli/#installation-and-usage) installed, `cd` into your
|
||||
project directory and run the following command:
|
||||
|
||||
```bash
|
||||
|
@ -72,7 +72,7 @@ openapi preview-docs -p 8888 openapi.yaml
|
|||
Replace `openapi.yaml` in the example command with the file path to your OpenAPI definition.
|
||||
|
||||
For more information about the `preview-docs` command, refer to
|
||||
[OpenAPI CLI commands](https://redocly.com/docs/cli/commands/preview-docs/#preview-docs) in the OpenAPI CLI documentation.
|
||||
[Redocly CLI commands](https://redocly.com/docs/cli/commands/preview-docs/#preview-docs) in the Redocly CLI documentation.
|
||||
|
||||
#### Python
|
||||
|
||||
|
|
|
@ -44,12 +44,12 @@ other security reasons.
|
|||
|
||||
If you want to view your Redoc output locally, you can simulate an HTTP server.
|
||||
|
||||
#### Using Redocly OpenAPI CLI
|
||||
#### Using Redocly CLI
|
||||
|
||||
Redocly OpenAPI CLI is an open source command-line tool that includes a command
|
||||
Redocly CLI is an open source command-line tool that includes a command
|
||||
for simulating an HTTP server to provide a preview of your OpenAPI definition locally.
|
||||
|
||||
If you have [OpenAPI CLI](https://redocly.com/docs/cli/#installation-and-usage) installed, `cd` into your
|
||||
If you have [Redocly CLI](https://redocly.com/docs/cli/#installation-and-usage) installed, `cd` into your
|
||||
project directory and run the following command:
|
||||
|
||||
```bash
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
# Usage With IE11
|
||||
|
||||
|
||||
## Standalone package
|
||||
|
||||
IE11 is supported by default if you use ReDoc as a standalone package.
|
||||
|
||||
## Usage as a React component
|
||||
|
||||
If you use ReDoc as a React component you should include the following polyfills in your project:
|
||||
|
||||
```js
|
||||
import 'core-js/es6/promise';
|
||||
import 'core-js/fn/array/find';
|
||||
import 'core-js/fn/object/assign';
|
||||
import 'core-js/fn/string/ends-with';
|
||||
import 'core-js/fn/string/starts-with';
|
||||
|
||||
import 'core-js/es6/map';
|
||||
import 'core-js/es6/symbol';
|
||||
|
||||
import 'unfetch/polyfill/index'; // or any other fetch polyfill
|
||||
import 'url-polyfill';
|
||||
```
|
|
@ -49,7 +49,7 @@ describe('Menu', () => {
|
|||
cy.location('hash').should('equal', '#tag/pet');
|
||||
|
||||
cy.contains('[role=menuitem]', 'Find pet by ID').click({ force: true });
|
||||
cy.location('hash').should('equal', '#operation/getPetById');
|
||||
cy.location('hash').should('equal', '#tag/pet/operation/getPetById');
|
||||
});
|
||||
|
||||
it('should deactivate tag when other is activated', () => {
|
||||
|
@ -76,4 +76,20 @@ describe('Menu', () => {
|
|||
.then($h5 => $h5[0].firstChild!.nodeValue!.trim())
|
||||
.should('eq', 'Response Schema:');
|
||||
});
|
||||
|
||||
it('should be able to open the operation details when the operation IDs have quotes', () => {
|
||||
cy.visit('e2e/standalone-3-1.html');
|
||||
cy.get('label span[title="pet"]').click({ multiple: true, force: true });
|
||||
cy.get('li').contains('OperationId with quotes').click({ multiple: true, force: true });
|
||||
cy.get('h2').contains('OperationId with quotes').should('be.visible');
|
||||
cy.url().should('include', 'deletePetBy%22Id');
|
||||
});
|
||||
|
||||
it.only('should encode URL when the operation IDs have backslashes', () => {
|
||||
cy.visit('e2e/standalone-3-1.html');
|
||||
cy.get('label span[title="pet"]').click({ multiple: true, force: true });
|
||||
cy.get('li').contains('OperationId with backslash').click({ multiple: true, force: true });
|
||||
cy.get('h2').contains('OperationId with backslash').should('be.visible');
|
||||
cy.url().should('include', 'delete%5CPetById');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,12 +21,12 @@ describe('Servers', () => {
|
|||
initReDoc(win, spec, {});
|
||||
|
||||
// TODO add cy-data attributes
|
||||
cy.get('[data-section-id="operation/addPet"]').should(
|
||||
cy.get('[data-section-id="tag/pet/operation/addPet"]').should(
|
||||
'contain',
|
||||
'http://petstore.swagger.io/v2/pet',
|
||||
);
|
||||
|
||||
cy.get('[data-section-id="operation/addPet"]').should(
|
||||
cy.get('[data-section-id="tag/pet/operation/addPet"]').should(
|
||||
'contain',
|
||||
'http://petstore.swagger.io/sandbox/pet',
|
||||
);
|
||||
|
@ -40,7 +40,7 @@ describe('Servers', () => {
|
|||
initReDoc(win, spec, {});
|
||||
|
||||
// TODO add cy-data attributes
|
||||
cy.get('[data-section-id="operation/addPet"]').should(
|
||||
cy.get('[data-section-id="tag/pet/operation/addPet"]').should(
|
||||
'contain',
|
||||
'http://localhost:' + win.location.port + '/pet',
|
||||
);
|
||||
|
@ -55,7 +55,7 @@ describe('Servers', () => {
|
|||
initReDoc(win, spec, {});
|
||||
|
||||
// TODO add cy-data attributes
|
||||
cy.get('[data-section-id="operation/addPet"]').should(
|
||||
cy.get('[data-section-id="tag/pet/operation/addPet"]').should(
|
||||
'contain',
|
||||
'http://localhost:' + win.location.port + '/pet',
|
||||
);
|
||||
|
|
19
e2e/integration/urls.e2e.ts
Normal file
19
e2e/integration/urls.e2e.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
describe('Supporting both operation/* and parent/*/operation* urls', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('e2e/standalone.html');
|
||||
});
|
||||
|
||||
it('should supporting operation/* url', () => {
|
||||
cy.url().then(loc => {
|
||||
cy.visit(loc + '#operation/updatePet');
|
||||
cy.get('li[data-item-id="tag/pet/operation/updatePet"]').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('should supporting parent/*/operation url', () => {
|
||||
cy.url().then(loc => {
|
||||
cy.visit(loc + '#tag/pet/operation/addPet');
|
||||
cy.get('li[data-item-id="tag/pet/operation/addPet"]').should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
357
package-lock.json
generated
357
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
|
@ -1,14 +1,13 @@
|
|||
{
|
||||
"name": "redoc",
|
||||
"version": "2.0.0-rc.65",
|
||||
"version": "2.0.0-rc.68",
|
||||
"description": "ReDoc",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Redocly/redoc"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults",
|
||||
"ie 11"
|
||||
"defaults"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6.9",
|
||||
|
@ -33,7 +32,7 @@
|
|||
"start": "webpack serve --mode=development --env playground --hot --config demo/webpack.config.ts",
|
||||
"start:prod": "webpack serve --env playground --mode=production --config demo/webpack.config.ts",
|
||||
"start:benchmark": "webpack serve --mode=production --env.bench --config demo/webpack.config.ts",
|
||||
"test": "npm run lint && npm run unit && npm run license-check",
|
||||
"test": "npm run unit && npm run license-check",
|
||||
"unit": "jest --coverage",
|
||||
"e2e": "cypress run",
|
||||
"e2e-ci": "cypress run --record",
|
||||
|
@ -49,13 +48,13 @@
|
|||
"stats": "webpack --env production --env standalone --json --profile --mode=production > stats.json",
|
||||
"prettier": "prettier --write \"cli/index.ts\" \"src/**/*.{ts,tsx}\"",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1",
|
||||
"lint": "eslint 'src/**/*.{js,ts,tsx}' --cache",
|
||||
"lint": "eslint --fix 'src/**/*.{js,ts,tsx}' --cache",
|
||||
"benchmark": "node ./benchmark/benchmark.js",
|
||||
"start:demo": "webpack serve --hot --config demo/webpack.config.ts --mode=development",
|
||||
"compile:cli": "tsc custom.d.ts cli/index.ts --target es6 --module commonjs --types yargs",
|
||||
"build:demo": "webpack --mode=production --config demo/webpack.config.ts",
|
||||
"deploy:demo": "aws s3 sync demo/dist s3://production-redoc-demo --acl=public-read",
|
||||
"license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause;CC-BY-4.0' --summary",
|
||||
"license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause;CC-BY-4.0;Python-2.0' --summary",
|
||||
"docker:build": "docker build -f config/docker/Dockerfile -t redoc .",
|
||||
"prepare": "husky install",
|
||||
"pre-commit": "pretty-quick --staged && npm run lint"
|
||||
|
@ -72,7 +71,7 @@
|
|||
"@types/json-pointer": "^1.0.30",
|
||||
"@types/lunr": "^2.3.3",
|
||||
"@types/mark.js": "^8.11.5",
|
||||
"@types/marked": "^4.0.1",
|
||||
"@types/marked": "^4.0.3",
|
||||
"@types/node": "^15.6.1",
|
||||
"@types/prismjs": "^1.16.5",
|
||||
"@types/prop-types": "^15.7.3",
|
||||
|
@ -139,8 +138,7 @@
|
|||
"styled-components": "^4.1.1 || ^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@redocly/openapi-core": "^1.0.0-beta.54",
|
||||
"@redocly/react-dropdown-aria": "^2.0.11",
|
||||
"@redocly/openapi-core": "^1.0.0-beta.95",
|
||||
"classnames": "^2.3.1",
|
||||
"decko": "^1.2.0",
|
||||
"dompurify": "^2.2.8",
|
||||
|
@ -148,7 +146,7 @@
|
|||
"json-pointer": "^0.6.2",
|
||||
"lunr": "^2.3.9",
|
||||
"mark.js": "^8.11.1",
|
||||
"marked": "^4.0.10",
|
||||
"marked": "^4.0.15",
|
||||
"mobx-react": "^7.2.0",
|
||||
"openapi-sampler": "^1.2.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
|
|
68
src/common-elements/Dropdown/Dropdown.tsx
Normal file
68
src/common-elements/Dropdown/Dropdown.tsx
Normal file
|
@ -0,0 +1,68 @@
|
|||
import * as React from 'react';
|
||||
import styled from '../../styled-components';
|
||||
import { ArrowIconProps, DropdownProps, DropdownOption } from './types';
|
||||
|
||||
const ArrowSvg = ({ className, style }: ArrowIconProps): JSX.Element => (
|
||||
<svg
|
||||
className={className}
|
||||
style={style}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const ArrowIcon = styled(ArrowSvg)`
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
right: 8px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
polyline {
|
||||
color: ${props => props.variant === 'dark' && 'white'};
|
||||
}
|
||||
`;
|
||||
|
||||
const DropdownComponent = (props: DropdownProps): JSX.Element => {
|
||||
const { options, onChange, placeholder, value = '', variant, className } = props;
|
||||
|
||||
const handleOnChange = event => {
|
||||
const { selectedIndex } = event.target;
|
||||
const index = placeholder ? selectedIndex - 1 : selectedIndex;
|
||||
onChange(options[index]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<ArrowIcon variant={variant} />
|
||||
<select onChange={handleOnChange} value={value} className="dropdown-select">
|
||||
{placeholder && (
|
||||
<option disabled hidden value={placeholder}>
|
||||
{placeholder}
|
||||
</option>
|
||||
)}
|
||||
{options.map(({ idx, value, title }: DropdownOption, index) => (
|
||||
<option key={idx || value + index} value={value}>
|
||||
{title || value}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<label>{value}</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Dropdown = React.memo<DropdownProps>(DropdownComponent);
|
2
src/common-elements/Dropdown/index.ts
Normal file
2
src/common-elements/Dropdown/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from './styled';
|
||||
export * from './types';
|
94
src/common-elements/Dropdown/styled.ts
Normal file
94
src/common-elements/Dropdown/styled.ts
Normal file
|
@ -0,0 +1,94 @@
|
|||
import styled from 'styled-components';
|
||||
|
||||
import { Dropdown as DropdownComponent } from './Dropdown';
|
||||
|
||||
export const Dropdown = styled(DropdownComponent)<{
|
||||
fullWidth?: boolean;
|
||||
}>`
|
||||
label {
|
||||
box-sizing: border-box;
|
||||
min-width: 100px;
|
||||
outline: none;
|
||||
display: inline-block;
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
vertical-align: bottom;
|
||||
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
|
||||
text-transform: none;
|
||||
padding: 0 22px 0 4px;
|
||||
|
||||
font-size: 0.929em;
|
||||
line-height: 1.5em;
|
||||
font-family: inherit;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.dropdown-select {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
border: none;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
line-height: inherit;
|
||||
font-family: inherit;
|
||||
}
|
||||
box-sizing: border-box;
|
||||
min-width: 100px;
|
||||
outline: none;
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
border: 1px solid rgba(38, 50, 56, 0.5);
|
||||
vertical-align: bottom;
|
||||
padding: 2px 0px 2px 6px;
|
||||
position: relative;
|
||||
width: auto;
|
||||
background: white;
|
||||
color: #263238;
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
font-size: 0.929em;
|
||||
line-height: 1.5em;
|
||||
cursor: pointer;
|
||||
transition: border 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border: 1px solid ${props => props.theme.colors.primary.main};
|
||||
color: ${props => props.theme.colors.primary.main};
|
||||
box-shadow: 0px 0px 0px 1px ${props => props.theme.colors.primary.main};
|
||||
}
|
||||
`;
|
||||
|
||||
export const SimpleDropdown = styled(Dropdown)`
|
||||
margin-left: 10px;
|
||||
text-transform: none;
|
||||
font-size: 0.969em;
|
||||
|
||||
font-size: 1em;
|
||||
border: none;
|
||||
padding: 0 1.2em 0 0;
|
||||
background: transparent;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
label {
|
||||
color: ${props => props.theme.colors.primary.main};
|
||||
text-shadow: 0px 0px 0px ${props => props.theme.colors.primary.main};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MimeLabel = styled.span`
|
||||
margin-left: 10px;
|
||||
text-transform: none;
|
||||
font-size: 0.929em;
|
||||
color: black;
|
||||
`;
|
25
src/common-elements/Dropdown/types.ts
Normal file
25
src/common-elements/Dropdown/types.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
export interface DropdownOption {
|
||||
idx?: number;
|
||||
value: string;
|
||||
title?: string;
|
||||
serverUrl?: string;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export interface DropdownProps {
|
||||
options: DropdownOption[];
|
||||
onChange: (option: DropdownOption) => void;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
value?: string;
|
||||
dense?: boolean;
|
||||
fullWidth?: boolean;
|
||||
variant?: 'dark' | 'light';
|
||||
}
|
||||
|
||||
export interface ArrowIconProps {
|
||||
className?: string;
|
||||
variant?: 'light' | 'dark';
|
||||
style?: React.CSSProperties;
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
import Dropdown from '@redocly/react-dropdown-aria';
|
||||
|
||||
import styled from '../styled-components';
|
||||
|
||||
export interface DropdownOption {
|
||||
idx: number;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface DropdownProps {
|
||||
options: DropdownOption[];
|
||||
value: string;
|
||||
onChange: (option: DropdownOption) => void;
|
||||
ariaLabel: string;
|
||||
}
|
||||
|
||||
export const StyledDropdown = styled(Dropdown)`
|
||||
&& {
|
||||
box-sizing: border-box;
|
||||
min-width: 100px;
|
||||
outline: none;
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
border: 1px solid rgba(38, 50, 56, 0.5);
|
||||
vertical-align: bottom;
|
||||
padding: 2px 0px 2px 6px;
|
||||
position: relative;
|
||||
width: auto;
|
||||
background: white;
|
||||
color: #263238;
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
font-size: 0.929em;
|
||||
line-height: 1.5em;
|
||||
cursor: pointer;
|
||||
transition: border 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border: 1px solid ${props => props.theme.colors.primary.main};
|
||||
color: ${props => props.theme.colors.primary.main};
|
||||
box-shadow: 0px 0px 0px 1px ${props => props.theme.colors.primary.main};
|
||||
}
|
||||
.dropdown-selector {
|
||||
display: inline-flex;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
padding-right: 20px;
|
||||
position: relative;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.dropdown-selector-value {
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
position: relative;
|
||||
font-size: 0.929em;
|
||||
width: 100%;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
color: #263238;
|
||||
left: 0;
|
||||
transition: color 0.25s ease, text-shadow 0.25s ease;
|
||||
}
|
||||
.dropdown-arrow {
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border-color: ${props => props.theme.colors.primary.main} transparent transparent;
|
||||
border-style: solid;
|
||||
border-width: 0.35em 0.35em 0;
|
||||
width: 0;
|
||||
svg {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-selector-content {
|
||||
position: absolute;
|
||||
margin-top: 2px;
|
||||
left: -2px;
|
||||
right: 0;
|
||||
|
||||
z-index: 10;
|
||||
min-width: 100px;
|
||||
|
||||
background: white;
|
||||
border: 1px solid rgba(38, 50, 56, 0.2);
|
||||
box-shadow: 0px 2px 4px 0px rgba(34, 36, 38, 0.12), 0px 2px 10px 0px rgba(34, 36, 38, 0.08);
|
||||
|
||||
max-height: 220px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dropdown-option {
|
||||
font-size: 0.9em;
|
||||
color: #263238;
|
||||
cursor: pointer;
|
||||
padding: 0.4em;
|
||||
background-color: #ffffff;
|
||||
|
||||
&[aria-selected='true'] {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(38, 50, 56, 0.12);
|
||||
}
|
||||
}
|
||||
input {
|
||||
cursor: pointer;
|
||||
height: 1px;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const SimpleDropdown = styled(StyledDropdown)`
|
||||
&& {
|
||||
margin-left: 10px;
|
||||
text-transform: none;
|
||||
font-size: 0.969em;
|
||||
|
||||
font-size: 1em;
|
||||
border: none;
|
||||
padding: 0 1.2em 0 0;
|
||||
background: transparent;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
.dropdown-selector-value {
|
||||
color: ${props => props.theme.colors.primary.main};
|
||||
text-shadow: 0px 0px 0px ${props => props.theme.colors.primary.main};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MimeLabel = styled.span`
|
||||
margin-left: 10px;
|
||||
text-transform: none;
|
||||
font-size: 0.929em;
|
||||
color: black;
|
||||
`;
|
|
@ -71,6 +71,7 @@ export const PatternLabel = styled(FieldLabel)`
|
|||
|
||||
export const ExampleValue = styled(FieldLabel)`
|
||||
border-radius: 2px;
|
||||
word-break: break-word;
|
||||
${({ theme }) => `
|
||||
background-color: ${transparentize(0.95, theme.colors.text.primary)};
|
||||
color: ${transparentize(0.1, theme.colors.text.primary)};
|
||||
|
|
|
@ -15,21 +15,22 @@ export const headerCommonMixin = level => css`
|
|||
|
||||
export const H1 = styled.h1`
|
||||
${headerCommonMixin(1)};
|
||||
color: ${({ theme }) => theme.colors.primary.main};
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
|
||||
${extensionsHook('H1')};
|
||||
`;
|
||||
|
||||
export const H2 = styled.h2`
|
||||
${headerCommonMixin(2)};
|
||||
color: black;
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
margin: 0 0 20px;
|
||||
|
||||
${extensionsHook('H2')};
|
||||
`;
|
||||
|
||||
export const H3 = styled.h2`
|
||||
${headerCommonMixin(3)};
|
||||
color: black;
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
|
||||
${extensionsHook('H3')};
|
||||
`;
|
||||
|
|
|
@ -4,8 +4,8 @@ export * from './linkify';
|
|||
export * from './shelfs';
|
||||
export * from './fields-layout';
|
||||
export * from './schema';
|
||||
export * from './dropdown';
|
||||
export * from './mixins';
|
||||
export * from './tabs';
|
||||
export * from './samples';
|
||||
export * from './perfect-scrollbar';
|
||||
export * from './Dropdown';
|
||||
|
|
|
@ -67,7 +67,7 @@ function navigate(history: HistoryService, event: React.MouseEvent<HTMLAnchorEle
|
|||
!isModifiedEvent(event) // ignore clicks with modifier keys
|
||||
) {
|
||||
event.preventDefault();
|
||||
history.replace(to);
|
||||
history.replace(encodeURI(to));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,14 @@ export class CallbackSamples extends React.Component<CallbackSamplesProps> {
|
|||
context: RedocNormalizedOptions;
|
||||
|
||||
private renderDropdown = props => {
|
||||
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
||||
return (
|
||||
<DropdownOrLabel
|
||||
Label={MimeLabel}
|
||||
Dropdown={InvertedSimpleDropdown}
|
||||
{...props}
|
||||
variant="dark"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { DropdownProps, MimeLabel, SimpleDropdown } from '../../common-elements/dropdown';
|
||||
import { DropdownProps, MimeLabel, SimpleDropdown } from '../../common-elements/Dropdown';
|
||||
|
||||
export interface DropdownOrLabelProps extends DropdownProps {
|
||||
Label?: React.ComponentClass;
|
||||
|
@ -12,5 +12,5 @@ export function DropdownOrLabel(props: DropdownOrLabelProps): JSX.Element {
|
|||
if (props.options.length === 1) {
|
||||
return <Label>{props.options[0].value}</Label>;
|
||||
}
|
||||
return <Dropdown {...props} searchable={false} />;
|
||||
return <Dropdown {...props} />;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { FieldLabel, ExampleValue } from '../../common-elements/fields';
|
||||
import { getSerializedValue } from '../../utils';
|
||||
import { getSerializedValue, isArray } from '../../utils';
|
||||
|
||||
import { l } from '../../services/Labels';
|
||||
import { FieldModel } from '../../services';
|
||||
|
@ -15,22 +15,31 @@ export function Examples({ field }: { field: FieldModel }) {
|
|||
return (
|
||||
<>
|
||||
<FieldLabel> {l('examples')}: </FieldLabel>
|
||||
<ExamplesList>
|
||||
{Object.values(field.examples).map((example, idx) => {
|
||||
{isArray(field.examples) ? (
|
||||
field.examples.map((example, idx) => {
|
||||
const value = getSerializedValue(field, example);
|
||||
const stringifyValue = field.in ? String(value) : JSON.stringify(value);
|
||||
return (
|
||||
<li key={idx}>
|
||||
<React.Fragment key={idx}>
|
||||
<ExampleValue>{stringifyValue}</ExampleValue>{' '}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<ExamplesList>
|
||||
{Object.values(field.examples).map((example, idx) => (
|
||||
<li key={idx + example.value}>
|
||||
<ExampleValue>{getSerializedValue(field, example.value)}</ExampleValue> -{' '}
|
||||
{example.summary || example.description}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
))}
|
||||
</ExamplesList>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const ExamplesList = styled.ul`
|
||||
margin-top: 1em;
|
||||
padding-left: 0;
|
||||
list-style-position: inside;
|
||||
list-style-position: outside;
|
||||
`;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { DropdownProps, DropdownOption } from '../../common-elements/dropdown';
|
||||
import { DropdownProps, DropdownOption } from '../../common-elements/Dropdown';
|
||||
import { DropdownLabel, DropdownWrapper } from '../PayloadSamples/styled.elements';
|
||||
|
||||
export interface GenericChildrenSwitcherProps<T> {
|
||||
|
@ -32,8 +32,8 @@ export class GenericChildrenSwitcher<T> extends React.Component<
|
|||
};
|
||||
}
|
||||
|
||||
switchItem = ({ idx }) => {
|
||||
if (this.props.items) {
|
||||
switchItem = ({ idx }: DropdownOption) => {
|
||||
if (this.props.items && idx !== undefined) {
|
||||
this.setState({
|
||||
activeItemIdx: idx,
|
||||
});
|
||||
|
|
|
@ -26,12 +26,21 @@ class Json extends React.PureComponent<JsonProps> {
|
|||
return <CopyButtonWrapper data={this.props.data}>{this.renderInner}</CopyButtonWrapper>;
|
||||
}
|
||||
|
||||
renderInner = ({ renderCopyButton }) => (
|
||||
renderInner = ({ renderCopyButton }) => {
|
||||
const showFoldingButtons = this.props.data && Object.values(this.props.data).some(
|
||||
(value) => typeof value === 'object' && value !== null,
|
||||
);
|
||||
|
||||
return (
|
||||
<JsonViewerWrap>
|
||||
<SampleControls>
|
||||
{renderCopyButton()}
|
||||
{showFoldingButtons &&
|
||||
<>
|
||||
<button onClick={this.expandAll}> Expand all </button>
|
||||
<button onClick={this.collapseAll}> Collapse all </button>
|
||||
</>
|
||||
}
|
||||
</SampleControls>
|
||||
<OptionsContext.Consumer>
|
||||
{options => (
|
||||
|
@ -47,6 +56,7 @@ class Json extends React.PureComponent<JsonProps> {
|
|||
</OptionsContext.Consumer>
|
||||
</JsonViewerWrap>
|
||||
);
|
||||
};
|
||||
|
||||
expandAll = () => {
|
||||
const elements = this.node.getElementsByClassName('collapsible');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { DropdownProps } from '../../common-elements/dropdown';
|
||||
import { DropdownOption, DropdownProps } from '../../common-elements/Dropdown';
|
||||
import { MediaContentModel, MediaTypeModel, SchemaModel } from '../../services/models';
|
||||
import { DropdownLabel, DropdownWrapper } from '../PayloadSamples/styled.elements';
|
||||
|
||||
|
@ -20,8 +20,8 @@ export interface MediaTypesSwitchProps {
|
|||
|
||||
@observer
|
||||
export class MediaTypesSwitch extends React.Component<MediaTypesSwitchProps> {
|
||||
switchMedia = ({ idx }) => {
|
||||
if (this.props.content) {
|
||||
switchMedia = ({ idx }: DropdownOption) => {
|
||||
if (this.props.content && idx !== undefined) {
|
||||
this.props.content.activate(idx);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ import { RequestSamples } from '../RequestSamples/RequestSamples';
|
|||
import { ResponsesList } from '../Responses/ResponsesList';
|
||||
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
||||
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
|
||||
import { SECTION_ATTR } from '../../services';
|
||||
|
||||
const Description = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
||||
|
@ -37,7 +38,7 @@ export class Operation extends React.Component<OperationProps> {
|
|||
return (
|
||||
<OptionsContext.Consumer>
|
||||
{options => (
|
||||
<Row>
|
||||
<Row {...{ [SECTION_ATTR]: operation.operationHash }} id={operation.operationHash}>
|
||||
<MiddlePanel>
|
||||
<H2>
|
||||
<ShareLink to={operation.id} />
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
|||
|
||||
import styled from '../../styled-components';
|
||||
|
||||
import { DropdownProps } from '../../common-elements';
|
||||
import { DropdownOption, DropdownProps } from '../../common-elements';
|
||||
import { MediaTypeModel } from '../../services/models';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { Example } from './Example';
|
||||
|
@ -21,10 +21,12 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps, Media
|
|||
state = {
|
||||
activeIdx: 0,
|
||||
};
|
||||
switchMedia = ({ idx }) => {
|
||||
switchMedia = ({ idx }: DropdownOption) => {
|
||||
if (idx !== undefined) {
|
||||
this.setState({
|
||||
activeIdx: idx,
|
||||
});
|
||||
}
|
||||
};
|
||||
render() {
|
||||
const { activeIdx } = this.state;
|
||||
|
|
|
@ -33,6 +33,13 @@ export class PayloadSamples extends React.Component<PayloadSamplesProps> {
|
|||
}
|
||||
|
||||
private renderDropdown = props => {
|
||||
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
||||
return (
|
||||
<DropdownOrLabel
|
||||
Label={MimeLabel}
|
||||
Dropdown={InvertedSimpleDropdown}
|
||||
{...props}
|
||||
variant="dark"
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { transparentize } from 'polished';
|
||||
import styled from '../../styled-components';
|
||||
import { StyledDropdown } from '../../common-elements';
|
||||
import { Dropdown } from '../../common-elements/Dropdown';
|
||||
|
||||
export const MimeLabel = styled.div`
|
||||
padding: 0.9em;
|
||||
|
@ -27,15 +27,19 @@ export const DropdownWrapper = styled.div`
|
|||
position: relative;
|
||||
`;
|
||||
|
||||
export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
||||
&& {
|
||||
margin-left: 10px;
|
||||
export const InvertedSimpleDropdown = styled(Dropdown)`
|
||||
label {
|
||||
color: ${({ theme }) => theme.rightPanel.textColor};
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
font-size: 1em;
|
||||
text-transform: none;
|
||||
font-size: 0.929em;
|
||||
border: none;
|
||||
}
|
||||
margin: 0 0 10px 0;
|
||||
display: block;
|
||||
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
||||
font-size: 1em;
|
||||
border: none;
|
||||
padding: 0.9em 1.6em 0.9em 0.9em;
|
||||
box-shadow: none;
|
||||
|
@ -43,31 +47,8 @@ export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
|||
&:focus-within {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
&:focus-within {
|
||||
background-color: ${({ theme }) => transparentize(0.3, theme.rightPanel.backgroundColor)};
|
||||
}
|
||||
|
||||
.dropdown-arrow {
|
||||
border-top-color: ${({ theme }) => theme.rightPanel.textColor};
|
||||
}
|
||||
.dropdown-selector-value {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
color: ${({ theme }) => theme.rightPanel.textColor};
|
||||
}
|
||||
|
||||
.dropdown-selector-content {
|
||||
margin: 0;
|
||||
margin-top: 2px;
|
||||
.dropdown-option {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const NoSampleLabel = styled.div`
|
||||
|
|
|
@ -17,12 +17,21 @@ export interface RedocStandaloneProps {
|
|||
onLoaded?: (e?: Error) => any;
|
||||
}
|
||||
|
||||
declare let __webpack_nonce__: string;
|
||||
|
||||
export const RedocStandalone = function (props: RedocStandaloneProps) {
|
||||
const { spec, specUrl, options = {}, onLoaded } = props;
|
||||
const hideLoading = argValueToBoolean(options.hideLoading, false);
|
||||
|
||||
const normalizedOpts = new RedocNormalizedOptions(options);
|
||||
|
||||
if (normalizedOpts.nonce !== undefined) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
__webpack_nonce__ = normalizedOpts.nonce;
|
||||
} catch {} // If we have exception, Webpack was not used to run this.
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<StoreBuilder spec={spec} specUrl={specUrl} options={options} onLoaded={onLoaded}>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { DropdownOption, StyledDropdown } from '../../common-elements/dropdown';
|
||||
import { DropdownOption, Dropdown } from '../../common-elements/Dropdown';
|
||||
import { SchemaModel } from '../../services/models';
|
||||
|
||||
@observer
|
||||
|
@ -43,7 +43,7 @@ export class DiscriminatorDropdown extends React.Component<{
|
|||
this.sortOptions(options, enumValues);
|
||||
|
||||
return (
|
||||
<StyledDropdown
|
||||
<Dropdown
|
||||
value={activeValue}
|
||||
options={options}
|
||||
onChange={this.changeActiveChild}
|
||||
|
@ -53,6 +53,8 @@ export class DiscriminatorDropdown extends React.Component<{
|
|||
}
|
||||
|
||||
changeActiveChild = (option: DropdownOption) => {
|
||||
if (option.idx !== undefined) {
|
||||
this.props.parent.activateOneOf(option.idx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { ObjectSchema } from './ObjectSchema';
|
|||
import { OneOfSchema } from './OneOfSchema';
|
||||
|
||||
import { l } from '../../services/Labels';
|
||||
import { isArray } from '../../utils/helpers';
|
||||
|
||||
export interface SchemaOptions {
|
||||
showTitle?: boolean;
|
||||
|
@ -68,7 +69,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
|||
return <OneOfSchema schema={schema} {...rest} />;
|
||||
}
|
||||
|
||||
const types = Array.isArray(type) ? type : [type];
|
||||
const types = isArray(type) ? type : [type];
|
||||
if (types.includes('object')) {
|
||||
if (schema.fields?.length) {
|
||||
return <ObjectSchema {...(this.props as any)} level={level} />;
|
||||
|
|
|
@ -75,7 +75,14 @@ export class SchemaDefinition extends React.PureComponent<ObjectDescriptionProps
|
|||
}
|
||||
|
||||
private renderDropdown = props => {
|
||||
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
||||
return (
|
||||
<DropdownOrLabel
|
||||
Label={MimeLabel}
|
||||
Dropdown={InvertedSimpleDropdown}
|
||||
{...props}
|
||||
variant="dark"
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { MenuItem } from '../SideMenu/MenuItem';
|
|||
import { MarkerService } from '../../services/MarkerService';
|
||||
import { SearchResult } from '../../services/SearchWorker.worker';
|
||||
|
||||
import { OptionsContext } from '../OptionsProvider';
|
||||
import { bind, debounce } from 'decko';
|
||||
import { PerfectScrollbarWrap } from '../../common-elements/perfect-scrollbar';
|
||||
import {
|
||||
|
@ -37,6 +38,8 @@ export interface SearchBoxState {
|
|||
export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxState> {
|
||||
activeItemRef: MenuItem | null = null;
|
||||
|
||||
static contextType = OptionsContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
@ -114,8 +117,9 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
|||
}
|
||||
|
||||
search = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { minCharacterLengthToInitSearch } = this.context;
|
||||
const q = event.target.value;
|
||||
if (q.length < 3) {
|
||||
if (q.length < minCharacterLengthToInitSearch) {
|
||||
this.clearResults(q);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,23 @@ import { Link, UnderlinedHeader } from '../../common-elements/';
|
|||
import { SecurityRequirementModel } from '../../services/models/SecurityRequirement';
|
||||
import { linksCss } from '../Markdown/styled.elements';
|
||||
|
||||
const ScopeNameList = styled.ul`
|
||||
display: inline;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
display: inherit;
|
||||
|
||||
&:after {
|
||||
content: ',';
|
||||
}
|
||||
&:last-child:after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ScopeName = styled.code`
|
||||
font-size: ${props => props.theme.typography.code.fontSize};
|
||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||
|
@ -14,13 +31,6 @@ const ScopeName = styled.code`
|
|||
padding: 0.2em;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
|
||||
&:after {
|
||||
content: ',';
|
||||
}
|
||||
&:last-child:after {
|
||||
content: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const SecurityRequirementAndWrap = styled.span`
|
||||
|
@ -72,9 +82,13 @@ export class SecurityRequirement extends React.PureComponent<SecurityRequirement
|
|||
<SecurityRequirementAndWrap key={scheme.id}>
|
||||
<Link to={scheme.sectionId}>{scheme.displayName}</Link>
|
||||
{scheme.scopes.length > 0 && ' ('}
|
||||
<ScopeNameList>
|
||||
{scheme.scopes.map(scope => (
|
||||
<ScopeName key={scope}>{scope}</ScopeName>
|
||||
<li key={scope}>
|
||||
<ScopeName>{scope}</ScopeName>
|
||||
</li>
|
||||
))}
|
||||
</ScopeNameList>
|
||||
{scheme.scopes.length > 0 && ') '}
|
||||
</SecurityRequirementAndWrap>
|
||||
);
|
||||
|
|
|
@ -23,6 +23,7 @@ export interface OAuthFlowProps {
|
|||
export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
|
||||
render() {
|
||||
const { type, flow } = this.props;
|
||||
const scopesNames = Object.keys(flow?.scopes || {});
|
||||
return (
|
||||
<tr>
|
||||
<th> {type} OAuth Flow </th>
|
||||
|
@ -45,16 +46,21 @@ export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
|
|||
{flow!.refreshUrl}
|
||||
</div>
|
||||
)}
|
||||
{!!scopesNames.length && (
|
||||
<>
|
||||
<div>
|
||||
<strong> Scopes: </strong>
|
||||
</div>
|
||||
<ul>
|
||||
{Object.keys(flow!.scopes || {}).map(scope => (
|
||||
{scopesNames.map(scope => (
|
||||
<li key={scope}>
|
||||
<code>{scope}</code> - <Markdown inline={true} source={flow!.scopes[scope] || ''} />
|
||||
<code>{scope}</code> -{' '}
|
||||
<Markdown inline={true} source={flow!.scopes[scope] || ''} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
|
|
@ -25,8 +25,8 @@ export class SideMenu extends React.Component<{ menu: MenuStore; className?: str
|
|||
>
|
||||
<MenuItems items={store.items} onActivate={this.activate} root={true} />
|
||||
<RedocAttribution>
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://github.com/Redocly/redoc">
|
||||
Documentation Powered by ReDoc
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://redocly.com/redoc/">
|
||||
Documentation Powered by Redocly
|
||||
</a>
|
||||
</RedocAttribution>
|
||||
</PerfectScrollbarWrap>
|
||||
|
|
|
@ -66,11 +66,15 @@ export const OperationBadge = styled.span.attrs((props: { type: string }) => ({
|
|||
}
|
||||
`;
|
||||
|
||||
function menuItemActiveBg(depth, { theme }: { theme: ResolvedThemeInterface }): string {
|
||||
function menuItemActive(
|
||||
depth,
|
||||
{ theme }: { theme: ResolvedThemeInterface },
|
||||
option: string,
|
||||
): string {
|
||||
if (depth > 1) {
|
||||
return darken(0.1, theme.sidebar.backgroundColor);
|
||||
return theme.sidebar.level1Items[option];
|
||||
} else if (depth === 1) {
|
||||
return darken(0.05, theme.sidebar.backgroundColor);
|
||||
return theme.sidebar.groupItems[option];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
@ -102,17 +106,10 @@ export const menuItemDepth = {
|
|||
font-size: 0.8em;
|
||||
padding-bottom: 0;
|
||||
cursor: default;
|
||||
color: ${props => props.theme.sidebar.textColor};
|
||||
`,
|
||||
1: css`
|
||||
font-size: 0.929em;
|
||||
text-transform: ${({ theme }) => theme.sidebar.level1Items.textTransform};
|
||||
&:hover {
|
||||
color: ${props => props.theme.sidebar.activeTextColor};
|
||||
}
|
||||
`,
|
||||
2: css`
|
||||
color: ${props => props.theme.sidebar.textColor};
|
||||
`,
|
||||
};
|
||||
|
||||
|
@ -131,7 +128,9 @@ export const MenuItemLabel = styled.label.attrs((props: MenuItemLabelType) => ({
|
|||
}))<MenuItemLabelType>`
|
||||
cursor: pointer;
|
||||
color: ${props =>
|
||||
props.active ? props.theme.sidebar.activeTextColor : props.theme.sidebar.textColor};
|
||||
props.active
|
||||
? menuItemActive(props.depth, props, 'activeTextColor')
|
||||
: props.theme.sidebar.textColor};
|
||||
margin: 0;
|
||||
padding: 12.5px ${props => props.theme.spacing.unit * 4}px;
|
||||
${({ depth, type, theme }) =>
|
||||
|
@ -140,12 +139,16 @@ export const MenuItemLabel = styled.label.attrs((props: MenuItemLabelType) => ({
|
|||
justify-content: space-between;
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
${props => menuItemDepth[props.depth]};
|
||||
background-color: ${props => (props.active ? menuItemActiveBg(props.depth, props) : '')};
|
||||
background-color: ${props =>
|
||||
props.active
|
||||
? menuItemActive(props.depth, props, 'activeBackgroundColor')
|
||||
: props.theme.sidebar.backgroundColor};
|
||||
|
||||
${props => (props.deprecated && deprecatedCss) || ''};
|
||||
|
||||
&:hover {
|
||||
background-color: ${props => menuItemActiveBg(props.depth, props)};
|
||||
color: ${props => menuItemActive(props.depth, props, 'activeTextColor')};
|
||||
background-color: ${props => menuItemActive(props.depth, props, 'activeBackgroundColor')};
|
||||
}
|
||||
|
||||
${ShelfIcon} {
|
||||
|
|
|
@ -33,6 +33,10 @@ export function StoreBuilder(props: StoreBuilderProps) {
|
|||
const { spec, specUrl, options, onLoaded, children } = props;
|
||||
|
||||
const [resolvedSpec, setResolvedSpec] = React.useState<any>(null);
|
||||
const [error, setError] = React.useState<Error | null>(null);
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
async function load() {
|
||||
|
@ -40,8 +44,13 @@ export function StoreBuilder(props: StoreBuilderProps) {
|
|||
return undefined;
|
||||
}
|
||||
setResolvedSpec(null);
|
||||
try {
|
||||
const resolved = await loadAndBundleSpec(spec || specUrl!);
|
||||
setResolvedSpec(resolved);
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
load();
|
||||
}, [spec, specUrl]);
|
||||
|
|
|
@ -42,5 +42,13 @@ describe('Components', () => {
|
|||
|
||||
expect(ClipboardService.copySelected as jest.Mock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Expand/Collapse buttons disappears for flat structures', () => {
|
||||
const flatData = { a: 1, b: '2', c: null };
|
||||
const flatDataComponent = mount(withTheme(<JsonViewer data={flatData} />));
|
||||
|
||||
expect(flatDataComponent.html()).not.toContain('Expand all');
|
||||
expect(flatDataComponent.html()).not.toContain('Collapse all');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,6 +19,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"displayType": "object",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"format": undefined,
|
||||
"isCircular": undefined,
|
||||
|
@ -39,6 +40,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"displayType": "object",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"fields": Array [
|
||||
FieldModel {
|
||||
|
@ -65,6 +67,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"displayType": "number",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"format": undefined,
|
||||
"isCircular": undefined,
|
||||
|
@ -89,8 +92,10 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"jsonSampleExpandLevel": 2,
|
||||
"maxDisplayedEnumValues": undefined,
|
||||
"menuToggle": true,
|
||||
"minCharacterLengthToInitSearch": 3,
|
||||
"nativeScrollbars": false,
|
||||
"noAutoAuth": false,
|
||||
"nonce": undefined,
|
||||
"onlyRequiredInSamples": false,
|
||||
"pathInMiddlePanel": false,
|
||||
"payloadSampleIdx": 0,
|
||||
|
@ -224,9 +229,13 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
},
|
||||
"backgroundColor": "#fafafa",
|
||||
"groupItems": Object {
|
||||
"activeBackgroundColor": "#e1e1e1",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "uppercase",
|
||||
},
|
||||
"level1Items": Object {
|
||||
"activeBackgroundColor": "#ededed",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "none",
|
||||
},
|
||||
"textColor": "#333333",
|
||||
|
@ -311,6 +320,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"displayType": "string",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"format": undefined,
|
||||
"isCircular": undefined,
|
||||
|
@ -335,8 +345,10 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"jsonSampleExpandLevel": 2,
|
||||
"maxDisplayedEnumValues": undefined,
|
||||
"menuToggle": true,
|
||||
"minCharacterLengthToInitSearch": 3,
|
||||
"nativeScrollbars": false,
|
||||
"noAutoAuth": false,
|
||||
"nonce": undefined,
|
||||
"onlyRequiredInSamples": false,
|
||||
"pathInMiddlePanel": false,
|
||||
"payloadSampleIdx": 0,
|
||||
|
@ -470,9 +482,13 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
},
|
||||
"backgroundColor": "#fafafa",
|
||||
"groupItems": Object {
|
||||
"activeBackgroundColor": "#e1e1e1",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "uppercase",
|
||||
},
|
||||
"level1Items": Object {
|
||||
"activeBackgroundColor": "#ededed",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "none",
|
||||
},
|
||||
"textColor": "#333333",
|
||||
|
@ -557,8 +573,10 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"jsonSampleExpandLevel": 2,
|
||||
"maxDisplayedEnumValues": undefined,
|
||||
"menuToggle": true,
|
||||
"minCharacterLengthToInitSearch": 3,
|
||||
"nativeScrollbars": false,
|
||||
"noAutoAuth": false,
|
||||
"nonce": undefined,
|
||||
"onlyRequiredInSamples": false,
|
||||
"pathInMiddlePanel": false,
|
||||
"payloadSampleIdx": 0,
|
||||
|
@ -692,9 +710,13 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
},
|
||||
"backgroundColor": "#fafafa",
|
||||
"groupItems": Object {
|
||||
"activeBackgroundColor": "#e1e1e1",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "uppercase",
|
||||
},
|
||||
"level1Items": Object {
|
||||
"activeBackgroundColor": "#ededed",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "none",
|
||||
},
|
||||
"textColor": "#333333",
|
||||
|
@ -794,6 +816,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"displayType": "object",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"fields": Array [
|
||||
FieldModel {
|
||||
|
@ -820,6 +843,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"displayType": "string",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"format": undefined,
|
||||
"isCircular": undefined,
|
||||
|
@ -844,8 +868,10 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"jsonSampleExpandLevel": 2,
|
||||
"maxDisplayedEnumValues": undefined,
|
||||
"menuToggle": true,
|
||||
"minCharacterLengthToInitSearch": 3,
|
||||
"nativeScrollbars": false,
|
||||
"noAutoAuth": false,
|
||||
"nonce": undefined,
|
||||
"onlyRequiredInSamples": false,
|
||||
"pathInMiddlePanel": false,
|
||||
"payloadSampleIdx": 0,
|
||||
|
@ -979,9 +1005,13 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
},
|
||||
"backgroundColor": "#fafafa",
|
||||
"groupItems": Object {
|
||||
"activeBackgroundColor": "#e1e1e1",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "uppercase",
|
||||
},
|
||||
"level1Items": Object {
|
||||
"activeBackgroundColor": "#ededed",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "none",
|
||||
},
|
||||
"textColor": "#333333",
|
||||
|
@ -1066,6 +1096,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"displayType": "number",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"format": undefined,
|
||||
"isCircular": undefined,
|
||||
|
@ -1090,8 +1121,10 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"jsonSampleExpandLevel": 2,
|
||||
"maxDisplayedEnumValues": undefined,
|
||||
"menuToggle": true,
|
||||
"minCharacterLengthToInitSearch": 3,
|
||||
"nativeScrollbars": false,
|
||||
"noAutoAuth": false,
|
||||
"nonce": undefined,
|
||||
"onlyRequiredInSamples": false,
|
||||
"pathInMiddlePanel": false,
|
||||
"payloadSampleIdx": 0,
|
||||
|
@ -1225,9 +1258,13 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
},
|
||||
"backgroundColor": "#fafafa",
|
||||
"groupItems": Object {
|
||||
"activeBackgroundColor": "#e1e1e1",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "uppercase",
|
||||
},
|
||||
"level1Items": Object {
|
||||
"activeBackgroundColor": "#ededed",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "none",
|
||||
},
|
||||
"textColor": "#333333",
|
||||
|
@ -1312,8 +1349,10 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"jsonSampleExpandLevel": 2,
|
||||
"maxDisplayedEnumValues": undefined,
|
||||
"menuToggle": true,
|
||||
"minCharacterLengthToInitSearch": 3,
|
||||
"nativeScrollbars": false,
|
||||
"noAutoAuth": false,
|
||||
"nonce": undefined,
|
||||
"onlyRequiredInSamples": false,
|
||||
"pathInMiddlePanel": false,
|
||||
"payloadSampleIdx": 0,
|
||||
|
@ -1447,9 +1486,13 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
},
|
||||
"backgroundColor": "#fafafa",
|
||||
"groupItems": Object {
|
||||
"activeBackgroundColor": "#e1e1e1",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "uppercase",
|
||||
},
|
||||
"level1Items": Object {
|
||||
"activeBackgroundColor": "#ededed",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "none",
|
||||
},
|
||||
"textColor": "#333333",
|
||||
|
@ -1557,8 +1600,10 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"jsonSampleExpandLevel": 2,
|
||||
"maxDisplayedEnumValues": undefined,
|
||||
"menuToggle": true,
|
||||
"minCharacterLengthToInitSearch": 3,
|
||||
"nativeScrollbars": false,
|
||||
"noAutoAuth": false,
|
||||
"nonce": undefined,
|
||||
"onlyRequiredInSamples": false,
|
||||
"pathInMiddlePanel": false,
|
||||
"payloadSampleIdx": 0,
|
||||
|
@ -1692,9 +1737,13 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
},
|
||||
"backgroundColor": "#fafafa",
|
||||
"groupItems": Object {
|
||||
"activeBackgroundColor": "#e1e1e1",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "uppercase",
|
||||
},
|
||||
"level1Items": Object {
|
||||
"activeBackgroundColor": "#ededed",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "none",
|
||||
},
|
||||
"textColor": "#333333",
|
||||
|
@ -1791,6 +1840,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"displayType": "object",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"fields": Array [
|
||||
FieldModel {
|
||||
|
@ -1817,6 +1867,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"displayType": "number",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"format": undefined,
|
||||
"isCircular": undefined,
|
||||
|
@ -1841,8 +1892,10 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"jsonSampleExpandLevel": 2,
|
||||
"maxDisplayedEnumValues": undefined,
|
||||
"menuToggle": true,
|
||||
"minCharacterLengthToInitSearch": 3,
|
||||
"nativeScrollbars": false,
|
||||
"noAutoAuth": false,
|
||||
"nonce": undefined,
|
||||
"onlyRequiredInSamples": false,
|
||||
"pathInMiddlePanel": false,
|
||||
"payloadSampleIdx": 0,
|
||||
|
@ -1976,9 +2029,13 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
},
|
||||
"backgroundColor": "#fafafa",
|
||||
"groupItems": Object {
|
||||
"activeBackgroundColor": "#e1e1e1",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "uppercase",
|
||||
},
|
||||
"level1Items": Object {
|
||||
"activeBackgroundColor": "#ededed",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "none",
|
||||
},
|
||||
"textColor": "#333333",
|
||||
|
@ -2063,6 +2120,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"displayType": "string",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"format": undefined,
|
||||
"isCircular": undefined,
|
||||
|
@ -2087,8 +2145,10 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"jsonSampleExpandLevel": 2,
|
||||
"maxDisplayedEnumValues": undefined,
|
||||
"menuToggle": true,
|
||||
"minCharacterLengthToInitSearch": 3,
|
||||
"nativeScrollbars": false,
|
||||
"noAutoAuth": false,
|
||||
"nonce": undefined,
|
||||
"onlyRequiredInSamples": false,
|
||||
"pathInMiddlePanel": false,
|
||||
"payloadSampleIdx": 0,
|
||||
|
@ -2222,9 +2282,13 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
},
|
||||
"backgroundColor": "#fafafa",
|
||||
"groupItems": Object {
|
||||
"activeBackgroundColor": "#e1e1e1",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "uppercase",
|
||||
},
|
||||
"level1Items": Object {
|
||||
"activeBackgroundColor": "#ededed",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "none",
|
||||
},
|
||||
"textColor": "#333333",
|
||||
|
@ -2309,8 +2373,10 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"jsonSampleExpandLevel": 2,
|
||||
"maxDisplayedEnumValues": undefined,
|
||||
"menuToggle": true,
|
||||
"minCharacterLengthToInitSearch": 3,
|
||||
"nativeScrollbars": false,
|
||||
"noAutoAuth": false,
|
||||
"nonce": undefined,
|
||||
"onlyRequiredInSamples": false,
|
||||
"pathInMiddlePanel": false,
|
||||
"payloadSampleIdx": 0,
|
||||
|
@ -2444,9 +2510,13 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
},
|
||||
"backgroundColor": "#fafafa",
|
||||
"groupItems": Object {
|
||||
"activeBackgroundColor": "#e1e1e1",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "uppercase",
|
||||
},
|
||||
"level1Items": Object {
|
||||
"activeBackgroundColor": "#ededed",
|
||||
"activeTextColor": "#32329f",
|
||||
"textTransform": "none",
|
||||
},
|
||||
"textColor": "#333333",
|
||||
|
@ -2567,6 +2637,7 @@ exports[`Components SchemaView discriminator should correctly render discriminat
|
|||
"displayType": "number",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"format": undefined,
|
||||
"isCircular": undefined,
|
||||
|
@ -2623,6 +2694,7 @@ exports[`Components SchemaView discriminator should correctly render discriminat
|
|||
"displayType": "string",
|
||||
"enum": Array [],
|
||||
"example": undefined,
|
||||
"examples": undefined,
|
||||
"externalDocs": undefined,
|
||||
"format": undefined,
|
||||
"isCircular": undefined,
|
||||
|
|
|
@ -33,10 +33,10 @@ exports[`Components SchemaView OneOf deprecated should match snapshot 1`] = `
|
|||
<div>
|
||||
<div>
|
||||
<span
|
||||
class="sc-fbIWvP sc-FRrlG CMpTe bBFKjV"
|
||||
class="sc-laZMeE sc-iNiQyp jWaWWE jrLlAa"
|
||||
/>
|
||||
<span
|
||||
class="sc-fbIWvP sc-fXazdy CMpTe gJKPGC"
|
||||
class="sc-laZMeE sc-jffHpj jWaWWE cThoNa"
|
||||
>
|
||||
string
|
||||
</span>
|
||||
|
@ -44,7 +44,7 @@ exports[`Components SchemaView OneOf deprecated should match snapshot 1`] = `
|
|||
|
||||
<div>
|
||||
<div
|
||||
class="sc-iBzEeX sc-cOifOu dFWqin hjSJYo"
|
||||
class="sc-iJCRrE sc-ciSkZP jCdxGr emlfPd"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@ export {
|
|||
Row,
|
||||
RightPanel,
|
||||
Section,
|
||||
StyledDropdown,
|
||||
Dropdown,
|
||||
SimpleDropdown,
|
||||
} from './common-elements/';
|
||||
export type { DropdownOption } from './common-elements';
|
||||
|
|
|
@ -101,7 +101,9 @@ export class MarkdownRenderer {
|
|||
|
||||
attachHeadingsDescriptions(rawText: string) {
|
||||
const buildRegexp = (heading: MarkdownHeading) => {
|
||||
return new RegExp(`##?\\s+${heading.name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}\s*\n`);
|
||||
return new RegExp(
|
||||
`##?\\s+${heading.name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}\s*(\n|\r\n)`,
|
||||
);
|
||||
};
|
||||
|
||||
const flatHeadings = this.flattenHeadings(this.headings);
|
||||
|
|
|
@ -5,7 +5,7 @@ import { SpecStore } from './models';
|
|||
import { history as historyInst, HistoryService } from './HistoryService';
|
||||
import { ScrollService } from './ScrollService';
|
||||
|
||||
import { flattenByProp, SECURITY_SCHEMES_SECTION_PREFIX } from '../utils';
|
||||
import { escapeHTMLAttrChars, flattenByProp, SECURITY_SCHEMES_SECTION_PREFIX } from '../utils';
|
||||
import { GROUP_DEPTH } from './MenuBuilder';
|
||||
|
||||
export type MenuItemGroupType = 'group' | 'tag' | 'section';
|
||||
|
@ -48,7 +48,7 @@ export class MenuStore {
|
|||
if (!id) {
|
||||
return;
|
||||
}
|
||||
scroll.scrollIntoViewBySelector(`[${SECTION_ATTR}="${id}"]`);
|
||||
scroll.scrollIntoViewBySelector(`[${SECTION_ATTR}="${escapeHTMLAttrChars(id)}"]`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -154,7 +154,7 @@ export class MenuStore {
|
|||
item = this.flatItems.find(i => SECURITY_SCHEMES_SECTION_PREFIX.startsWith(i.id));
|
||||
this.activateAndScroll(item, false);
|
||||
}
|
||||
this.scroll.scrollIntoViewBySelector(`[${SECTION_ATTR}="${id}"]`);
|
||||
this.scroll.scrollIntoViewBySelector(`[${SECTION_ATTR}="${escapeHTMLAttrChars(id)}"]`);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -164,7 +164,7 @@ export class MenuStore {
|
|||
*/
|
||||
getElementAt(idx: number): Element | null {
|
||||
const item = this.flatItems[idx];
|
||||
return (item && querySelector(`[${SECTION_ATTR}="${item.id}"]`)) || null;
|
||||
return (item && querySelector(`[${SECTION_ATTR}="${escapeHTMLAttrChars(item.id)}"]`)) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,7 +176,7 @@ export class MenuStore {
|
|||
if (item && item.type === 'group') {
|
||||
item = item.items[0];
|
||||
}
|
||||
return (item && querySelector(`[${SECTION_ATTR}="${item.id}"]`)) || null;
|
||||
return (item && querySelector(`[${SECTION_ATTR}="${escapeHTMLAttrChars(item.id)}"]`)) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -225,7 +225,7 @@ export class MenuStore {
|
|||
|
||||
this.activeItemIdx = item.absoluteIdx!;
|
||||
if (updateLocation) {
|
||||
this.history.replace(item.id, rewriteHistory);
|
||||
this.history.replace(encodeURI(item.id), rewriteHistory);
|
||||
}
|
||||
|
||||
item.activate();
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { resolve as urlResolve } from 'url';
|
||||
|
||||
import { OpenAPIRef, OpenAPISchema, OpenAPISpec, Referenced } from '../types';
|
||||
|
||||
import { appendToMdHeading, IS_BROWSER } from '../utils/';
|
||||
import { appendToMdHeading, isArray, IS_BROWSER } from '../utils/';
|
||||
import { JsonPointer } from '../utils/JsonPointer';
|
||||
import {
|
||||
getDefinitionName,
|
||||
|
@ -62,7 +60,7 @@ export class OpenAPIParser {
|
|||
|
||||
const href = IS_BROWSER ? window.location.href : '';
|
||||
if (typeof specUrl === 'string') {
|
||||
this.specUrl = urlResolve(href, specUrl);
|
||||
this.specUrl = new URL(specUrl, href).href;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,7 +363,7 @@ export class OpenAPIParser {
|
|||
const allOf = schema.allOf;
|
||||
for (let i = 0; i < allOf.length; i++) {
|
||||
const sub = allOf[i];
|
||||
if (Array.isArray(sub.oneOf)) {
|
||||
if (isArray(sub.oneOf)) {
|
||||
const beforeAllOf = allOf.slice(0, i);
|
||||
const afterAllOf = allOf.slice(i + 1);
|
||||
return {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import defaultTheme, { ResolvedThemeInterface, resolveTheme, ThemeInterface } from '../theme';
|
||||
import { querySelector } from '../utils/dom';
|
||||
import { isNumeric, mergeObjects } from '../utils/helpers';
|
||||
import { isArray, isNumeric, mergeObjects } from '../utils/helpers';
|
||||
|
||||
import { LabelsConfigRaw, setRedocLabels } from './Labels';
|
||||
import { MDXComponentMeta } from './MarkdownRenderer';
|
||||
|
@ -8,6 +8,7 @@ import { MDXComponentMeta } from './MarkdownRenderer';
|
|||
export enum SideNavStyleEnum {
|
||||
SummaryOnly = 'summary-only',
|
||||
PathOnly = 'path-only',
|
||||
IdOnly = 'id-only',
|
||||
}
|
||||
|
||||
export interface RedocRawOptions {
|
||||
|
@ -54,7 +55,9 @@ export interface RedocRawOptions {
|
|||
ignoreNamedSchemas?: string[] | string;
|
||||
hideSchemaPattern?: boolean;
|
||||
generatedPayloadSamplesMaxDepth?: number;
|
||||
nonce?: string;
|
||||
hideFab?: boolean;
|
||||
minCharacterLengthToInitSearch?: number;
|
||||
}
|
||||
|
||||
export function argValueToBoolean(val?: string | boolean, defaultValue?: boolean): boolean {
|
||||
|
@ -171,6 +174,8 @@ export class RedocNormalizedOptions {
|
|||
return value;
|
||||
case SideNavStyleEnum.PathOnly:
|
||||
return SideNavStyleEnum.PathOnly;
|
||||
case SideNavStyleEnum.IdOnly:
|
||||
return SideNavStyleEnum.IdOnly;
|
||||
default:
|
||||
return defaultValue;
|
||||
}
|
||||
|
@ -257,6 +262,9 @@ export class RedocNormalizedOptions {
|
|||
hideSchemaPattern: boolean;
|
||||
generatedPayloadSamplesMaxDepth: number;
|
||||
hideFab: boolean;
|
||||
minCharacterLengthToInitSearch: number;
|
||||
|
||||
nonce?: string;
|
||||
|
||||
constructor(raw: RedocRawOptions, defaults: RedocRawOptions = {}) {
|
||||
raw = { ...defaults, ...raw };
|
||||
|
@ -319,7 +327,7 @@ export class RedocNormalizedOptions {
|
|||
|
||||
this.expandDefaultServerVariables = argValueToBoolean(raw.expandDefaultServerVariables);
|
||||
this.maxDisplayedEnumValues = argValueToNumber(raw.maxDisplayedEnumValues);
|
||||
const ignoreNamedSchemas = Array.isArray(raw.ignoreNamedSchemas)
|
||||
const ignoreNamedSchemas = isArray(raw.ignoreNamedSchemas)
|
||||
? raw.ignoreNamedSchemas
|
||||
: raw.ignoreNamedSchemas?.split(',').map(s => s.trim());
|
||||
this.ignoreNamedSchemas = new Set(ignoreNamedSchemas);
|
||||
|
@ -328,6 +336,8 @@ export class RedocNormalizedOptions {
|
|||
RedocNormalizedOptions.normalizeGeneratedPayloadSamplesMaxDepth(
|
||||
raw.generatedPayloadSamplesMaxDepth,
|
||||
);
|
||||
this.nonce = raw.nonce;
|
||||
this.hideFab = argValueToBoolean(raw.hideFab);
|
||||
this.minCharacterLengthToInitSearch = argValueToNumber(raw.minCharacterLengthToInitSearch) || 3;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "Schema definition with unevaluatedProperties",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "example.com"
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Test": {
|
||||
"type": "object",
|
||||
"unevaluatedProperties": true,
|
||||
"properties": {
|
||||
"$ref": "#/components/schemas/Cat"
|
||||
}
|
||||
},
|
||||
"Test2": {
|
||||
"type": "object",
|
||||
"unevaluatedProperties": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Cat"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Dog"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Test3": {
|
||||
"type": "object",
|
||||
"unevaluatedProperties": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"properties": {
|
||||
"$ref": "#/components/schemas/Cat"
|
||||
}
|
||||
},
|
||||
"Cat": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,5 +48,33 @@ describe('Models', () => {
|
|||
expect(schema.fields).toHaveLength(1);
|
||||
expect(schema.pointer).toBe('#/components/schemas/Child');
|
||||
});
|
||||
|
||||
test('schemaDefinition should resolve unevaluatedProperties in properties', () => {
|
||||
const spec = require('../fixtures/3.1/unevaluatedProperties.json');
|
||||
parser = new OpenAPIParser(spec, undefined, opts);
|
||||
const schema = new SchemaModel(parser, spec.components.schemas.Test, '', opts);
|
||||
expect(schema.fields).toHaveLength(2);
|
||||
expect(schema.fields![1].kind).toEqual('additionalProperties');
|
||||
expect(schema.fields![1].schema.type).toEqual('any');
|
||||
});
|
||||
|
||||
test('schemaDefinition should resolve unevaluatedProperties in anyOf', () => {
|
||||
const spec = require('../fixtures/3.1/unevaluatedProperties.json');
|
||||
parser = new OpenAPIParser(spec, undefined, opts);
|
||||
const schema = new SchemaModel(parser, spec.components.schemas.Test2, '', opts);
|
||||
expect(schema.oneOf![0].fields).toHaveLength(2);
|
||||
expect(schema.oneOf![0].fields![1].kind).toEqual('additionalProperties');
|
||||
expect(schema.oneOf![1].fields).toHaveLength(2);
|
||||
expect(schema.oneOf![1].fields![1].kind).toEqual('additionalProperties');
|
||||
});
|
||||
|
||||
test('schemaDefinition should resolve unevaluatedProperties type boolean', () => {
|
||||
const spec = require('../fixtures/3.1/unevaluatedProperties.json');
|
||||
parser = new OpenAPIParser(spec, undefined, opts);
|
||||
const schema = new SchemaModel(parser, spec.components.schemas.Test3, '', opts);
|
||||
expect(schema.fields).toHaveLength(2);
|
||||
expect(schema.fields![1].kind).toEqual('additionalProperties');
|
||||
expect(schema.fields![1].schema.type).toEqual('boolean');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { resolve as urlResolve } from 'url';
|
||||
|
||||
import { OpenAPIEncoding, OpenAPIExample, Referenced } from '../../types';
|
||||
import { isFormUrlEncoded, isJsonLike, urlFormEncodePayload } from '../../utils/openapi';
|
||||
import { OpenAPIParser } from '../OpenAPIParser';
|
||||
|
@ -23,7 +21,7 @@ export class ExampleModel {
|
|||
this.summary = example.summary;
|
||||
this.description = example.description;
|
||||
if (example.externalValue) {
|
||||
this.externalValueUrl = urlResolve(parser.specUrl || '', example.externalValue);
|
||||
this.externalValueUrl = new URL(example.externalValue, parser.specUrl || '').href;
|
||||
}
|
||||
parser.exitRef(infoOrRef);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { extractExtensions } from '../../utils/openapi';
|
|||
import { OpenAPIParser } from '../OpenAPIParser';
|
||||
import { SchemaModel } from './Schema';
|
||||
import { ExampleModel } from './Example';
|
||||
import { mapValues } from '../../utils/helpers';
|
||||
import { isArray, mapValues } from '../../utils/helpers';
|
||||
|
||||
const DEFAULT_SERIALIZATION: Record<
|
||||
OpenAPIParameterLocation,
|
||||
|
@ -48,7 +48,7 @@ export class FieldModel {
|
|||
required: boolean;
|
||||
description: string;
|
||||
example?: string;
|
||||
examples?: Record<string, ExampleModel>;
|
||||
examples?: Record<string, ExampleModel> | any[];
|
||||
deprecated: boolean;
|
||||
in?: OpenAPIParameterLocation;
|
||||
kind: string;
|
||||
|
@ -85,9 +85,12 @@ export class FieldModel {
|
|||
info.description === undefined ? this.schema.description || '' : info.description;
|
||||
this.example = info.example || this.schema.example;
|
||||
|
||||
if (info.examples !== undefined) {
|
||||
this.examples = mapValues(
|
||||
info.examples,
|
||||
if (info.examples !== undefined || this.schema.examples !== undefined) {
|
||||
const exampleValue = info.examples || this.schema.examples;
|
||||
this.examples = isArray(exampleValue)
|
||||
? exampleValue
|
||||
: mapValues(
|
||||
exampleValue!,
|
||||
(example, name) => new ExampleModel(parser, example, name, info.encoding),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ export class OperationModel implements IMenuItem {
|
|||
|
||||
pointer: string;
|
||||
operationId?: string;
|
||||
operationHash?: string;
|
||||
httpVerb: string;
|
||||
deprecated: boolean;
|
||||
path: string;
|
||||
|
@ -107,7 +108,12 @@ export class OperationModel implements IMenuItem {
|
|||
|
||||
this.name = getOperationSummary(operationSpec);
|
||||
|
||||
this.sidebarLabel = options.sideNavStyle === SideNavStyleEnum.PathOnly ? this.path : this.name;
|
||||
this.sidebarLabel =
|
||||
options.sideNavStyle === SideNavStyleEnum.IdOnly
|
||||
? this.operationId || this.path
|
||||
: options.sideNavStyle === SideNavStyleEnum.PathOnly
|
||||
? this.path
|
||||
: this.name;
|
||||
|
||||
if (this.isCallback) {
|
||||
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
|
||||
|
@ -119,9 +125,10 @@ export class OperationModel implements IMenuItem {
|
|||
// TODO: update getting pathInfo for overriding servers on path level
|
||||
this.servers = normalizeServers('', operationSpec.servers || operationSpec.pathServers || []);
|
||||
} else {
|
||||
this.operationHash = operationSpec.operationId && 'operation/' + operationSpec.operationId;
|
||||
this.id =
|
||||
operationSpec.operationId !== undefined
|
||||
? 'operation/' + operationSpec.operationId
|
||||
? (parent ? parent.id + '/' : '') + this.operationHash
|
||||
: parent !== undefined
|
||||
? parent.id + this.pointer
|
||||
: this.pointer;
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
detectType,
|
||||
extractExtensions,
|
||||
humanizeConstraints,
|
||||
isArray,
|
||||
isNamedDefinition,
|
||||
isPrimitiveType,
|
||||
JsonPointer,
|
||||
|
@ -41,6 +42,7 @@ export class SchemaModel {
|
|||
deprecated: boolean;
|
||||
pattern?: string;
|
||||
example?: any;
|
||||
examples?: any[];
|
||||
enum: any[];
|
||||
default?: any;
|
||||
readOnly: boolean;
|
||||
|
@ -103,7 +105,7 @@ export class SchemaModel {
|
|||
}
|
||||
|
||||
hasType(type: string) {
|
||||
return this.type === type || (Array.isArray(this.type) && this.type.includes(type));
|
||||
return this.type === type || (isArray(this.type) && this.type.includes(type));
|
||||
}
|
||||
|
||||
init(parser: OpenAPIParser, isChild: boolean) {
|
||||
|
@ -117,6 +119,7 @@ export class SchemaModel {
|
|||
this.format = schema.format;
|
||||
this.enum = schema.enum || [];
|
||||
this.example = schema.example;
|
||||
this.examples = schema.examples;
|
||||
this.deprecated = !!schema.deprecated;
|
||||
this.pattern = schema.pattern;
|
||||
this.externalDocs = schema.externalDocs;
|
||||
|
@ -134,17 +137,14 @@ export class SchemaModel {
|
|||
this.maxItems = schema.maxItems;
|
||||
|
||||
if (!!schema.nullable || schema['x-nullable']) {
|
||||
if (
|
||||
Array.isArray(this.type) &&
|
||||
!this.type.some(value => value === null || value === 'null')
|
||||
) {
|
||||
if (isArray(this.type) && !this.type.some(value => value === null || value === 'null')) {
|
||||
this.type = [...this.type, 'null'];
|
||||
} else if (!Array.isArray(this.type) && (this.type !== null || this.type !== 'null')) {
|
||||
} else if (!isArray(this.type) && (this.type !== null || this.type !== 'null')) {
|
||||
this.type = [this.type, 'null'];
|
||||
}
|
||||
}
|
||||
|
||||
this.displayType = Array.isArray(this.type)
|
||||
this.displayType = isArray(this.type)
|
||||
? this.type.map(item => (item === null ? 'null' : item)).join(' or ')
|
||||
: this.type;
|
||||
|
||||
|
@ -157,7 +157,7 @@ export class SchemaModel {
|
|||
return;
|
||||
} else if (
|
||||
isChild &&
|
||||
Array.isArray(schema.oneOf) &&
|
||||
isArray(schema.oneOf) &&
|
||||
schema.oneOf.find(s => s.$ref === this.pointer)
|
||||
) {
|
||||
// we hit allOf of the schema with the parent discriminator
|
||||
|
@ -196,7 +196,7 @@ export class SchemaModel {
|
|||
if (this.items.isPrimitive) {
|
||||
this.enum = this.items.enum;
|
||||
}
|
||||
if (Array.isArray(this.type)) {
|
||||
if (isArray(this.type)) {
|
||||
const filteredType = this.type.filter(item => item !== 'array');
|
||||
if (filteredType.length) this.displayType += ` or ${filteredType.join(' or ')}`;
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ export class SchemaModel {
|
|||
for (const name in mapping) {
|
||||
const $ref = mapping[name];
|
||||
|
||||
if (Array.isArray(explicitInversedMapping[$ref])) {
|
||||
if (isArray(explicitInversedMapping[$ref])) {
|
||||
explicitInversedMapping[$ref].push(name);
|
||||
} else {
|
||||
// overrides implicit mapping here
|
||||
|
@ -311,7 +311,7 @@ export class SchemaModel {
|
|||
|
||||
for (const $ref of Object.keys(inversedMapping)) {
|
||||
const names = inversedMapping[$ref];
|
||||
if (Array.isArray(names)) {
|
||||
if (isArray(names)) {
|
||||
for (const name of names) {
|
||||
refs.push({ $ref, name });
|
||||
}
|
||||
|
@ -364,7 +364,7 @@ function buildFields(
|
|||
options: RedocNormalizedOptions,
|
||||
): FieldModel[] {
|
||||
const props = schema.properties || {};
|
||||
const additionalProps = schema.additionalProperties;
|
||||
const additionalProps = schema.additionalProperties || schema.unevaluatedProperties;
|
||||
const defaults = schema.default;
|
||||
let fields = Object.keys(props || []).map(fieldName => {
|
||||
let field = props[fieldName];
|
||||
|
|
|
@ -139,9 +139,13 @@ const defaultTheme: ThemeInterface = {
|
|||
? theme.sidebar.textColor
|
||||
: theme.colors.primary.main,
|
||||
groupItems: {
|
||||
activeBackgroundColor: theme => darken(0.1, theme.sidebar.backgroundColor),
|
||||
activeTextColor: theme => theme.sidebar.activeTextColor,
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
level1Items: {
|
||||
activeBackgroundColor: theme => darken(0.05, theme.sidebar.backgroundColor),
|
||||
activeTextColor: theme => theme.sidebar.activeTextColor,
|
||||
textTransform: 'none',
|
||||
},
|
||||
arrow: {
|
||||
|
@ -319,9 +323,13 @@ export interface ResolvedThemeInterface {
|
|||
textColor: string;
|
||||
activeTextColor: string;
|
||||
groupItems: {
|
||||
activeBackgroundColor: string;
|
||||
activeTextColor: string;
|
||||
textTransform: string;
|
||||
};
|
||||
level1Items: {
|
||||
activeBackgroundColor: string;
|
||||
activeTextColor: string;
|
||||
textTransform: string;
|
||||
};
|
||||
arrow: {
|
||||
|
|
|
@ -114,6 +114,7 @@ export interface OpenAPISchema {
|
|||
type?: string | string[];
|
||||
properties?: { [name: string]: OpenAPISchema };
|
||||
additionalProperties?: boolean | OpenAPISchema;
|
||||
unevaluatedProperties?: boolean | OpenAPISchema;
|
||||
description?: string;
|
||||
default?: any;
|
||||
items?: OpenAPISchema;
|
||||
|
@ -146,6 +147,7 @@ export interface OpenAPISchema {
|
|||
minProperties?: number;
|
||||
enum?: any[];
|
||||
example?: any;
|
||||
examples?: any[];
|
||||
const?: string;
|
||||
contentEncoding?: string;
|
||||
contentMediaType?: string;
|
||||
|
|
|
@ -2330,6 +2330,20 @@ and standard method from web, mobile and desktop applications.
|
|||
"openapi": "3.1.0",
|
||||
"paths": Object {
|
||||
"/pet": Object {
|
||||
"delete": Object {
|
||||
"operationId": "deletePetBy\\"Id",
|
||||
"summary": "OperationId with quotes",
|
||||
"tags": Array [
|
||||
"pet",
|
||||
],
|
||||
},
|
||||
"get": Object {
|
||||
"operationId": "delete\\\\PetById",
|
||||
"summary": "OperationId with backslash",
|
||||
"tags": Array [
|
||||
"pet",
|
||||
],
|
||||
},
|
||||
"parameters": Array [
|
||||
Object {
|
||||
"description": "The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US",
|
||||
|
@ -2762,6 +2776,10 @@ try {
|
|||
"application/json": Object {
|
||||
"schema": Object {
|
||||
"$ref": "#/components/schemas/ApiResponse",
|
||||
"unevaluatedProperties": Object {
|
||||
"format": "int32",
|
||||
"type": "integer",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -146,7 +146,14 @@ describe('Utils', () => {
|
|||
string: ['pattern', 'minLength', 'maxLength'],
|
||||
|
||||
array: ['items', 'maxItems', 'minItems', 'uniqueItems'],
|
||||
object: ['maxProperties', 'minProperties', 'required', 'additionalProperties', 'properties'],
|
||||
object: [
|
||||
'maxProperties',
|
||||
'minProperties',
|
||||
'required',
|
||||
'additionalProperties',
|
||||
'unevaluatedProperties',
|
||||
'properties',
|
||||
],
|
||||
};
|
||||
|
||||
Object.keys(tests).forEach(name => {
|
||||
|
@ -212,6 +219,17 @@ describe('Utils', () => {
|
|||
expect(isPrimitiveType(schema)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false for array contains array type and schema has items (unevaluatedProperties)', () => {
|
||||
const schema = {
|
||||
type: ['array'],
|
||||
items: {
|
||||
type: 'object',
|
||||
unevaluatedProperties: true,
|
||||
},
|
||||
};
|
||||
expect(isPrimitiveType(schema)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false for array contains object and array types and schema has items', () => {
|
||||
const schema = {
|
||||
type: ['array', 'object'],
|
||||
|
@ -223,6 +241,17 @@ describe('Utils', () => {
|
|||
expect(isPrimitiveType(schema)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false for array contains object and array types and schema has items (unevaluatedProperties)', () => {
|
||||
const schema = {
|
||||
type: ['array', 'object'],
|
||||
items: {
|
||||
type: 'object',
|
||||
unevaluatedProperties: true,
|
||||
},
|
||||
};
|
||||
expect(isPrimitiveType(schema)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false for array contains object and array types and schema has properties', () => {
|
||||
const schema = {
|
||||
type: ['array', 'object'],
|
||||
|
@ -281,6 +310,17 @@ describe('Utils', () => {
|
|||
expect(isPrimitiveType(schema)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false for object with unevaluatedProperties', () => {
|
||||
const schema = {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
unevaluatedProperties: true,
|
||||
},
|
||||
};
|
||||
expect(isPrimitiveType(schema)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should work with externally provided type', () => {
|
||||
const schema = {
|
||||
properties: {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import slugify from 'slugify';
|
||||
import { format, parse } from 'url';
|
||||
|
||||
/**
|
||||
* Maps over array passing `isLast` bool to iterator as the second argument
|
||||
|
@ -113,7 +112,7 @@ const isObject = (item: any): boolean => {
|
|||
};
|
||||
|
||||
const isMergebleObject = (item): boolean => {
|
||||
return isObject(item) && !Array.isArray(item);
|
||||
return isObject(item) && !isArray(item);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -146,18 +145,23 @@ export function isAbsoluteUrl(url: string) {
|
|||
export function resolveUrl(url: string, to: string) {
|
||||
let res;
|
||||
if (to.startsWith('//')) {
|
||||
const { protocol: specProtocol } = parse(url);
|
||||
res = `${specProtocol || 'https:'}${to}`;
|
||||
try {
|
||||
res = `${new URL(url).protocol || 'https:'}${to}`;
|
||||
} catch {
|
||||
res = `https:${to}`;
|
||||
}
|
||||
} else if (isAbsoluteUrl(to)) {
|
||||
res = to;
|
||||
} else if (!to.startsWith('/')) {
|
||||
res = stripTrailingSlash(url) + '/' + to;
|
||||
} else {
|
||||
const urlObj = parse(url);
|
||||
res = format({
|
||||
...urlObj,
|
||||
pathname: to,
|
||||
});
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
urlObj.pathname = to;
|
||||
res = urlObj.href;
|
||||
} catch {
|
||||
res = to;
|
||||
}
|
||||
}
|
||||
return stripTrailingSlash(res);
|
||||
}
|
||||
|
@ -195,8 +199,17 @@ function parseURL(url: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export function escapeHTMLAttrChars(str: string): string {
|
||||
return str.replace(/["\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
export function unescapeHTMLChars(str: string): string {
|
||||
return str
|
||||
.replace(/&#(\d+);/g, (_m, code) => String.fromCharCode(parseInt(code, 10)))
|
||||
.replace(/&/g, '&');
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
export function isArray(value: unknown): value is Array<any> {
|
||||
return Array.isArray(value);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import type { Source, Document } from '@redocly/openapi-core';
|
||||
// eslint-disable-next-line import/no-internal-modules
|
||||
import type { ResolvedConfig } from '@redocly/openapi-core/lib/config';
|
||||
|
||||
// eslint-disable-next-line import/no-internal-modules
|
||||
import { bundle } from '@redocly/openapi-core/lib/bundle';
|
||||
|
@ -11,7 +13,7 @@ import { OpenAPISpec } from '../types';
|
|||
import { IS_BROWSER } from './dom';
|
||||
|
||||
export async function loadAndBundleSpec(specUrlOrObject: object | string): Promise<OpenAPISpec> {
|
||||
const config = new Config({});
|
||||
const config = new Config({} as ResolvedConfig);
|
||||
const bundleOpts = {
|
||||
config,
|
||||
base: IS_BROWSER ? window.location.href : process.cwd(),
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
Referenced,
|
||||
} from '../types';
|
||||
import { IS_BROWSER } from './dom';
|
||||
import { isNumeric, removeQueryString, resolveUrl } from './helpers';
|
||||
import { isNumeric, removeQueryString, resolveUrl, isArray } from './helpers';
|
||||
|
||||
function isWildcardStatusCode(statusCode: string | number): statusCode is string {
|
||||
return typeof statusCode === 'string' && /\dxx/i.test(statusCode);
|
||||
|
@ -97,11 +97,12 @@ const schemaKeywordTypes = {
|
|||
minProperties: 'object',
|
||||
required: 'object',
|
||||
additionalProperties: 'object',
|
||||
unevaluatedProperties: 'object',
|
||||
properties: 'object',
|
||||
};
|
||||
|
||||
export function detectType(schema: OpenAPISchema): string {
|
||||
if (schema.type !== undefined && !Array.isArray(schema.type)) {
|
||||
if (schema.type !== undefined && !isArray(schema.type)) {
|
||||
return schema.type;
|
||||
}
|
||||
const keywords = Object.keys(schemaKeywordTypes);
|
||||
|
@ -124,16 +125,19 @@ export function isPrimitiveType(
|
|||
}
|
||||
|
||||
let isPrimitive = true;
|
||||
const isArray = Array.isArray(type);
|
||||
const isArrayType = isArray(type);
|
||||
|
||||
if (type === 'object' || (isArray && type?.includes('object'))) {
|
||||
if (type === 'object' || (isArrayType && type?.includes('object'))) {
|
||||
isPrimitive =
|
||||
schema.properties !== undefined
|
||||
? Object.keys(schema.properties).length === 0
|
||||
: schema.additionalProperties === undefined;
|
||||
: schema.additionalProperties === undefined && schema.unevaluatedProperties === undefined;
|
||||
}
|
||||
|
||||
if (schema.items !== undefined && (type === 'array' || (isArray && type?.includes('array')))) {
|
||||
if (
|
||||
schema.items !== undefined &&
|
||||
(type === 'array' || (isArrayType && type?.includes('array')))
|
||||
) {
|
||||
isPrimitive = isPrimitiveType(schema.items, schema.items.type);
|
||||
}
|
||||
|
||||
|
@ -149,7 +153,7 @@ export function isFormUrlEncoded(contentType: string): boolean {
|
|||
}
|
||||
|
||||
function delimitedEncodeField(fieldVal: any, fieldName: string, delimiter: string): string {
|
||||
if (Array.isArray(fieldVal)) {
|
||||
if (isArray(fieldVal)) {
|
||||
return fieldVal.map(v => v.toString()).join(delimiter);
|
||||
} else if (typeof fieldVal === 'object') {
|
||||
return Object.keys(fieldVal)
|
||||
|
@ -161,7 +165,7 @@ function delimitedEncodeField(fieldVal: any, fieldName: string, delimiter: strin
|
|||
}
|
||||
|
||||
function deepObjectEncodeField(fieldVal: any, fieldName: string): string {
|
||||
if (Array.isArray(fieldVal)) {
|
||||
if (isArray(fieldVal)) {
|
||||
console.warn('deepObject style cannot be used with array value:' + fieldVal.toString());
|
||||
return '';
|
||||
} else if (typeof fieldVal === 'object') {
|
||||
|
@ -194,7 +198,7 @@ export function urlFormEncodePayload(
|
|||
payload: object,
|
||||
encoding: { [field: string]: OpenAPIEncoding } = {},
|
||||
) {
|
||||
if (Array.isArray(payload)) {
|
||||
if (isArray(payload)) {
|
||||
throw new Error('Payload must have fields: ' + payload.toString());
|
||||
} else {
|
||||
return Object.keys(payload)
|
||||
|
@ -253,7 +257,7 @@ function serializeQueryParameter(
|
|||
case 'form':
|
||||
return serializeFormValue(name, explode, value);
|
||||
case 'spaceDelimited':
|
||||
if (!Array.isArray(value)) {
|
||||
if (!isArray(value)) {
|
||||
console.warn('The style spaceDelimited is only applicable to arrays');
|
||||
return '';
|
||||
}
|
||||
|
@ -263,7 +267,7 @@ function serializeQueryParameter(
|
|||
|
||||
return `${name}=${value.join('%20')}`;
|
||||
case 'pipeDelimited':
|
||||
if (!Array.isArray(value)) {
|
||||
if (!isArray(value)) {
|
||||
console.warn('The style pipeDelimited is only applicable to arrays');
|
||||
return '';
|
||||
}
|
||||
|
@ -273,7 +277,7 @@ function serializeQueryParameter(
|
|||
|
||||
return `${name}=${value.join('|')}`;
|
||||
case 'deepObject':
|
||||
if (!explode || Array.isArray(value) || typeof value !== 'object') {
|
||||
if (!explode || isArray(value) || typeof value !== 'object') {
|
||||
console.warn('The style deepObject is only applicable for objects with explode=true');
|
||||
return '';
|
||||
}
|
||||
|
@ -329,7 +333,7 @@ export function serializeParameterValueWithMime(value: any, mime: string): strin
|
|||
}
|
||||
|
||||
export function serializeParameterValue(
|
||||
parameter: OpenAPIParameter & { serializationMime?: string },
|
||||
parameter: (OpenAPIParameter & { serializationMime?: string }) | FieldModel,
|
||||
value: any,
|
||||
): string {
|
||||
const { name, style, explode = false, serializationMime } = parameter;
|
||||
|
|
|
@ -8,6 +8,7 @@ const nodeExternals = require('webpack-node-externals')({
|
|||
// bundle in modules that need transpiling + non-js (e.g. css)
|
||||
allowlist: [
|
||||
'swagger2openapi',
|
||||
'marked',
|
||||
/reftools/,
|
||||
'oas-resolver',
|
||||
'oas-kit-common',
|
||||
|
@ -65,11 +66,12 @@ export default (env: { standalone?: boolean; browser?: boolean } = {}) => ({
|
|||
'node-fetch': 'null',
|
||||
'node-fetch-h2': 'null',
|
||||
yaml: 'null',
|
||||
url: 'null',
|
||||
'safe-json-stringify': 'null',
|
||||
}
|
||||
: (context, request, callback) => {
|
||||
// ignore node-fetch dep of swagger2openapi as it is not used
|
||||
if (/esprima|node-fetch|node-fetch-h2|\/yaml|safe-json-stringify$/i.test(request)) {
|
||||
if (/esprima|node-fetch|node-fetch-h2|\/yaml|safe-json-stringify|url$/i.test(request)) {
|
||||
return callback(null, 'var undefined');
|
||||
}
|
||||
return nodeExternals(context, request, callback);
|
||||
|
|
Loading…
Reference in New Issue
Block a user