mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-07 13:44:54 +03:00
merge master
This commit is contained in:
commit
12a2ce9b6d
75
.github/workflows/publish-cli.yml
vendored
75
.github/workflows/publish-cli.yml
vendored
|
@ -2,51 +2,56 @@ name: Publish cli
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
branches:
|
||||||
- v[0-9]*.[0-9]*.[0-9]*
|
- master
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
bundle:
|
bundle:
|
||||||
|
needs: [check-version-cli]
|
||||||
|
if: needs.check-version-cli.outputs.changed == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
- name: Cache node modules
|
- name: Cache node modules
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
||||||
key: npm-${{ hashFiles('package-lock.json') }}
|
key: npm-${{ hashFiles('package-lock.json') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
npm-${{ hashFiles('package-lock.json') }}
|
npm-${{ hashFiles('package-lock.json') }}
|
||||||
npm-
|
npm-
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run bundle
|
- run: npm run bundle
|
||||||
- name: Store bundle artifact
|
- name: Store bundle artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: bundles-cli
|
name: bundles-cli
|
||||||
path: bundles
|
path: bundles
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
unit-tests:
|
unit-tests:
|
||||||
|
needs: [check-version-cli]
|
||||||
|
if: needs.check-version-cli.outputs.changed == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
e2e-tests:
|
e2e-tests:
|
||||||
needs: [bundle]
|
needs: [bundle]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- name: Download bundled artifact
|
- name: Download bundled artifact
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: bundles
|
name: bundles-cli
|
||||||
path: bundles-cli
|
path: bundles
|
||||||
- run: npm run e2e
|
- run: npm run e2e
|
||||||
bundle-cli:
|
bundle-cli:
|
||||||
|
needs: [check-version-cli]
|
||||||
|
if: needs.check-version-cli.outputs.changed == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
@ -72,7 +77,6 @@ jobs:
|
||||||
check-version-cli:
|
check-version-cli:
|
||||||
name: Check Version
|
name: Check Version
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [bundle-cli, unit-tests, e2e-tests]
|
|
||||||
outputs:
|
outputs:
|
||||||
changed: ${{ steps.check.outputs.changed }}
|
changed: ${{ steps.check.outputs.changed }}
|
||||||
steps:
|
steps:
|
||||||
|
@ -88,13 +92,14 @@ jobs:
|
||||||
file-url: https://unpkg.com/redoc-cli/package.json
|
file-url: https://unpkg.com/redoc-cli/package.json
|
||||||
static-checking: localIsNew
|
static-checking: localIsNew
|
||||||
publish-cli:
|
publish-cli:
|
||||||
needs: [ check-version-cli ]
|
needs: [bundle-cli, unit-tests, e2e-tests]
|
||||||
if: needs.check-version-cli.outputs.changed == 'true'
|
if: needs.check-version-cli.outputs.changed == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: "14.x"
|
node-version: '14.x'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Download cli bundled artifact
|
- name: Download cli bundled artifact
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
|
|
104
.github/workflows/publish.yml
vendored
104
.github/workflows/publish.yml
vendored
|
@ -9,73 +9,75 @@ jobs:
|
||||||
bundle:
|
bundle:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
- name: Cache node modules
|
- name: Cache node modules
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
||||||
key: npm-${{ hashFiles('package-lock.json') }}
|
key: npm-${{ hashFiles('package-lock.json') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
npm-${{ hashFiles('package-lock.json') }}
|
npm-${{ hashFiles('package-lock.json') }}
|
||||||
npm-
|
npm-
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run bundle
|
- run: npm run bundle
|
||||||
- name: Store bundle artifact
|
- name: Store bundle artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: bundles
|
name: bundles
|
||||||
path: bundles
|
path: bundles
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
unit-tests:
|
unit-tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
e2e-tests:
|
e2e-tests:
|
||||||
needs: [bundle]
|
needs: [bundle]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- run: npm ci
|
|
||||||
- name: Download bundled artifact
|
|
||||||
uses: actions/download-artifact@v2
|
|
||||||
with:
|
|
||||||
name: bundles
|
|
||||||
path: bundles
|
|
||||||
- run: npm run e2e
|
|
||||||
deploy-demo:
|
|
||||||
needs: [bundle, unit-tests, e2e-tests]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Configure AWS Credentials
|
- run: npm ci
|
||||||
uses: aws-actions/configure-aws-credentials@v1
|
- name: Download bundled artifact
|
||||||
with:
|
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
aws-region: us-east-1
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
- name: Download bundled artifacts
|
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: bundles
|
name: bundles
|
||||||
path: bundles
|
path: bundles
|
||||||
- name: Build package
|
- run: npm run e2e
|
||||||
run: npm run build:demo
|
# disable this for now
|
||||||
- name: Deploy to S3 bucket
|
# deploy-demo:
|
||||||
run: npm run deploy:demo
|
# needs: [bundle, unit-tests, e2e-tests]
|
||||||
- name: Invalidate
|
# runs-on: ubuntu-latest
|
||||||
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DEMO_DISTRIBUTION_ID }} --paths "/*"
|
# steps:
|
||||||
|
# - uses: actions/checkout@v1
|
||||||
|
# - name: Configure AWS Credentials
|
||||||
|
# uses: aws-actions/configure-aws-credentials@v1
|
||||||
|
# with:
|
||||||
|
# aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
# aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
# aws-region: us-east-1
|
||||||
|
# - name: Install dependencies
|
||||||
|
# run: npm ci
|
||||||
|
# - name: Download bundled artifacts
|
||||||
|
# uses: actions/download-artifact@v2
|
||||||
|
# with:
|
||||||
|
# name: bundles
|
||||||
|
# path: bundles
|
||||||
|
# - name: Build package
|
||||||
|
# run: npm run build:demo
|
||||||
|
# - name: Deploy to S3 bucket
|
||||||
|
# run: npm run deploy:demo
|
||||||
|
# - name: Invalidate
|
||||||
|
# run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DEMO_DISTRIBUTION_ID }} --paths "/*"
|
||||||
publish:
|
publish:
|
||||||
needs: [bundle, unit-tests, e2e-tests]
|
needs: [bundle, unit-tests, e2e-tests]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: "14.x"
|
node-version: '14.x'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Download bundled artifacts
|
- name: Download bundled artifacts
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
|
@ -91,10 +93,8 @@ jobs:
|
||||||
npm-${{ hashFiles('package-lock.json') }}
|
npm-${{ hashFiles('package-lock.json') }}
|
||||||
npm-
|
npm-
|
||||||
- name: Before deploy
|
- name: Before deploy
|
||||||
run: npm run declarations
|
run: npm ci && npm run declarations
|
||||||
- name: Publish to NPM
|
- name: Publish to NPM
|
||||||
run: npm publish
|
run: npm publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
- name: After script
|
|
||||||
run: cat ./coverage/lcov.info | coveralls
|
|
||||||
|
|
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npm run pre-commit
|
1
.prettierignore
Normal file
1
.prettierignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.md
|
48
CHANGELOG.md
48
CHANGELOG.md
|
@ -1,3 +1,51 @@
|
||||||
|
# [2.0.0-rc.59](https://github.com/Redocly/redoc/compare/v2.0.0-rc.58...v2.0.0-rc.59) (2021-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix scroll in example dropdown ([#1803](https://github.com/Redocly/redoc/issues/1803)) ([bc2d9a7](https://github.com/Redocly/redoc/commit/bc2d9a7d9cd530274483fecd136db290a5b46ff7))
|
||||||
|
* x-examples for request body param does not display [#1743](https://github.com/Redocly/redoc/issues/1743) ([#1826](https://github.com/Redocly/redoc/issues/1826)) ([aaa3b32](https://github.com/Redocly/redoc/commit/aaa3b3280c8422d450e8849ae02135dde199d6d5))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add option sideNavStyle ([#1805](https://github.com/Redocly/redoc/pull/1805)) ([2e4663b](https://github.com/Redocly/redoc/commit/2e4663b3b7022f25d3dc808afbcb3b3ad9483c41))
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.58](https://github.com/Redocly/redoc/compare/v2.0.0-rc.57...v2.0.0-rc.58) (2021-11-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add browser build for webpack 5 ([#1796](https://github.com/Redocly/redoc/issues/1796)) ([0e43ad3](https://github.com/Redocly/redoc/commit/0e43ad3102cfba8c4b30e59500ad4efc53f01c2d))
|
||||||
|
* Default boolean property value not rendered [#1779](https://github.com/Redocly/redoc/issues/1779) ([#1781](https://github.com/Redocly/redoc/issues/1781)) ([734080c](https://github.com/Redocly/redoc/commit/734080c35471d16f87004f7f9a51dcdeee1278a6))
|
||||||
|
* exclusiveMin/Max shows incorect range ([#1799](https://github.com/Redocly/redoc/issues/1799)) ([b604bd8](https://github.com/Redocly/redoc/commit/b604bd8da874f07e9e9f8b193ad10117a5f5059c))
|
||||||
|
* mobile view in docker image ([#1795](https://github.com/Redocly/redoc/issues/1795)) ([ad652b9](https://github.com/Redocly/redoc/commit/ad652b9c7fbcd84a6e83397272de64e57213fe9a))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.57](https://github.com/Redocly/redoc/compare/v2.0.0-rc.56...v2.0.0-rc.57) (2021-10-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix deref logic for oas3.1 ([#1767](https://github.com/Redocly/redoc/issues/1767)) ([4fb9c83](https://github.com/Redocly/redoc/commit/4fb9c835256b9e44bcecabde7baf0f0f3e5beb3f))
|
||||||
|
* improve publish action scripts ([#1729](https://github.com/Redocly/redoc/issues/1729)) ([952c05c](https://github.com/Redocly/redoc/commit/952c05c6b4b95fe6082611fed9e2f0913272b904))
|
||||||
|
* No match scenario in search ([#1667](https://github.com/Redocly/redoc/issues/1667)) ([352a851](https://github.com/Redocly/redoc/commit/352a8518576dfb6b240ec41212a64f1c7312ab67))
|
||||||
|
* OpenAPI 3.1: Missing description when $ref used [#1727](https://github.com/Redocly/redoc/issues/1727) ([fe6909e](https://github.com/Redocly/redoc/commit/fe6909ed80dd6053b48c30f63a2460614bf957a9))
|
||||||
|
* OpenAPI 3.1: Missing description when $ref used [#1727](https://github.com/Redocly/redoc/issues/1727) ([35f7787](https://github.com/Redocly/redoc/commit/35f77878de7d1dd250040771f17757a5a6ce85f9))
|
||||||
|
* Redoc spelling ([c87600d](https://github.com/Redocly/redoc/commit/c87600d520f037d291169b44b5803a35af16b5a5))
|
||||||
|
* Schema for events incorrectly omits readOnly and includes writeOnly ([#1720](https://github.com/Redocly/redoc/issues/1720) [#1540](https://github.com/Redocly/redoc/issues/1540)) ([a8e0c29](https://github.com/Redocly/redoc/commit/a8e0c296852661dec1dcad2388d7589f9e0d3609))
|
||||||
|
* scrolling to the first item ([#1753](https://github.com/Redocly/redoc/issues/1753)) ([bccd213](https://github.com/Redocly/redoc/commit/bccd21394ef79940c2efbe24a0d866c7af103d94))
|
||||||
|
* The number of items in the array in the array is incorrect [#1762](https://github.com/Redocly/redoc/issues/1762) ([#1763](https://github.com/Redocly/redoc/issues/1763)) ([3b8d644](https://github.com/Redocly/redoc/commit/3b8d6441bd9978b849a53021d40fd4fe150272ea))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add q/kdb+ syntax highlighting ([#1605](https://github.com/Redocly/redoc/issues/1605)) ([43451ba](https://github.com/Redocly/redoc/commit/43451ba4cd24270b8629a967d3fd2ce2eed8912e))
|
||||||
|
* new option generatedPayloadSamplesMaxDepth ([#1642](https://github.com/Redocly/redoc/issues/1642)) ([bd9390a](https://github.com/Redocly/redoc/commit/bd9390a5bfc5458c06121110db33968a20fcebe4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [2.0.0-rc.56](https://github.com/Redocly/redoc/compare/v2.0.0-rc.53...v2.0.0-rc.56) (2021-08-11)
|
# [2.0.0-rc.56](https://github.com/Redocly/redoc/compare/v2.0.0-rc.53...v2.0.0-rc.56) (2021-08-11)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
# Generate interactive API documentation from OpenAPI definitions
|
# Generate interactive API documentation from OpenAPI definitions
|
||||||
|
|
||||||
[](https://travis-ci.com/Redocly/redoc) [](https://coveralls.io/github/Redocly/redoc?branch=master) [](https://david-dm.org/Redocly/redoc) [](https://david-dm.org/Redocly/redoc#info=devDependencies) [](https://www.npmjs.com/package/redoc) [](https://github.com/Redocly/redoc/blob/master/LICENSE)
|
[](https://travis-ci.com/Redocly/redoc) [](https://coveralls.io/github/Redocly/redoc?branch=master) [](https://www.npmjs.com/package/redoc) [](https://github.com/Redocly/redoc/blob/master/LICENSE)
|
||||||
|
|
||||||
[](https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js) [](https://www.npmjs.com/package/redoc) [](https://www.jsdelivr.com/package/npm/redoc) [](https://hub.docker.com/r/redocly/redoc/)
|
[](https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js) [](https://www.npmjs.com/package/redoc) [](https://www.jsdelivr.com/package/npm/redoc) [](https://hub.docker.com/r/redocly/redoc/)
|
||||||
</div>
|
</div>
|
||||||
|
@ -183,6 +183,7 @@ IE support for Redoc.
|
||||||
For more information on Redoc's commmand-line interface, refer to
|
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://redoc.ly/docs/redoc/quickstart/cli/).
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Security Definition location
|
### Security Definition location
|
||||||
|
@ -239,6 +240,9 @@ 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.
|
* `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).
|
* `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!**
|
* `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!**
|
||||||
|
* `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.
|
||||||
|
|
||||||
### `<redoc>` theme object
|
### `<redoc>` theme object
|
||||||
* `spacing`
|
* `spacing`
|
||||||
|
|
12
cli/index.ts
12
cli/index.ts
|
@ -61,6 +61,12 @@ YargsParser.command(
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
yargs.option('h', {
|
||||||
|
alias: 'host',
|
||||||
|
type: 'string',
|
||||||
|
default: '127.0.0.1',
|
||||||
|
});
|
||||||
|
|
||||||
yargs.option('p', {
|
yargs.option('p', {
|
||||||
alias: 'port',
|
alias: 'port',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
@ -93,7 +99,7 @@ YargsParser.command(
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await serve(argv.port as number, argv.spec as string, config);
|
await serve(argv.host as string, argv.port as number, argv.spec as string, config);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e);
|
handleError(e);
|
||||||
}
|
}
|
||||||
|
@ -167,7 +173,7 @@ YargsParser.command(
|
||||||
describe: 'ReDoc options, use dot notation, e.g. options.nativeScrollbars',
|
describe: 'ReDoc options, use dot notation, e.g. options.nativeScrollbars',
|
||||||
}).argv;
|
}).argv;
|
||||||
|
|
||||||
async function serve(port: number, pathToSpec: string, options: Options = {}) {
|
async function serve(host: string, port: number, pathToSpec: string, options: Options = {}) {
|
||||||
let spec = await loadAndBundleSpec(isURL(pathToSpec) ? pathToSpec : resolve(pathToSpec));
|
let spec = await loadAndBundleSpec(isURL(pathToSpec) ? pathToSpec : resolve(pathToSpec));
|
||||||
let pageHTML = await getPageHTML(spec, pathToSpec, options);
|
let pageHTML = await getPageHTML(spec, pathToSpec, options);
|
||||||
const server = createServer((request, response) => {
|
const server = createServer((request, response) => {
|
||||||
|
@ -201,7 +207,7 @@ async function serve(port: number, pathToSpec: string, options: Options = {}) {
|
||||||
|
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
server.listen(port, () => console.log(`Server started: http://127.0.0.1:${port}`));
|
server.listen(port, host, () => console.log(`Server started: http://${host}:${port}`));
|
||||||
|
|
||||||
if (options.watch && existsSync(pathToSpec)) {
|
if (options.watch && existsSync(pathToSpec)) {
|
||||||
const pathToSpecDirectory = resolve(dirname(pathToSpec));
|
const pathToSpecDirectory = resolve(dirname(pathToSpec));
|
||||||
|
|
141
cli/npm-shrinkwrap.json
generated
141
cli/npm-shrinkwrap.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "redoc-cli",
|
"name": "redoc-cli",
|
||||||
"version": "0.12.3",
|
"version": "0.13.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "redoc-cli",
|
"name": "redoc-cli",
|
||||||
"version": "0.12.3",
|
"version": "0.13.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^3.5.1",
|
"chokidar": "^3.5.1",
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
"node-libs-browser": "^2.2.1",
|
"node-libs-browser": "^2.2.1",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"redoc": "2.0.0-rc.56",
|
"redoc": "2.0.0-rc.59",
|
||||||
"styled-components": "^5.3.0",
|
"styled-components": "^5.3.0",
|
||||||
"yargs": "^17.0.1"
|
"yargs": "^17.0.1"
|
||||||
},
|
},
|
||||||
|
@ -198,13 +198,13 @@
|
||||||
"integrity": "sha512-GoXw0U2Qaa33m3eUcxuHnHpNvHjNlLo0gtV091XBpaRINaB4X6FGCG5XKxSFNFiPpugUDqNruHzaqpTdDm4AOg=="
|
"integrity": "sha512-GoXw0U2Qaa33m3eUcxuHnHpNvHjNlLo0gtV091XBpaRINaB4X6FGCG5XKxSFNFiPpugUDqNruHzaqpTdDm4AOg=="
|
||||||
},
|
},
|
||||||
"node_modules/@redocly/ajv": {
|
"node_modules/@redocly/ajv": {
|
||||||
"version": "6.12.4",
|
"version": "8.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-6.12.4.tgz",
|
"resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.6.2.tgz",
|
||||||
"integrity": "sha512-RB6vWO78v6c+SW/3bZh+XZMr4nGdJKAiPGsBALuUZnLuCiQ7aXCT1AuFHqnfS2gyXbEUEj+kw8p4ux8KdAfs3A==",
|
"integrity": "sha512-tU8fQs0D76ZKhJ2cWtnfQthWqiZgGBx0gH0+5D8JvaBEBaqA8foPPBt3Nonwr3ygyv5xrw2IzKWgIY86BlGs+w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"json-schema-traverse": "^1.0.0",
|
||||||
"json-schema-traverse": "^0.4.1",
|
"require-from-string": "^2.0.2",
|
||||||
"uri-js": "^4.2.2"
|
"uri-js": "^4.2.2"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -213,11 +213,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@redocly/openapi-core": {
|
"node_modules/@redocly/openapi-core": {
|
||||||
"version": "1.0.0-beta.50",
|
"version": "1.0.0-beta.62",
|
||||||
"resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.50.tgz",
|
"resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.62.tgz",
|
||||||
"integrity": "sha512-GuXn4IETxpbRd8dlAQDQPtvqOpbMvPMeC/e5mv5MOXkLIznNk4vjiQVe6QSCbZbCHzzpb2+89B6S7asebPm4Rg==",
|
"integrity": "sha512-g4iPB7qpSNFCOpf/FgKiic+QCaCn4mdNQOWVEPuwpN7l72hlQ7J3YUa9cssJomkJXXxZ1zdP4h208s12LkhwVA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@redocly/ajv": "^6.12.3",
|
"@redocly/ajv": "^8.6.2",
|
||||||
"@types/node": "^14.11.8",
|
"@types/node": "^14.11.8",
|
||||||
"colorette": "^1.2.0",
|
"colorette": "^1.2.0",
|
||||||
"js-levenshtein": "^1.1.6",
|
"js-levenshtein": "^1.1.6",
|
||||||
|
@ -232,9 +232,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@redocly/openapi-core/node_modules/@types/node": {
|
"node_modules/@redocly/openapi-core/node_modules/@types/node": {
|
||||||
"version": "14.17.3",
|
"version": "14.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.21.tgz",
|
||||||
"integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw=="
|
"integrity": "sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA=="
|
||||||
},
|
},
|
||||||
"node_modules/@redocly/react-dropdown-aria": {
|
"node_modules/@redocly/react-dropdown-aria": {
|
||||||
"version": "2.0.12",
|
"version": "2.0.12",
|
||||||
|
@ -283,7 +283,8 @@
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "15.12.2",
|
"version": "15.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz",
|
||||||
"integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww=="
|
"integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
|
@ -637,9 +638,9 @@
|
||||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
},
|
},
|
||||||
"node_modules/colorette": {
|
"node_modules/colorette": {
|
||||||
"version": "1.2.2",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
|
||||||
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w=="
|
"integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="
|
||||||
},
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
@ -893,11 +894,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
},
|
},
|
||||||
"node_modules/fast-json-stable-stringify": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
|
|
||||||
},
|
|
||||||
"node_modules/fast-safe-stringify": {
|
"node_modules/fast-safe-stringify": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
|
||||||
|
@ -925,9 +921,7 @@
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": ["darwin"],
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
|
@ -1175,9 +1169,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/json-schema-traverse": {
|
"node_modules/json-schema-traverse": {
|
||||||
"version": "0.4.1",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
|
||||||
},
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
|
@ -1231,11 +1225,6 @@
|
||||||
"safe-buffer": "^5.1.2"
|
"safe-buffer": "^5.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/memoize-one": {
|
|
||||||
"version": "5.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
|
||||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
|
||||||
},
|
|
||||||
"node_modules/miller-rabin": {
|
"node_modules/miller-rabin": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
|
||||||
|
@ -1751,14 +1740,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/redoc": {
|
"node_modules/redoc": {
|
||||||
"version": "2.0.0-rc.56",
|
"version": "2.0.0-rc.59",
|
||||||
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.56.tgz",
|
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.59.tgz",
|
||||||
"integrity": "sha512-ir2TtQ2d/1FqZWIoLmUZ3qvAAnO6jg8dt0SV75TanmfCXpEABcElXWH3mtUf6qKlvgDVt40diDCVuSvyPPxkAw==",
|
"integrity": "sha512-1Wkj/HSCv5CdtwF7FSZc5L0EeBgI0N7YpAIsatMtfiMHEon0WhuArAkc5rMQ6mQXUPRrqq5Fs6QPc4GpNp6DuA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.14.0",
|
"@babel/runtime": "^7.14.0",
|
||||||
"@redocly/openapi-core": "^1.0.0-beta.50",
|
"@redocly/openapi-core": "^1.0.0-beta.54",
|
||||||
"@redocly/react-dropdown-aria": "^2.0.11",
|
"@redocly/react-dropdown-aria": "^2.0.11",
|
||||||
"@types/node": "^15.6.1",
|
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"decko": "^1.2.0",
|
"decko": "^1.2.0",
|
||||||
"dompurify": "^2.2.8",
|
"dompurify": "^2.2.8",
|
||||||
|
@ -1767,7 +1755,6 @@
|
||||||
"lunr": "^2.3.9",
|
"lunr": "^2.3.9",
|
||||||
"mark.js": "^8.11.1",
|
"mark.js": "^8.11.1",
|
||||||
"marked": "^0.7.0",
|
"marked": "^0.7.0",
|
||||||
"memoize-one": "^5.2.1",
|
|
||||||
"mobx-react": "^7.2.0",
|
"mobx-react": "^7.2.0",
|
||||||
"openapi-sampler": "^1.0.1",
|
"openapi-sampler": "^1.0.1",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
|
@ -1819,6 +1806,14 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-from-string": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ripemd160": {
|
"node_modules/ripemd160": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||||
|
@ -2451,22 +2446,22 @@
|
||||||
"integrity": "sha512-GoXw0U2Qaa33m3eUcxuHnHpNvHjNlLo0gtV091XBpaRINaB4X6FGCG5XKxSFNFiPpugUDqNruHzaqpTdDm4AOg=="
|
"integrity": "sha512-GoXw0U2Qaa33m3eUcxuHnHpNvHjNlLo0gtV091XBpaRINaB4X6FGCG5XKxSFNFiPpugUDqNruHzaqpTdDm4AOg=="
|
||||||
},
|
},
|
||||||
"@redocly/ajv": {
|
"@redocly/ajv": {
|
||||||
"version": "6.12.4",
|
"version": "8.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-6.12.4.tgz",
|
"resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.6.2.tgz",
|
||||||
"integrity": "sha512-RB6vWO78v6c+SW/3bZh+XZMr4nGdJKAiPGsBALuUZnLuCiQ7aXCT1AuFHqnfS2gyXbEUEj+kw8p4ux8KdAfs3A==",
|
"integrity": "sha512-tU8fQs0D76ZKhJ2cWtnfQthWqiZgGBx0gH0+5D8JvaBEBaqA8foPPBt3Nonwr3ygyv5xrw2IzKWgIY86BlGs+w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"json-schema-traverse": "^1.0.0",
|
||||||
"json-schema-traverse": "^0.4.1",
|
"require-from-string": "^2.0.2",
|
||||||
"uri-js": "^4.2.2"
|
"uri-js": "^4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@redocly/openapi-core": {
|
"@redocly/openapi-core": {
|
||||||
"version": "1.0.0-beta.50",
|
"version": "1.0.0-beta.62",
|
||||||
"resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.50.tgz",
|
"resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.62.tgz",
|
||||||
"integrity": "sha512-GuXn4IETxpbRd8dlAQDQPtvqOpbMvPMeC/e5mv5MOXkLIznNk4vjiQVe6QSCbZbCHzzpb2+89B6S7asebPm4Rg==",
|
"integrity": "sha512-g4iPB7qpSNFCOpf/FgKiic+QCaCn4mdNQOWVEPuwpN7l72hlQ7J3YUa9cssJomkJXXxZ1zdP4h208s12LkhwVA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@redocly/ajv": "^6.12.3",
|
"@redocly/ajv": "^8.6.2",
|
||||||
"@types/node": "^14.11.8",
|
"@types/node": "^14.11.8",
|
||||||
"colorette": "^1.2.0",
|
"colorette": "^1.2.0",
|
||||||
"js-levenshtein": "^1.1.6",
|
"js-levenshtein": "^1.1.6",
|
||||||
|
@ -2478,9 +2473,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "14.17.3",
|
"version": "14.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.21.tgz",
|
||||||
"integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw=="
|
"integrity": "sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2525,7 +2520,8 @@
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "15.12.2",
|
"version": "15.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz",
|
||||||
"integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww=="
|
"integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
|
@ -2841,9 +2837,9 @@
|
||||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
},
|
},
|
||||||
"colorette": {
|
"colorette": {
|
||||||
"version": "1.2.2",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
|
||||||
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w=="
|
"integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
@ -3064,11 +3060,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
},
|
},
|
||||||
"fast-json-stable-stringify": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
|
|
||||||
},
|
|
||||||
"fast-safe-stringify": {
|
"fast-safe-stringify": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
|
||||||
|
@ -3273,9 +3264,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"json-schema-traverse": {
|
"json-schema-traverse": {
|
||||||
"version": "0.4.1",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
|
||||||
},
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
|
@ -3320,11 +3311,6 @@
|
||||||
"safe-buffer": "^5.1.2"
|
"safe-buffer": "^5.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"memoize-one": {
|
|
||||||
"version": "5.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
|
||||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
|
||||||
},
|
|
||||||
"miller-rabin": {
|
"miller-rabin": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
|
||||||
|
@ -3741,14 +3727,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redoc": {
|
"redoc": {
|
||||||
"version": "2.0.0-rc.56",
|
"version": "2.0.0-rc.59",
|
||||||
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.56.tgz",
|
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.59.tgz",
|
||||||
"integrity": "sha512-ir2TtQ2d/1FqZWIoLmUZ3qvAAnO6jg8dt0SV75TanmfCXpEABcElXWH3mtUf6qKlvgDVt40diDCVuSvyPPxkAw==",
|
"integrity": "sha512-1Wkj/HSCv5CdtwF7FSZc5L0EeBgI0N7YpAIsatMtfiMHEon0WhuArAkc5rMQ6mQXUPRrqq5Fs6QPc4GpNp6DuA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.14.0",
|
"@babel/runtime": "^7.14.0",
|
||||||
"@redocly/openapi-core": "^1.0.0-beta.50",
|
"@redocly/openapi-core": "^1.0.0-beta.54",
|
||||||
"@redocly/react-dropdown-aria": "^2.0.11",
|
"@redocly/react-dropdown-aria": "^2.0.11",
|
||||||
"@types/node": "^15.6.1",
|
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"decko": "^1.2.0",
|
"decko": "^1.2.0",
|
||||||
"dompurify": "^2.2.8",
|
"dompurify": "^2.2.8",
|
||||||
|
@ -3757,7 +3742,6 @@
|
||||||
"lunr": "^2.3.9",
|
"lunr": "^2.3.9",
|
||||||
"mark.js": "^8.11.1",
|
"mark.js": "^8.11.1",
|
||||||
"marked": "^0.7.0",
|
"marked": "^0.7.0",
|
||||||
"memoize-one": "^5.2.1",
|
|
||||||
"mobx-react": "^7.2.0",
|
"mobx-react": "^7.2.0",
|
||||||
"openapi-sampler": "^1.0.1",
|
"openapi-sampler": "^1.0.1",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
|
@ -3794,6 +3778,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
|
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
|
||||||
},
|
},
|
||||||
|
"require-from-string": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
|
||||||
|
},
|
||||||
"ripemd160": {
|
"ripemd160": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "redoc-cli",
|
"name": "redoc-cli",
|
||||||
"version": "0.12.3",
|
"version": "0.13.2",
|
||||||
"description": "ReDoc's Command Line Interface",
|
"description": "ReDoc's Command Line Interface",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"bin": "index.js",
|
"bin": "index.js",
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
"node-libs-browser": "^2.2.1",
|
"node-libs-browser": "^2.2.1",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"redoc": "2.0.0-rc.56",
|
"redoc": "2.0.0-rc.59",
|
||||||
"styled-components": "^5.3.0",
|
"styled-components": "^5.3.0",
|
||||||
"yargs": "^17.0.1"
|
"yargs": "^17.0.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>%PAGE_TITLE%</title>
|
||||||
|
<link rel="icon" href="%PAGE_FAVICON%" />
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
<head>
|
redoc {
|
||||||
<meta charset="UTF-8" />
|
display: block;
|
||||||
<title>%PAGE_TITLE%</title>
|
}
|
||||||
<link rel="icon" href="%PAGE_FAVICON%">
|
</style>
|
||||||
<style>
|
<link
|
||||||
body {
|
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
|
||||||
margin: 0;
|
rel="stylesheet"
|
||||||
padding: 0;
|
/>
|
||||||
}
|
</head>
|
||||||
|
|
||||||
redoc {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<redoc spec-url="%SPEC_URL%" %REDOC_OPTIONS%></redoc>
|
|
||||||
<script src="redoc.standalone.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<redoc spec-url="%SPEC_URL%" %REDOC_OPTIONS%></redoc>
|
||||||
|
<script src="redoc.standalone.js"></script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -33,14 +33,14 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
},
|
},
|
||||||
|
|
||||||
devServer: {
|
devServer: {
|
||||||
contentBase: __dirname,
|
static: __dirname,
|
||||||
watchContentBase: true,
|
|
||||||
port: 9090,
|
port: 9090,
|
||||||
disableHostCheck: true,
|
|
||||||
stats: 'minimal',
|
|
||||||
hot: true,
|
hot: true,
|
||||||
|
historyApiFallback: true,
|
||||||
|
},
|
||||||
|
stats: {
|
||||||
|
children: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.tsx', '.js', '.json'],
|
extensions: ['.ts', '.tsx', '.js', '.json'],
|
||||||
fallback: {
|
fallback: {
|
||||||
|
@ -72,7 +72,7 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
{ test: [/\.eot$/, /\.gif$/, /\.woff$/, /\.svg$/, /\.ttf$/], use: 'null-loader' },
|
{ test: [/\.eot$/, /\.gif$/, /\.woff$/, /\.svg$/, /\.ttf$/], use: 'null-loader' },
|
||||||
{
|
{
|
||||||
test: /\.tsx?$/,
|
test: /\.tsx?$/,
|
||||||
use: [getBabelLoader({useBuiltIns: true, hot: true} )],
|
use: [getBabelLoader({ useBuiltIns: true, hot: true })],
|
||||||
exclude: {
|
exclude: {
|
||||||
and: [/node_modules/],
|
and: [/node_modules/],
|
||||||
not: {
|
not: {
|
||||||
|
|
114
docs/deployment/cli.md
Normal file
114
docs/deployment/cli.md
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
---
|
||||||
|
title: Using the Redoc CLI
|
||||||
|
redirectFrom:
|
||||||
|
- /docs/quickstart/cli/
|
||||||
|
---
|
||||||
|
|
||||||
|
# Using the Redoc CLI
|
||||||
|
|
||||||
|
With Redoc's command-line interface you can bundle your OpenAPI definition and API documentation
|
||||||
|
(made with Redoc) into a zero-dependency HTML file and locally render your
|
||||||
|
OpenAPI definition with Redoc.
|
||||||
|
|
||||||
|
## Step 1 - Install Redoc CLI
|
||||||
|
|
||||||
|
You can install the `redoc-cli` package globally using one of the following package managers:
|
||||||
|
|
||||||
|
- [npm](https://docs.npmjs.com/about-npm)
|
||||||
|
- [yarn](https://classic.yarnpkg.com/en/docs/getting-started)
|
||||||
|
|
||||||
|
Or you can install `redoc-cli` using [npx](https://www.freecodecamp.org/news/npm-vs-npx-whats-the-difference/).
|
||||||
|
|
||||||
|
### Install Redoc CLI with yarn
|
||||||
|
|
||||||
|
To install the `redoc-cli` package globally with yarn:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn global add redoc-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install Redoc with npm
|
||||||
|
|
||||||
|
To install the `redoc-cli` package globally with npm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i -g redoc-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install with `npx`
|
||||||
|
|
||||||
|
To install the `redoc-cli` package locally with `npx`, navigate to your project
|
||||||
|
directory in your terminal, then use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx redoc-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2 - Use the CLI
|
||||||
|
|
||||||
|
### Redoc CLI commands
|
||||||
|
|
||||||
|
The CLI includes the following commands:
|
||||||
|
|
||||||
|
- **`redoc-cli serve [spec]`:** Starts a local server with Redoc. You must include the required parameter, spec, which is
|
||||||
|
a reference to an OpenAPI definition. Options include:
|
||||||
|
- `--ssr`: Implements a server-side rendering model.
|
||||||
|
- `--watch`: Automatically reloads the server while you edit your OpenAPI definition.
|
||||||
|
- `--options`: Customizes your output using [Redoc options](https://redoc.ly/docs/api-reference-docs/configuration/).
|
||||||
|
To add nested options, use dot notation.
|
||||||
|
- **`redoc-cli bundle [spec]`:** Bundles `spec` and Redoc into a zero-dependency HTML file. Options include:
|
||||||
|
- `-t` or `--template`: Uses custom [Handlebars](https://handlebarsjs.com/) templates to render your OpenAPI definition.
|
||||||
|
- `--templateOptions`: Adds template options you want to pass to your
|
||||||
|
custom Handlebars template. To add options, use dot notation.
|
||||||
|
- **`--help`:** Prints help text for the Redoc CLI commands and options.
|
||||||
|
- **`--version`:** Prints the version of the `redoc-cli` package you have installed.
|
||||||
|
|
||||||
|
### Redoc CLI examples
|
||||||
|
|
||||||
|
#### Bundle
|
||||||
|
|
||||||
|
Bundle with the main color changed to `orange`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redoc-cli bundle openapi.yaml --options.theme.colors.primary.main=orange
|
||||||
|
```
|
||||||
|
|
||||||
|
Bundle using a custom Handlebars template and add custom `templateOptions`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redoc-cli bundle http://petstore.swagger.io/v2/swagger.json -t custom.hbs --templateOptions.metaDescription "Page meta description"
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample Handlebars template:
|
||||||
|
|
||||||
|
```handlebars
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf8" />
|
||||||
|
<title>{{title}}</title>
|
||||||
|
<!-- needed for adaptive design -->
|
||||||
|
<meta description="{{{templateOptions.metaDescription}}}">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{{{redocHead}}}
|
||||||
|
{{#unless disableGoogleFont}}<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">{{/unless}}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{{redocHTML}}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Serve
|
||||||
|
|
||||||
|
Serve with the `nativeScrollbars` option set to `true`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redoc-cli serve openapi/dist.yaml --options.nativeScrollbars
|
||||||
|
```
|
41
docs/deployment/docker.md
Normal file
41
docs/deployment/docker.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
title: Using the Redoc Docker image
|
||||||
|
redirectFrom:
|
||||||
|
- /docs/quickstart/docker/
|
||||||
|
---
|
||||||
|
|
||||||
|
# Using the Redoc Docker image
|
||||||
|
|
||||||
|
Redoc is available as a pre-built Docker image in [Docker Hub](https://hub.docker.com/r/redocly/redoc/).
|
||||||
|
|
||||||
|
If you have [Docker](https://docs.docker.com/get-docker/) installed, pull the image with the following command:
|
||||||
|
|
||||||
|
```docker
|
||||||
|
docker pull redocly/redoc
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run the image with the following command:
|
||||||
|
|
||||||
|
```docker
|
||||||
|
docker run -p 8080:80 redocly/redoc
|
||||||
|
```
|
||||||
|
|
||||||
|
The preview starts on port 8080, based on the port used in the command,
|
||||||
|
and can be accessed at `http://localhost:8080`.
|
||||||
|
To exit the preview, use `control+C`.
|
||||||
|
|
||||||
|
By default Redoc starts with a demo Swagger Petstore OpenAPI definition located at
|
||||||
|
http://petstore.swagger.io/v2/swagger.json. You can update this URL using
|
||||||
|
the environment variable `SPEC_URL`.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -p 8080:80 -e SPEC_URL=https://api.example.com/openapi.json redocly/redoc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using a Dockerfile
|
||||||
|
|
||||||
|
You can also create a Dockerfile with some predefined environment variables. Check out
|
||||||
|
a sample [Dockerfile](https://github.com/Redocly/redoc/blob/master/config/docker/Dockerfile)
|
||||||
|
in our code repo.
|
123
docs/deployment/html.md
Normal file
123
docs/deployment/html.md
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
---
|
||||||
|
title: Using the Redoc HTML element
|
||||||
|
redirectFrom:
|
||||||
|
- /docs/quickstart/html/
|
||||||
|
---
|
||||||
|
|
||||||
|
# Using the Redoc HTML element
|
||||||
|
|
||||||
|
## Step 1 - Install Redoc
|
||||||
|
|
||||||
|
You can install Redoc using one of the following package managers:
|
||||||
|
|
||||||
|
- [npm](https://docs.npmjs.com/about-npm)
|
||||||
|
- [yarn](https://classic.yarnpkg.com/en/docs/getting-started)
|
||||||
|
|
||||||
|
:::attention Initialize your package manager
|
||||||
|
If you do not have a `package.json` file in your project directory,
|
||||||
|
you need to add one by initializing npm or yarn in your project. Use the command `npm init` for npm,
|
||||||
|
or `yarn init` for yarn. These initialization commands will lead you through the process
|
||||||
|
of creating a `package.json` file in your project.
|
||||||
|
|
||||||
|
For more information, see
|
||||||
|
[Creating a package.json file](https://docs.npmjs.com/creating-a-package-json-file)
|
||||||
|
in the npm documentation or [Yarn init](https://classic.yarnpkg.com/en/docs/cli/init/)
|
||||||
|
in the yarn documentation.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Install Redoc with yarn
|
||||||
|
|
||||||
|
After navigating to your project directory in your terminal, use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn add redoc
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install Redoc with npm
|
||||||
|
|
||||||
|
After navigating to your project directory in your terminal, use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i redoc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2 - Reference the Redoc script
|
||||||
|
|
||||||
|
You can reference the Redoc script using either a link to the files hosted on a CDN
|
||||||
|
or the files located in your `node modules` folder.
|
||||||
|
|
||||||
|
### CDN link
|
||||||
|
|
||||||
|
To reference the Redoc script with a CDN link:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"> </script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Node modules link
|
||||||
|
|
||||||
|
To reference the Redoc script with a node modules link:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="node_modules/redoc/bundles/redoc.standalone.js"> </script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 3 - Add the <redoc> element
|
||||||
|
|
||||||
|
You can add the <redoc> element to your HTML page and reference your OpenAPI
|
||||||
|
definition using the `spec-url` attribute, or you can initialize Redoc using
|
||||||
|
a globally exposed Redoc object.
|
||||||
|
|
||||||
|
### Using the `spec-url` attribute
|
||||||
|
|
||||||
|
To add the <redoc> element with the `spec-url` attribute:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<redoc spec-url="url/to/your/spec"></redoc>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
```html
|
||||||
|
<redoc spec-url="http://petstore.swagger.io/v2/swagger.json"></redoc>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use a local file (JSON or YAML) in your project, for instance:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<redoc spec-url="dist.json"></redoc>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using a Redoc object
|
||||||
|
|
||||||
|
To add the <redoc> element with a globally exposed Redoc object:
|
||||||
|
|
||||||
|
```js
|
||||||
|
Redoc.init(specOrSpecUrl, options, element, callback)
|
||||||
|
```
|
||||||
|
- `specOrSpecUrl`: Either a JSON object with the OpenAPI definition or a URL to the
|
||||||
|
definition in JSON or YAML format.
|
||||||
|
- `options`: See [options object](https://redoc.ly/docs/api-reference-docs/configuration/) reference.
|
||||||
|
- `element`: DOM element Redoc will be inserted into.
|
||||||
|
- `callback`(optional): Callback to be called after Redoc has been fully rendered.
|
||||||
|
It is also called on errors with `error` as the first argument.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
Redoc.init('http://petstore.swagger.io/v2/swagger.json', {
|
||||||
|
scrollYOffset: 50
|
||||||
|
}, document.getElementById('redoc-container'))
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use a local file (JSON or YAML) in your project, for instance:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
Redoc.init('dist.yaml', {
|
||||||
|
scrollYOffset: 50
|
||||||
|
}, document.getElementById('redoc-container'))
|
||||||
|
</script>
|
||||||
|
```
|
112
docs/deployment/intro.md
Normal file
112
docs/deployment/intro.md
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
---
|
||||||
|
title: Redoc deployment guide
|
||||||
|
redirectFrom:
|
||||||
|
- /docs/quickstart/intro/
|
||||||
|
---
|
||||||
|
|
||||||
|
# Redoc deployment guide
|
||||||
|
|
||||||
|
Redoc offers multiple options for rendering your OpenAPI definition.
|
||||||
|
You should select the option that best fits your needs.
|
||||||
|
|
||||||
|
The following options are supported:
|
||||||
|
|
||||||
|
- **[Live demo](https://redocly.github.io/redoc/):**
|
||||||
|
The live demo offers a fast way to see how your OpenAPI will render with Redoc.
|
||||||
|
A version of the Swagger Petstore API is displayed by default. To test it with your own OpenAPI definition, enter the URL for your
|
||||||
|
definition and select **TRY IT**.
|
||||||
|
- **[HTML element](./html.md):**
|
||||||
|
Using the HTML element works well for typical website deployments.
|
||||||
|
- **[React component](./react.md):**
|
||||||
|
Using the React component is an option for users with a React-based application.
|
||||||
|
- **[Docker image](./docker.md):**
|
||||||
|
Using the Docker image works in a container-based deployment.
|
||||||
|
- **[CLI](./cli.md):**
|
||||||
|
Using the CLI is an option for users who prefer to use a command-line interface.
|
||||||
|
|
||||||
|
## Before you start
|
||||||
|
|
||||||
|
### OpenAPI definition
|
||||||
|
|
||||||
|
You will need an OpenAPI definition. For testing purposes, you can use one of the following sample OpenAPI definitions:
|
||||||
|
|
||||||
|
- OpenAPI 3.0
|
||||||
|
- [Rebilly Users OpenAPI Definition](https://raw.githubusercontent.com/Rebilly/api-definitions/main/openapi/users.yaml)
|
||||||
|
- [Swagger Petstore Sample OpenAPI Definition](https://petstore3.swagger.io/api/v3/openapi.json)
|
||||||
|
- OpenAPI 2.0
|
||||||
|
- [Thingful OpenAPI Definition](https://raw.githubusercontent.com/thingful/openapi-spec/master/spec/swagger.yaml)
|
||||||
|
- [Fitbit Plus OpenAPI Definition](https://raw.githubusercontent.com/TwineHealth/TwineDeveloperDocs/master/spec/swagger.yaml)
|
||||||
|
|
||||||
|
:::info OpenAPI specification
|
||||||
|
For more information on the OpenAPI specification, refer to the [Learning OpenAPI 3](https://redoc.ly/docs/resources/learning-openapi/)
|
||||||
|
section in the documentation.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Running Redoc locally
|
||||||
|
|
||||||
|
If you want to view your Redoc output locally, you can simulate an HTTP server.
|
||||||
|
|
||||||
|
#### Using Redocly OpenAPI CLI
|
||||||
|
|
||||||
|
Redocly OpenAPI 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://redoc.ly/docs/cli/#installation-and-usage) installed, `cd` into your
|
||||||
|
project directory and run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openapi preview-docs openapi.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `openapi.yaml` in the example command with the file path to your OpenAPI definition.
|
||||||
|
|
||||||
|
By default, without providing a port, the preview starts on port 8080, and can be accessed at `http://localhost:8080`.
|
||||||
|
To exit the preview, use `control+C`.
|
||||||
|
|
||||||
|
You can alter the port if you are using 8080 already, for example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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://redoc.ly/docs/cli/commands/preview-docs/#preview-docs) in the OpenAPI CLI documentation.
|
||||||
|
|
||||||
|
#### Using Python
|
||||||
|
|
||||||
|
If you have [Python 3](https://www.python.org/downloads/) installed, `cd` into your
|
||||||
|
project directory and run the following command:
|
||||||
|
|
||||||
|
```python
|
||||||
|
python3 -m http.server
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have [Python 2](https://www.python.org/downloads/) installed, `cd` into your
|
||||||
|
project directory and run the following command:
|
||||||
|
|
||||||
|
```python
|
||||||
|
python -m SimpleHTTPServer 8000
|
||||||
|
```
|
||||||
|
|
||||||
|
The output after entering the command provides the local URL where the preview can be accessed.
|
||||||
|
To exit the preview, use `control-C`.
|
||||||
|
|
||||||
|
#### Using Node.js
|
||||||
|
|
||||||
|
If you have [Node.js](https://nodejs.org/en/download/) installed, install `http-server`
|
||||||
|
using the following npm command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g http-server
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, `cd` into your project directory and run the following command:
|
||||||
|
|
||||||
|
```node
|
||||||
|
http-server
|
||||||
|
```
|
||||||
|
|
||||||
|
The output after entering the command provides the local URL where the preview can be accessed.
|
||||||
|
To exit the preview, use `control-C`.
|
80
docs/deployment/react.md
Normal file
80
docs/deployment/react.md
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
---
|
||||||
|
title: Using the Redoc React component
|
||||||
|
redirectFrom:
|
||||||
|
- /docs/quickstart/react/
|
||||||
|
---
|
||||||
|
|
||||||
|
# Using the Redoc React component
|
||||||
|
|
||||||
|
## Before you start
|
||||||
|
|
||||||
|
Install the following dependencies required by Redoc if you do not already have them installed:
|
||||||
|
|
||||||
|
- `react`
|
||||||
|
- `react-dom`
|
||||||
|
- `mobx`
|
||||||
|
- `styled-components`
|
||||||
|
- `core-js`
|
||||||
|
|
||||||
|
If you have npm installed, you can install these dependencies using the following command:
|
||||||
|
|
||||||
|
```js
|
||||||
|
npm i react react-dom mobx styled-components core-js
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 1 - Import the `RedocStandalone` component
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { RedocStandalone } from 'redoc';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2 - Use the component
|
||||||
|
|
||||||
|
You can either link to your OpenAPI definition with a URL, using the following format:
|
||||||
|
|
||||||
|
```react
|
||||||
|
<RedocStandalone specUrl="url/to/your/spec"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
Or you can pass your OpenAPI definition as an object, using the following format:
|
||||||
|
|
||||||
|
```js
|
||||||
|
<RedocStandalone spec={/* spec as an object */}/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Optional - Pass options
|
||||||
|
|
||||||
|
Options can be passed into the RedocStandalone component to alter how it renders.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
<RedocStandalone
|
||||||
|
specUrl="http://petstore.swagger.io/v2/swagger.json"
|
||||||
|
options={{
|
||||||
|
nativeScrollbars: true,
|
||||||
|
theme: { colors: { primary: { main: '#dd5522' } } },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information on configuration options, refer to the
|
||||||
|
[Configuration options for Reference docs](https://redoc.ly/docs/api-reference-docs/configuration/)
|
||||||
|
section of the documentation. Options available for Redoc are noted,
|
||||||
|
"Supported in Redoc CE".
|
||||||
|
|
||||||
|
## Optional - Specify `onLoaded` callback
|
||||||
|
|
||||||
|
You can also specify the `onLoaded` callback, which is called each time Redoc
|
||||||
|
is fully rendered or when an error occurs (with an error as the first argument).
|
||||||
|
|
||||||
|
```js
|
||||||
|
<RedocStandalone
|
||||||
|
specUrl="http://petstore.swagger.io/v2/swagger.json"
|
||||||
|
onLoaded={error => {
|
||||||
|
if (!error) {
|
||||||
|
console.log('Yay!');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
```
|
53
docs/quickstart.md
Normal file
53
docs/quickstart.md
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
---
|
||||||
|
title: Redoc quickstart guide
|
||||||
|
---
|
||||||
|
|
||||||
|
# Redoc quickstart guide
|
||||||
|
|
||||||
|
To render your OpenAPI definition using Redoc, use the following HTML code sample and
|
||||||
|
replace the `spec-url` attribute with the URL or local file address to your definition.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Redoc</title>
|
||||||
|
<!-- needed for adaptive design -->
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Redoc doesn't change outer page styles
|
||||||
|
-->
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!--
|
||||||
|
Redoc element with link to your OpenAPI definition
|
||||||
|
-->
|
||||||
|
<redoc spec-url="http://petstore.swagger.io/v2/swagger.json"></redoc>
|
||||||
|
<!--
|
||||||
|
Link to Redoc JavaScript on CDN for rendering standalone element
|
||||||
|
-->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
:::attention Running Redoc locally requires an HTTP server
|
||||||
|
Loading local OpenAPI definitions is impossible without running a web server because of issues with
|
||||||
|
[same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) and
|
||||||
|
other security reasons. Refer to [Running Redoc locally](./deployment/intro.md#running-redoc-locally) for
|
||||||
|
more information.
|
||||||
|
:::
|
||||||
|
|
||||||
|
For a more detailed explanation with step-by-step instructions and additional options for using Redoc, refer to the [Redoc deployment guide](./deployment/intro.md).
|
|
@ -1,13 +0,0 @@
|
||||||
redoc:
|
|
||||||
- group: Quickstart
|
|
||||||
expanded: false
|
|
||||||
page: redoc/quickstart/intro.md
|
|
||||||
pages:
|
|
||||||
- label: HTML element
|
|
||||||
page: redoc/quickstart/html.md
|
|
||||||
- label: React component
|
|
||||||
page: redoc/quickstart/react.md
|
|
||||||
- label: Docker image
|
|
||||||
page: redoc/quickstart/docker.md
|
|
||||||
- label: Command-line interface
|
|
||||||
page: redoc/quickstart/cli.md
|
|
|
@ -53,4 +53,10 @@ describe('Search', () => {
|
||||||
getSearchInput().type('int', { force: true });
|
getSearchInput().type('int', { force: true });
|
||||||
cy.get('[data-markjs]').should('exist');
|
cy.get('[data-markjs]').should('exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show proper message when no search results are found', () => {
|
||||||
|
getSearchResults().should('not.exist');
|
||||||
|
getSearchInput().type('xzss', { force: true });
|
||||||
|
getSearchResults().should('exist').should('contain', 'No results found');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
7451
package-lock.json
generated
7451
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
34
package.json
34
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"version": "2.0.0-rc.56",
|
"version": "2.0.0-rc.59",
|
||||||
"description": "ReDoc",
|
"description": "ReDoc",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -27,6 +27,7 @@
|
||||||
"React.js"
|
"React.js"
|
||||||
],
|
],
|
||||||
"main": "bundles/redoc.lib.js",
|
"main": "bundles/redoc.lib.js",
|
||||||
|
"browser": "bundles/redoc.browser.lib.js",
|
||||||
"types": "typings/index.d.ts",
|
"types": "typings/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack serve --mode=development --env playground --hot --config demo/webpack.config.ts",
|
"start": "webpack serve --mode=development --env playground --hot --config demo/webpack.config.ts",
|
||||||
|
@ -36,13 +37,14 @@
|
||||||
"unit": "jest --coverage",
|
"unit": "jest --coverage",
|
||||||
"e2e": "cypress run",
|
"e2e": "cypress run",
|
||||||
"e2e-ci": "cypress run --record",
|
"e2e-ci": "cypress run --record",
|
||||||
"bundlesize": "bundlesize",
|
"bundlesize": "size-limit",
|
||||||
"ts-check": "tsc --noEmit --skipLibCheck",
|
"ts-check": "tsc --noEmit --skipLibCheck",
|
||||||
"cy:open": "cypress open",
|
"cy:open": "cypress open",
|
||||||
"bundle:clean": "rimraf bundles",
|
"bundle:clean": "rimraf bundles",
|
||||||
"bundle:standalone": "webpack --env production --env standalone --mode=production",
|
"bundle:standalone": "webpack --env production --env standalone --mode=production",
|
||||||
"bundle:lib": "webpack --mode=production && npm run declarations",
|
"bundle:lib": "webpack --mode=production && npm run declarations",
|
||||||
"bundle": "npm run bundle:clean && npm run bundle:lib && npm run bundle:standalone",
|
"bundle:browser": "webpack --env production --env browser --mode=production",
|
||||||
|
"bundle": "npm run bundle:clean && npm run bundle:lib && npm run bundle:browser && npm run bundle:standalone",
|
||||||
"declarations": "tsc --emitDeclarationOnly -p tsconfig.lib.json && cp -R src/types typings/",
|
"declarations": "tsc --emitDeclarationOnly -p tsconfig.lib.json && cp -R src/types typings/",
|
||||||
"stats": "webpack --env production --env standalone --json --profile --mode=production > stats.json",
|
"stats": "webpack --env production --env standalone --json --profile --mode=production > stats.json",
|
||||||
"prettier": "prettier --write \"cli/index.ts\" \"src/**/*.{ts,tsx}\"",
|
"prettier": "prettier --write \"cli/index.ts\" \"src/**/*.{ts,tsx}\"",
|
||||||
|
@ -54,7 +56,9 @@
|
||||||
"build:demo": "webpack --mode=production --config demo/webpack.config.ts",
|
"build:demo": "webpack --mode=production --config demo/webpack.config.ts",
|
||||||
"deploy:demo": "aws s3 sync demo/dist s3://production-redoc-demo --acl=public-read",
|
"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' --summary",
|
"license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause' --summary",
|
||||||
"docker:build": "docker build -f config/docker/Dockerfile -t redoc ."
|
"docker:build": "docker build -f config/docker/Dockerfile -t redoc .",
|
||||||
|
"prepare": "husky install",
|
||||||
|
"pre-commit": "pretty-quick --staged && npm run lint"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.14.3",
|
"@babel/core": "^7.14.3",
|
||||||
|
@ -71,6 +75,7 @@
|
||||||
"@babel/preset-typescript": "^7.13.0",
|
"@babel/preset-typescript": "^7.13.0",
|
||||||
"@cypress/webpack-preprocessor": "^5.9.0",
|
"@cypress/webpack-preprocessor": "^5.9.0",
|
||||||
"@hot-loader/react-dom": "^17.0.1",
|
"@hot-loader/react-dom": "^17.0.1",
|
||||||
|
"@size-limit/preset-app": "^7.0.4",
|
||||||
"@types/chai": "^4.2.18",
|
"@types/chai": "^4.2.18",
|
||||||
"@types/dompurify": "^2.2.2",
|
"@types/dompurify": "^2.2.2",
|
||||||
"@types/enzyme": "^3.10.5",
|
"@types/enzyme": "^3.10.5",
|
||||||
|
@ -79,9 +84,9 @@
|
||||||
"@types/json-pointer": "^1.0.30",
|
"@types/json-pointer": "^1.0.30",
|
||||||
"@types/lodash": "^4.14.170",
|
"@types/lodash": "^4.14.170",
|
||||||
"@types/lunr": "^2.3.3",
|
"@types/lunr": "^2.3.3",
|
||||||
"@types/node": "^15.6.1",
|
|
||||||
"@types/mark.js": "^8.11.5",
|
"@types/mark.js": "^8.11.5",
|
||||||
"@types/marked": "^1.1.0",
|
"@types/marked": "^3.0.1",
|
||||||
|
"@types/node": "^15.6.1",
|
||||||
"@types/prismjs": "^1.16.5",
|
"@types/prismjs": "^1.16.5",
|
||||||
"@types/prop-types": "^15.7.3",
|
"@types/prop-types": "^15.7.3",
|
||||||
"@types/react": "^17.0.8",
|
"@types/react": "^17.0.8",
|
||||||
|
@ -98,7 +103,6 @@
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
"babel-plugin-styled-components": "^1.12.0",
|
"babel-plugin-styled-components": "^1.12.0",
|
||||||
"beautify-benchmark": "^0.2.4",
|
"beautify-benchmark": "^0.2.4",
|
||||||
"bundlesize": "^0.18.1",
|
|
||||||
"conventional-changelog-cli": "^2.0.34",
|
"conventional-changelog-cli": "^2.0.34",
|
||||||
"copy-webpack-plugin": "^9.0.0",
|
"copy-webpack-plugin": "^9.0.0",
|
||||||
"core-js": "^3.13.1",
|
"core-js": "^3.13.1",
|
||||||
|
@ -112,18 +116,21 @@
|
||||||
"eslint-plugin-react": "^7.24.0",
|
"eslint-plugin-react": "^7.24.0",
|
||||||
"fork-ts-checker-webpack-plugin": "^6.2.10",
|
"fork-ts-checker-webpack-plugin": "^6.2.10",
|
||||||
"html-webpack-plugin": "^5.3.1",
|
"html-webpack-plugin": "^5.3.1",
|
||||||
|
"husky": "^7.0.0",
|
||||||
"jest": "^27.0.3",
|
"jest": "^27.0.3",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mobx": "^6.3.2",
|
"mobx": "^6.3.2",
|
||||||
"prettier": "^2.3.0",
|
"prettier": "^2.3.2",
|
||||||
|
"pretty-quick": "^3.0.0",
|
||||||
"raf": "^3.4.1",
|
"raf": "^3.4.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-hot-loader": "^4.13.0",
|
"react-hot-loader": "^4.13.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.4",
|
||||||
|
"size-limit": "^7.0.4",
|
||||||
"style-loader": "^2.0.0",
|
"style-loader": "^2.0.0",
|
||||||
"styled-components": "^5.3.0",
|
"styled-components": "^5.3.0",
|
||||||
"ts-jest": "^27.0.2",
|
"ts-jest": "^27.0.2",
|
||||||
|
@ -134,7 +141,7 @@
|
||||||
"url-polyfill": "^1.1.12",
|
"url-polyfill": "^1.1.12",
|
||||||
"webpack": "^5.38.1",
|
"webpack": "^5.38.1",
|
||||||
"webpack-cli": "^4.7.2",
|
"webpack-cli": "^4.7.2",
|
||||||
"webpack-dev-server": "^3.11.2",
|
"webpack-dev-server": "^4.6.0",
|
||||||
"webpack-node-externals": "^3.0.0",
|
"webpack-node-externals": "^3.0.0",
|
||||||
"workerize-loader": "github:redocly/workerize-loader#webpack-5-dist"
|
"workerize-loader": "github:redocly/workerize-loader#webpack-5-dist"
|
||||||
},
|
},
|
||||||
|
@ -156,8 +163,7 @@
|
||||||
"json-pointer": "^0.6.1",
|
"json-pointer": "^0.6.1",
|
||||||
"lunr": "^2.3.9",
|
"lunr": "^2.3.9",
|
||||||
"mark.js": "^8.11.1",
|
"mark.js": "^8.11.1",
|
||||||
"marked": "^0.7.0",
|
"marked": "^3.0.4",
|
||||||
"memoize-one": "^5.2.1",
|
|
||||||
"mobx-react": "^7.2.0",
|
"mobx-react": "^7.2.0",
|
||||||
"openapi-sampler": "^1.0.1",
|
"openapi-sampler": "^1.0.1",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
|
@ -171,10 +177,10 @@
|
||||||
"swagger2openapi": "^7.0.6",
|
"swagger2openapi": "^7.0.6",
|
||||||
"url-template": "^2.0.8"
|
"url-template": "^2.0.8"
|
||||||
},
|
},
|
||||||
"bundlesize": [
|
"size-limit": [
|
||||||
{
|
{
|
||||||
"path": "./bundles/redoc.standalone.js",
|
"path": "./bundles/redoc.standalone.js",
|
||||||
"maxSize": "350 kB"
|
"limit": "350 kB"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"jest": {
|
"jest": {
|
||||||
|
@ -207,6 +213,6 @@
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"parser": "typescript"
|
"arrowParens": "avoid"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,16 +28,16 @@ export const StyledDropdown = styled(Dropdown)`
|
||||||
width: auto;
|
width: auto;
|
||||||
background: white;
|
background: white;
|
||||||
color: #263238;
|
color: #263238;
|
||||||
font-family: ${(props) => props.theme.typography.headings.fontFamily};
|
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: border 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
|
transition: border 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
border: 1px solid ${(props) => props.theme.colors.primary.main};
|
border: 1px solid ${props => props.theme.colors.primary.main};
|
||||||
color: ${(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};
|
box-shadow: 0px 0px 0px 1px ${props => props.theme.colors.primary.main};
|
||||||
}
|
}
|
||||||
.dropdown-selector {
|
.dropdown-selector {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
@ -48,7 +48,7 @@ export const StyledDropdown = styled(Dropdown)`
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
.dropdown-selector-value {
|
.dropdown-selector-value {
|
||||||
font-family: ${(props) => props.theme.typography.headings.fontFamily};
|
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -63,7 +63,7 @@ export const StyledDropdown = styled(Dropdown)`
|
||||||
right: 3px;
|
right: 3px;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
border-color: ${(props) => props.theme.colors.primary.main} transparent transparent;
|
border-color: ${props => props.theme.colors.primary.main} transparent transparent;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 0.35em 0.35em 0;
|
border-width: 0.35em 0.35em 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
|
@ -128,8 +128,8 @@ export const SimpleDropdown = styled(StyledDropdown)`
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
.dropdown-selector-value {
|
.dropdown-selector-value {
|
||||||
color: ${(props) => props.theme.colors.primary.main};
|
color: ${props => props.theme.colors.primary.main};
|
||||||
text-shadow: 0px 0px 0px ${(props) => props.theme.colors.primary.main};
|
text-shadow: 0px 0px 0px ${props => props.theme.colors.primary.main};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// import { transparentize } from 'polished';
|
|
||||||
|
|
||||||
import styled, { extensionsHook, media } from '../styled-components';
|
import styled, { extensionsHook, media } from '../styled-components';
|
||||||
import { deprecatedCss } from './mixins';
|
import { deprecatedCss } from './mixins';
|
||||||
|
|
||||||
|
@ -68,7 +66,7 @@ export const PropertyNameCell = styled(PropertyCell)`
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
|
|
||||||
&.deprecated {
|
&.deprecated {
|
||||||
${deprecatedCss};
|
${deprecatedCss};
|
||||||
|
@ -82,7 +80,7 @@ export const PropertyNameCell = styled(PropertyCell)`
|
||||||
export const PropertyDetailsCell = styled.td`
|
export const PropertyDetailsCell = styled.td`
|
||||||
border-bottom: 1px solid #9fb4be;
|
border-bottom: 1px solid #9fb4be;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
width: ${(props) => props.theme.schema.defaultDetailsWidth};
|
width: ${props => props.theme.schema.defaultDetailsWidth};
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
tr.expanded & {
|
tr.expanded & {
|
||||||
|
@ -92,7 +90,7 @@ export const PropertyDetailsCell = styled.td`
|
||||||
${media.lessThan('small')`
|
${media.lessThan('small')`
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-left: 1px solid ${(props) => props.theme.schema.linesColor};
|
border-left: 1px solid ${props => props.theme.schema.linesColor};
|
||||||
|
|
||||||
tr.last > & {
|
tr.last > & {
|
||||||
border-left: none;
|
border-left: none;
|
||||||
|
|
|
@ -6,7 +6,7 @@ import styled, { css } from '../styled-components';
|
||||||
import { HistoryService } from '../services';
|
import { HistoryService } from '../services';
|
||||||
|
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
export const linkifyMixin = (className) => css`
|
export const linkifyMixin = className => css`
|
||||||
${className} {
|
${className} {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: -20px;
|
margin-left: -20px;
|
||||||
|
@ -33,13 +33,13 @@ export const linkifyMixin = (className) => css`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const isModifiedEvent = (event) =>
|
const isModifiedEvent = event =>
|
||||||
!!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
|
!!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
|
||||||
|
|
||||||
export function Link(props: { to: string; className?: string; children?: any }) {
|
export function Link(props: { to: string; className?: string; children?: any }) {
|
||||||
const store = React.useContext(StoreContext);
|
const store = React.useContext(StoreContext);
|
||||||
const clickHandler = React.useCallback(
|
const clickHandler = React.useCallback(
|
||||||
(event: React.MouseEvent<HTMLAnchorElement>) => {
|
(event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
if (!store) return;
|
if (!store) return;
|
||||||
navigate(store.menu.history, event, props.to);
|
navigate(store.menu.history, event, props.to);
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,7 +12,7 @@ import styled, { createGlobalStyle } from '../styled-components';
|
||||||
* That's why the following ugly fix is required
|
* That's why the following ugly fix is required
|
||||||
*/
|
*/
|
||||||
const PerfectScrollbarConstructor =
|
const PerfectScrollbarConstructor =
|
||||||
PerfectScrollbarNamespace.default || ((PerfectScrollbarNamespace as any) as PerfectScrollbarType);
|
PerfectScrollbarNamespace.default || (PerfectScrollbarNamespace as any as PerfectScrollbarType);
|
||||||
|
|
||||||
const PSStyling = createGlobalStyle`${psStyles && psStyles.toString()}`;
|
const PSStyling = createGlobalStyle`${psStyles && psStyles.toString()}`;
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,12 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
const license =
|
const license =
|
||||||
(info.license && (
|
(info.license && (
|
||||||
<InfoSpan>
|
<InfoSpan>
|
||||||
License: {info.license.identifier ? info.license.identifier : (<a href={info.license.url}>{info.license.name}</a>)}
|
License:{' '}
|
||||||
|
{info.license.identifier ? (
|
||||||
|
info.license.identifier
|
||||||
|
) : (
|
||||||
|
<a href={info.license.url}>{info.license.name}</a>
|
||||||
|
)}
|
||||||
</InfoSpan>
|
</InfoSpan>
|
||||||
)) ||
|
)) ||
|
||||||
null;
|
null;
|
||||||
|
@ -101,8 +106,8 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
)) ||
|
)) ||
|
||||||
null}
|
null}
|
||||||
</StyledMarkdownBlock>
|
</StyledMarkdownBlock>
|
||||||
<Markdown source={store.spec.info.summary} data-role="redoc-summary"/>
|
<Markdown source={store.spec.info.summary} data-role="redoc-summary" />
|
||||||
<Markdown source={store.spec.info.description} data-role="redoc-description"/>
|
<Markdown source={store.spec.info.description} data-role="redoc-description" />
|
||||||
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
|
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
|
||||||
</MiddlePanel>
|
</MiddlePanel>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
@ -48,10 +48,10 @@ const CallbackTitleWrapper = styled.button`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const CallbackName = styled.span<{ deprecated?: boolean }>`
|
const CallbackName = styled.span<{ deprecated?: boolean }>`
|
||||||
text-decoration: ${(props) => (props.deprecated ? 'line-through' : 'none')};
|
text-decoration: ${props => (props.deprecated ? 'line-through' : 'none')};
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const OperationBadgeStyled = styled(OperationBadge)`
|
const OperationBadgeStyled = styled(OperationBadge)`
|
||||||
margin: 0px 5px 0px 0px;
|
margin: 0 5px 0 0;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -35,7 +35,7 @@ export const EndpointInfo = styled.button<{ expanded?: boolean; inverted?: boole
|
||||||
(props.expanded && !props.inverted && `border-color: ${props.theme.colors.border.dark};`) || ''}
|
(props.expanded && !props.inverted && `border-color: ${props.theme.colors.border.dark};`) || ''}
|
||||||
|
|
||||||
.${ServerRelativeURL} {
|
.${ServerRelativeURL} {
|
||||||
color: ${props => (props.inverted ? props.theme.colors.text.primary : '#ffffff')}
|
color: ${props => (props.inverted ? props.theme.colors.text.primary : '#ffffff')};
|
||||||
}
|
}
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: inset 0 2px 2px rgba(0, 0, 0, 0.45), 0 2px 0 rgba(128, 128, 128, 0.25);
|
box-shadow: inset 0 2px 2px rgba(0, 0, 0, 0.45), 0 2px 0 rgba(128, 128, 128, 0.25);
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesS
|
||||||
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
|
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
|
||||||
</FieldLabel>{' '}
|
</FieldLabel>{' '}
|
||||||
{displayedItems.map((value, idx) => {
|
{displayedItems.map((value, idx) => {
|
||||||
const exampleValue = enumSkipQuotes ? value : JSON.stringify(value);
|
const exampleValue = enumSkipQuotes ? String(value) : JSON.stringify(value);
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={idx}>
|
<React.Fragment key={idx}>
|
||||||
<ExampleValue>{exampleValue}</ExampleValue>{' '}
|
<ExampleValue>{exampleValue}</ExampleValue>{' '}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class FieldDetail extends React.PureComponent<FieldDetailProps> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = this.props.raw ? this.props.value : JSON.stringify(this.props.value);
|
const value = this.props.raw ? String(this.props.value) : JSON.stringify(this.props.value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -45,7 +45,9 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
|
||||||
} else {
|
} else {
|
||||||
const label = l('example') + ':';
|
const label = l('example') + ':';
|
||||||
const raw = !!field.in;
|
const raw = !!field.in;
|
||||||
renderedExamples = <FieldDetail label={label} value={getSerializedValue(field, field.example)} raw={raw} />;
|
renderedExamples = (
|
||||||
|
<FieldDetail label={label} value={getSerializedValue(field, field.example)} raw={raw} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,21 +66,23 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
|
||||||
)}
|
)}
|
||||||
{schema.contentEncoding && (
|
{schema.contentEncoding && (
|
||||||
<TypeFormat>
|
<TypeFormat>
|
||||||
{' '}<
|
{' '}
|
||||||
|
<
|
||||||
{schema.contentEncoding}
|
{schema.contentEncoding}
|
||||||
>{' '}
|
>{' '}
|
||||||
</TypeFormat>
|
</TypeFormat>
|
||||||
)}
|
)}
|
||||||
{schema.contentMediaType && (
|
{schema.contentMediaType && (
|
||||||
<TypeFormat>
|
<TypeFormat>
|
||||||
{' '}<
|
{' '}
|
||||||
|
<
|
||||||
{schema.contentMediaType}
|
{schema.contentMediaType}
|
||||||
>{' '}
|
>{' '}
|
||||||
</TypeFormat>
|
</TypeFormat>
|
||||||
)}
|
)}
|
||||||
{schema.title && !hideSchemaTitles && <TypeTitle> ({schema.title}) </TypeTitle>}
|
{schema.title && !hideSchemaTitles && <TypeTitle> ({schema.title}) </TypeTitle>}
|
||||||
<ConstraintsView constraints={schema.constraints} />
|
<ConstraintsView constraints={schema.constraints} />
|
||||||
<Pattern schema={schema}/>
|
<Pattern schema={schema} />
|
||||||
{schema.isCircular && <RecursiveLabel> {l('recursive')} </RecursiveLabel>}
|
{schema.isCircular && <RecursiveLabel> {l('recursive')} </RecursiveLabel>}
|
||||||
</div>
|
</div>
|
||||||
{deprecated && (
|
{deprecated && (
|
||||||
|
@ -87,7 +91,9 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<FieldDetail raw={rawDefault} label={l('default') + ':'} value={schema.default} />
|
<FieldDetail raw={rawDefault} label={l('default') + ':'} value={schema.default} />
|
||||||
{!renderDiscriminatorSwitch && <EnumValues isArrayType={isArrayType} values={schema.enum} />}{' '}
|
{!renderDiscriminatorSwitch && (
|
||||||
|
<EnumValues isArrayType={isArrayType} values={schema.enum} />
|
||||||
|
)}{' '}
|
||||||
{renderedExamples}
|
{renderedExamples}
|
||||||
{<Extensions extensions={{ ...field.extensions, ...schema.extensions }} />}
|
{<Extensions extensions={{ ...field.extensions, ...schema.extensions }} />}
|
||||||
<div>
|
<div>
|
||||||
|
@ -97,8 +103,8 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
|
||||||
<ExternalDocumentation externalDocs={schema.externalDocs} compact={true} />
|
<ExternalDocumentation externalDocs={schema.externalDocs} compact={true} />
|
||||||
)}
|
)}
|
||||||
{(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null}
|
{(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null}
|
||||||
{field.const && (<FieldDetail label={l('const') + ':'} value={field.const} />) || null}
|
{(field.const && <FieldDetail label={l('const') + ':'} value={field.const} />) || null}
|
||||||
{isArrayType && schema.items && <ArrayItemDetails schema={schema.items}/>}
|
{isArrayType && schema.items && <ArrayItemDetails schema={schema.items} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,11 +34,11 @@ class Json extends React.PureComponent<JsonProps> {
|
||||||
<button onClick={this.collapseAll}> Collapse all </button>
|
<button onClick={this.collapseAll}> Collapse all </button>
|
||||||
</SampleControls>
|
</SampleControls>
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{(options) => (
|
{options => (
|
||||||
<PrismDiv
|
<PrismDiv
|
||||||
className={this.props.className}
|
className={this.props.className}
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
ref={(node) => (this.node = node!)}
|
ref={node => (this.node = node!)}
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel),
|
__html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel),
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -6,8 +6,8 @@ export const jsonStyles = css`
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
font-size: ${(props) => props.theme.typography.code.fontSize};
|
font-size: ${props => props.theme.typography.code.fontSize};
|
||||||
|
|
||||||
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
||||||
contain: content;
|
contain: content;
|
||||||
|
@ -51,8 +51,8 @@ export const jsonStyles = css`
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 0;
|
border: 0;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
font-size: ${(props) => props.theme.typography.code.fontSize};
|
font-size: ${props => props.theme.typography.code.fontSize};
|
||||||
padding-right: 6px;
|
padding-right: 6px;
|
||||||
padding-left: 6px;
|
padding-left: 6px;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
|
|
@ -26,7 +26,6 @@ export const StyledMarkdownBlock = styled(
|
||||||
{ compact?: boolean; inline?: boolean }
|
{ compact?: boolean; inline?: boolean }
|
||||||
>,
|
>,
|
||||||
)`
|
)`
|
||||||
|
|
||||||
font-family: ${props => props.theme.typography.fontFamily};
|
font-family: ${props => props.theme.typography.fontFamily};
|
||||||
font-weight: ${props => props.theme.typography.fontWeightRegular};
|
font-weight: ${props => props.theme.typography.fontWeightRegular};
|
||||||
line-height: ${props => props.theme.typography.lineHeight};
|
line-height: ${props => props.theme.typography.lineHeight};
|
||||||
|
@ -81,7 +80,7 @@ export const StyledMarkdownBlock = styled(
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
white-space:${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
||||||
background-color: ${({ theme }) => theme.codeBlock.backgroundColor};
|
background-color: ${({ theme }) => theme.codeBlock.backgroundColor};
|
||||||
color: white;
|
color: white;
|
||||||
padding: ${props => props.theme.spacing.unit * 4}px;
|
padding: ${props => props.theme.spacing.unit * 4}px;
|
||||||
|
@ -121,7 +120,8 @@ export const StyledMarkdownBlock = styled(
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
|
||||||
ul, ol {
|
ul,
|
||||||
|
ol {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,6 @@ import { ResponsesList } from '../Responses/ResponsesList';
|
||||||
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
||||||
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
|
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
|
||||||
|
|
||||||
const OperationRow = styled(Row)`
|
|
||||||
backface-visibility: hidden;
|
|
||||||
contain: content;
|
|
||||||
overflow: hidden;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Description = styled.div`
|
const Description = styled.div`
|
||||||
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
||||||
`;
|
`;
|
||||||
|
@ -42,8 +36,8 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{(options) => (
|
{options => (
|
||||||
<OperationRow>
|
<Row>
|
||||||
<MiddlePanel>
|
<MiddlePanel>
|
||||||
<H2>
|
<H2>
|
||||||
<ShareLink to={operation.id} />
|
<ShareLink to={operation.id} />
|
||||||
|
@ -71,7 +65,7 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
<ResponseSamples operation={operation} />
|
<ResponseSamples operation={operation} />
|
||||||
<CallbackSamples callbacks={operation.callbacks} />
|
<CallbackSamples callbacks={operation.callbacks} />
|
||||||
</DarkRightPanel>
|
</DarkRightPanel>
|
||||||
</OperationRow>
|
</Row>
|
||||||
)}
|
)}
|
||||||
</OptionsContext.Consumer>
|
</OptionsContext.Consumer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -67,7 +67,10 @@ function DropdownWithinHeader(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BodyContent(props: { content: MediaContentModel; description?: string }): JSX.Element {
|
export function BodyContent(props: {
|
||||||
|
content: MediaContentModel;
|
||||||
|
description?: string;
|
||||||
|
}): JSX.Element {
|
||||||
const { content, description } = props;
|
const { content, description } = props;
|
||||||
const { isRequestType } = content;
|
const { isRequestType } = content;
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { argValueToBoolean, RedocNormalizedOptions, RedocRawOptions } from '../services/RedocNormalizedOptions';
|
import {
|
||||||
|
argValueToBoolean,
|
||||||
|
RedocNormalizedOptions,
|
||||||
|
RedocRawOptions,
|
||||||
|
} from '../services/RedocNormalizedOptions';
|
||||||
import { ErrorBoundary } from './ErrorBoundary';
|
import { ErrorBoundary } from './ErrorBoundary';
|
||||||
import { Loading } from './Loading/Loading';
|
import { Loading } from './Loading/Loading';
|
||||||
import { Redoc } from './Redoc/Redoc';
|
import { Redoc } from './Redoc/Redoc';
|
||||||
|
@ -32,4 +36,4 @@ export const RedocStandalone = function (props: RedocStandaloneProps) {
|
||||||
</StoreBuilder>
|
</StoreBuilder>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,39 +1,47 @@
|
||||||
import { observer } from 'mobx-react';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
import { ResponseModel } from '../../services/models';
|
import type { ResponseModel, MediaTypeModel } from '../../services/models';
|
||||||
import { ResponseDetails } from './ResponseDetails';
|
import { ResponseDetails } from './ResponseDetails';
|
||||||
import { ResponseDetailsWrap, StyledResponseTitle } from './styled.elements';
|
import { ResponseDetailsWrap, StyledResponseTitle } from './styled.elements';
|
||||||
|
|
||||||
@observer
|
export interface ResponseViewProps {
|
||||||
export class ResponseView extends React.Component<{ response: ResponseModel }> {
|
response: ResponseModel;
|
||||||
toggle = () => {
|
|
||||||
this.props.response.toggle();
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { headers, type, summary, description, code, expanded, content } = this.props.response;
|
|
||||||
const mimes =
|
|
||||||
content === undefined ? [] : content.mediaTypes.filter(mime => mime.schema !== undefined);
|
|
||||||
|
|
||||||
const empty = headers.length === 0 && mimes.length === 0 && !description;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<StyledResponseTitle
|
|
||||||
onClick={this.toggle}
|
|
||||||
type={type}
|
|
||||||
empty={empty}
|
|
||||||
title={summary || ''}
|
|
||||||
code={code}
|
|
||||||
opened={expanded}
|
|
||||||
/>
|
|
||||||
{expanded && !empty && (
|
|
||||||
<ResponseDetailsWrap>
|
|
||||||
<ResponseDetails response={this.props.response} />
|
|
||||||
</ResponseDetailsWrap>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ResponseView = observer(({ response }: ResponseViewProps): React.ReactElement => {
|
||||||
|
const { extensions, headers, type, summary, description, code, expanded, content } = response;
|
||||||
|
|
||||||
|
const mimes = React.useMemo<MediaTypeModel[]>(
|
||||||
|
() =>
|
||||||
|
content === undefined ? [] : content.mediaTypes.filter(mime => mime.schema !== undefined),
|
||||||
|
[content],
|
||||||
|
);
|
||||||
|
|
||||||
|
const empty = React.useMemo<boolean>(
|
||||||
|
() =>
|
||||||
|
(!extensions || Object.keys(extensions).length === 0) &&
|
||||||
|
headers.length === 0 &&
|
||||||
|
mimes.length === 0 &&
|
||||||
|
!description,
|
||||||
|
[extensions, headers, mimes, description],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<StyledResponseTitle
|
||||||
|
onClick={response.toggle}
|
||||||
|
type={type}
|
||||||
|
empty={empty}
|
||||||
|
title={summary || ''}
|
||||||
|
code={code}
|
||||||
|
opened={expanded}
|
||||||
|
/>
|
||||||
|
{expanded && !empty && (
|
||||||
|
<ResponseDetailsWrap>
|
||||||
|
<ResponseDetails response={response} />
|
||||||
|
</ResponseDetailsWrap>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
@ -7,15 +7,17 @@ import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel';
|
||||||
import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
|
import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
|
||||||
import { Schema } from '../Schema';
|
import { Schema } from '../Schema';
|
||||||
|
|
||||||
|
import { Extensions } from '../Fields/Extensions';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { ResponseHeaders } from './ResponseHeaders';
|
import { ResponseHeaders } from './ResponseHeaders';
|
||||||
|
|
||||||
export class ResponseDetails extends React.PureComponent<{ response: ResponseModel }> {
|
export class ResponseDetails extends React.PureComponent<{ response: ResponseModel }> {
|
||||||
render() {
|
render() {
|
||||||
const { description, headers, content } = this.props.response;
|
const { description, extensions, headers, content } = this.props.response;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{description && <Markdown source={description} />}
|
{description && <Markdown source={description} />}
|
||||||
|
<Extensions extensions={extensions} />
|
||||||
<ResponseHeaders headers={headers} />
|
<ResponseHeaders headers={headers} />
|
||||||
<MediaTypesSwitch content={content} renderDropdown={this.renderDropdown}>
|
<MediaTypesSwitch content={content} renderDropdown={this.renderDropdown}>
|
||||||
{({ schema }) => {
|
{({ schema }) => {
|
||||||
|
|
|
@ -14,27 +14,34 @@ export interface ResponseTitleProps {
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ResponseTitle extends React.PureComponent<ResponseTitleProps> {
|
function ResponseTitleComponent({
|
||||||
render() {
|
title,
|
||||||
const { title, type, empty, code, opened, className, onClick } = this.props;
|
type,
|
||||||
return (
|
empty,
|
||||||
<button
|
code,
|
||||||
className={className}
|
opened,
|
||||||
onClick={(!empty && onClick) || undefined}
|
className,
|
||||||
aria-expanded={opened}
|
onClick,
|
||||||
disabled={empty}
|
}: ResponseTitleProps): React.ReactElement {
|
||||||
>
|
return (
|
||||||
{!empty && (
|
<button
|
||||||
<ShelfIcon
|
className={className}
|
||||||
size={'1.5em'}
|
onClick={(!empty && onClick) || undefined}
|
||||||
color={type}
|
aria-expanded={opened}
|
||||||
direction={opened ? 'down' : 'right'}
|
disabled={empty}
|
||||||
float={'left'}
|
>
|
||||||
/>
|
{!empty && (
|
||||||
)}
|
<ShelfIcon
|
||||||
<Code>{code} </Code>
|
size={'1.5em'}
|
||||||
<Markdown compact={true} inline={true} source={title} />
|
color={type}
|
||||||
</button>
|
direction={opened ? 'down' : 'right'}
|
||||||
);
|
float={'left'}
|
||||||
}
|
/>
|
||||||
|
)}
|
||||||
|
<Code>{code} </Code>
|
||||||
|
<Markdown compact={true} inline={true} source={title} />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ResponseTitle = React.memo<ResponseTitleProps>(ResponseTitleComponent);
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// import { transparentize } from 'polished';
|
|
||||||
|
|
||||||
import { UnderlinedHeader } from '../../common-elements';
|
import { UnderlinedHeader } from '../../common-elements';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
import { ResponseTitle } from './ResponseTitle';
|
import { ResponseTitle } from './ResponseTitle';
|
||||||
|
@ -13,16 +11,14 @@ export const StyledResponseTitle = styled(ResponseTitle)`
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
background-color: #f2f2f2;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
color: ${(props) => props.theme.colors.responses[props.type].color};
|
color: ${props => props.theme.colors.responses[props.type].color};
|
||||||
background-color: ${(props) => props.theme.colors.responses[props.type].backgroundColor};
|
background-color: ${props => props.theme.colors.responses[props.type].backgroundColor};
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: auto;
|
outline: auto ${props => props.theme.colors.responses[props.type].color};
|
||||||
outline-color: ${(props) => props.theme.colors.responses[props.type].color};
|
|
||||||
}
|
}
|
||||||
${(props) =>
|
${props =>
|
||||||
(props.empty &&
|
(props.empty &&
|
||||||
`
|
`
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
|
@ -16,14 +16,17 @@ export class ArraySchema extends React.PureComponent<SchemaProps> {
|
||||||
const schema = this.props.schema;
|
const schema = this.props.schema;
|
||||||
const itemsSchema = schema.items;
|
const itemsSchema = schema.items;
|
||||||
|
|
||||||
const minMaxItems = schema.minItems === undefined && schema.maxItems === undefined ?
|
const minMaxItems =
|
||||||
'' :
|
schema.minItems === undefined && schema.maxItems === undefined
|
||||||
`(${humanizeConstraints(schema)})`;
|
? ''
|
||||||
|
: `(${humanizeConstraints(schema)})`;
|
||||||
|
|
||||||
if (schema.displayType && !itemsSchema && !minMaxItems.length) {
|
if (schema.displayType && !itemsSchema && !minMaxItems.length) {
|
||||||
return (<div>
|
return (
|
||||||
<TypeName>{schema.displayType}</TypeName>
|
<div>
|
||||||
</div>);
|
<TypeName>{schema.displayType}</TypeName>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -18,37 +18,35 @@ export interface ObjectSchemaProps extends SchemaProps {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
export const ObjectSchema = observer(
|
||||||
export class ObjectSchema extends React.Component<ObjectSchemaProps> {
|
({
|
||||||
static contextType = OptionsContext;
|
schema: { fields = [], title },
|
||||||
|
showTitle,
|
||||||
|
discriminator,
|
||||||
|
skipReadOnly,
|
||||||
|
skipWriteOnly,
|
||||||
|
}: ObjectSchemaProps) => {
|
||||||
|
const { expandSingleSchemaField, showObjectSchemaExamples } = React.useContext(OptionsContext);
|
||||||
|
|
||||||
get parentSchema() {
|
const filteredFields = React.useMemo(
|
||||||
return this.props.discriminator!.parentSchema;
|
() =>
|
||||||
}
|
skipReadOnly || skipWriteOnly
|
||||||
|
? fields.filter(
|
||||||
|
item =>
|
||||||
|
!(
|
||||||
|
(skipReadOnly && item.schema.readOnly) ||
|
||||||
|
(skipWriteOnly && item.schema.writeOnly)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: fields,
|
||||||
|
[skipReadOnly, skipWriteOnly, fields],
|
||||||
|
);
|
||||||
|
|
||||||
render() {
|
const expandByDefault = expandSingleSchemaField && filteredFields.length === 1;
|
||||||
const {
|
|
||||||
schema: { fields = [] },
|
|
||||||
showTitle,
|
|
||||||
discriminator,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const needFilter = this.props.skipReadOnly || this.props.skipWriteOnly;
|
|
||||||
|
|
||||||
const filteredFields = needFilter
|
|
||||||
? fields.filter(item => {
|
|
||||||
return !(
|
|
||||||
(this.props.skipReadOnly && item.schema.readOnly) ||
|
|
||||||
(this.props.skipWriteOnly && item.schema.writeOnly)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
: fields;
|
|
||||||
|
|
||||||
const expandByDefault = this.context.expandSingleSchemaField && filteredFields.length === 1;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PropertiesTable>
|
<PropertiesTable>
|
||||||
{showTitle && <PropertiesTableCaption>{this.props.schema.title}</PropertiesTableCaption>}
|
{showTitle && <PropertiesTableCaption>{title}</PropertiesTableCaption>}
|
||||||
<tbody>
|
<tbody>
|
||||||
{mapWithLast(filteredFields, (field, isLast) => {
|
{mapWithLast(filteredFields, (field, isLast) => {
|
||||||
return (
|
return (
|
||||||
|
@ -58,26 +56,25 @@ export class ObjectSchema extends React.Component<ObjectSchemaProps> {
|
||||||
field={field}
|
field={field}
|
||||||
expandByDefault={expandByDefault}
|
expandByDefault={expandByDefault}
|
||||||
renderDiscriminatorSwitch={
|
renderDiscriminatorSwitch={
|
||||||
(discriminator &&
|
discriminator?.fieldName === field.name
|
||||||
discriminator.fieldName === field.name &&
|
? () => (
|
||||||
(() => (
|
<DiscriminatorDropdown
|
||||||
<DiscriminatorDropdown
|
parent={discriminator!.parentSchema}
|
||||||
parent={this.parentSchema}
|
enumValues={field.schema.enum}
|
||||||
enumValues={field.schema.enum}
|
/>
|
||||||
/>
|
)
|
||||||
))) ||
|
: undefined
|
||||||
undefined
|
|
||||||
}
|
}
|
||||||
className={field.expanded ? 'expanded' : undefined}
|
className={field.expanded ? 'expanded' : undefined}
|
||||||
showExamples={false}
|
showExamples={showObjectSchemaExamples}
|
||||||
skipReadOnly={this.props.skipReadOnly}
|
skipReadOnly={skipReadOnly}
|
||||||
skipWriteOnly={this.props.skipWriteOnly}
|
skipWriteOnly={skipWriteOnly}
|
||||||
showTitle={this.props.showTitle}
|
showTitle={showTitle}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</PropertiesTable>
|
</PropertiesTable>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
);
|
||||||
|
|
|
@ -73,7 +73,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: maybe adjust FieldDetails to accept schema
|
// TODO: maybe adjust FieldDetails to accept schema
|
||||||
const field = ({
|
const field = {
|
||||||
schema,
|
schema,
|
||||||
name: '',
|
name: '',
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -82,7 +82,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
||||||
deprecated: false,
|
deprecated: false,
|
||||||
toggle: () => null,
|
toggle: () => null,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
} as any) as FieldModel; // cast needed for hot-loader to not fail
|
} as any as FieldModel; // cast needed for hot-loader to not fail
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
SearchResultsBox,
|
SearchResultsBox,
|
||||||
SearchWrap,
|
SearchWrap,
|
||||||
} from './styled.elements';
|
} from './styled.elements';
|
||||||
|
import { l } from '../../services/Labels';
|
||||||
|
|
||||||
export interface SearchBoxProps {
|
export interface SearchBoxProps {
|
||||||
search: SearchStore<string>;
|
search: SearchStore<string>;
|
||||||
|
@ -28,6 +29,7 @@ export interface SearchBoxProps {
|
||||||
|
|
||||||
export interface SearchBoxState {
|
export interface SearchBoxState {
|
||||||
results: SearchResult[];
|
results: SearchResult[];
|
||||||
|
noResults: boolean;
|
||||||
term: string;
|
term: string;
|
||||||
activeItemIdx: number;
|
activeItemIdx: number;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +41,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
results: [],
|
results: [],
|
||||||
|
noResults: false,
|
||||||
term: '',
|
term: '',
|
||||||
activeItemIdx: -1,
|
activeItemIdx: -1,
|
||||||
};
|
};
|
||||||
|
@ -47,6 +50,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
||||||
clearResults(term: string) {
|
clearResults(term: string) {
|
||||||
this.setState({
|
this.setState({
|
||||||
results: [],
|
results: [],
|
||||||
|
noResults: false,
|
||||||
term,
|
term,
|
||||||
});
|
});
|
||||||
this.props.marker.unmark();
|
this.props.marker.unmark();
|
||||||
|
@ -55,6 +59,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
||||||
clear = () => {
|
clear = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
results: [],
|
results: [],
|
||||||
|
noResults: false,
|
||||||
term: '',
|
term: '',
|
||||||
activeItemIdx: -1,
|
activeItemIdx: -1,
|
||||||
});
|
});
|
||||||
|
@ -95,6 +100,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
||||||
setResults(results: SearchResult[], term: string) {
|
setResults(results: SearchResult[], term: string) {
|
||||||
this.setState({
|
this.setState({
|
||||||
results,
|
results,
|
||||||
|
noResults: results.length === 0,
|
||||||
});
|
});
|
||||||
this.props.marker.mark(term);
|
this.props.marker.mark(term);
|
||||||
}
|
}
|
||||||
|
@ -166,6 +172,9 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
||||||
</SearchResultsBox>
|
</SearchResultsBox>
|
||||||
</PerfectScrollbarWrap>
|
</PerfectScrollbarWrap>
|
||||||
)}
|
)}
|
||||||
|
{this.state.term && this.state.noResults ? (
|
||||||
|
<SearchResultsBox data-role="search:results">{l('noResultsFound')}</SearchResultsBox>
|
||||||
|
) : null}
|
||||||
</SearchWrap>
|
</SearchWrap>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// import { transparentize } from 'polished';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import styled, { media } from '../../styled-components';
|
import styled, { media } from '../../styled-components';
|
||||||
|
@ -8,8 +7,8 @@ import { SecurityRequirementModel } from '../../services/models/SecurityRequirem
|
||||||
import { linksCss } from '../Markdown/styled.elements';
|
import { linksCss } from '../Markdown/styled.elements';
|
||||||
|
|
||||||
const ScopeName = styled.code`
|
const ScopeName = styled.code`
|
||||||
font-size: ${(props) => props.theme.typography.code.fontSize};
|
font-size: ${props => props.theme.typography.code.fontSize};
|
||||||
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
border: 1px solid ${({ theme }) => theme.colors.border.dark};
|
border: 1px solid ${({ theme }) => theme.colors.border.dark};
|
||||||
margin: 0 3px;
|
margin: 0 3px;
|
||||||
padding: 0.2em;
|
padding: 0.2em;
|
||||||
|
@ -68,12 +67,12 @@ export class SecurityRequirement extends React.PureComponent<SecurityRequirement
|
||||||
return (
|
return (
|
||||||
<SecurityRequirementOrWrap>
|
<SecurityRequirementOrWrap>
|
||||||
{security.schemes.length ? (
|
{security.schemes.length ? (
|
||||||
security.schemes.map((scheme) => {
|
security.schemes.map(scheme => {
|
||||||
return (
|
return (
|
||||||
<SecurityRequirementAndWrap key={scheme.id}>
|
<SecurityRequirementAndWrap key={scheme.id}>
|
||||||
<Link to={scheme.sectionId}>{scheme.id}</Link>
|
<Link to={scheme.sectionId}>{scheme.id}</Link>
|
||||||
{scheme.scopes.length > 0 && ' ('}
|
{scheme.scopes.length > 0 && ' ('}
|
||||||
{scheme.scopes.map((scope) => (
|
{scheme.scopes.map(scope => (
|
||||||
<ScopeName key={scope}>{scope}</ScopeName>
|
<ScopeName key={scope}>{scope}</ScopeName>
|
||||||
))}
|
))}
|
||||||
{scheme.scopes.length > 0 && ') '}
|
{scheme.scopes.length > 0 && ') '}
|
||||||
|
@ -93,7 +92,7 @@ const AuthHeaderColumn = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const SecuritiesColumn = styled.div`
|
const SecuritiesColumn = styled.div`
|
||||||
width: ${(props) => props.theme.schema.defaultDetailsWidth};
|
width: ${props => props.theme.schema.defaultDetailsWidth};
|
||||||
${media.lessThan('small')`
|
${media.lessThan('small')`
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
`}
|
`}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// import { observe } from 'mobx';
|
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
|
@ -8,6 +7,7 @@ import { shortenHTTPVerb } from '../../utils/openapi';
|
||||||
import { MenuItems } from './MenuItems';
|
import { MenuItems } from './MenuItems';
|
||||||
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
|
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
|
||||||
import { l } from '../../services/Labels';
|
import { l } from '../../services/Labels';
|
||||||
|
import { scrollIntoViewIfNeeded } from '../../utils';
|
||||||
|
|
||||||
export interface MenuItemProps {
|
export interface MenuItemProps {
|
||||||
item: IMenuItem;
|
item: IMenuItem;
|
||||||
|
@ -34,7 +34,7 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||||
|
|
||||||
scrollIntoViewIfActive() {
|
scrollIntoViewIfActive() {
|
||||||
if (this.props.item.active && this.ref.current) {
|
if (this.props.item.active && this.ref.current) {
|
||||||
this.ref.current.scrollIntoViewIfNeeded();
|
scrollIntoViewIfNeeded(this.ref.current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,8 +46,8 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||||
<OperationMenuItemContent {...this.props} item={item as OperationModel} />
|
<OperationMenuItemContent {...this.props} item={item as OperationModel} />
|
||||||
) : (
|
) : (
|
||||||
<MenuItemLabel depth={item.depth} active={item.active} type={item.type} ref={this.ref}>
|
<MenuItemLabel depth={item.depth} active={item.active} type={item.type} ref={this.ref}>
|
||||||
<MenuItemTitle title={item.name}>
|
<MenuItemTitle title={item.sidebarLabel}>
|
||||||
{item.name}
|
{item.sidebarLabel}
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</MenuItemTitle>
|
</MenuItemTitle>
|
||||||
{(item.depth > 0 && item.items.length > 0 && (
|
{(item.depth > 0 && item.items.length > 0 && (
|
||||||
|
@ -78,7 +78,7 @@ export class OperationMenuItemContent extends React.Component<OperationMenuItemC
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (this.props.item.active && this.ref.current) {
|
if (this.props.item.active && this.ref.current) {
|
||||||
this.ref.current.scrollIntoViewIfNeeded();
|
scrollIntoViewIfNeeded(this.ref.current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ export class OperationMenuItemContent extends React.Component<OperationMenuItemC
|
||||||
<OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge>
|
<OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge>
|
||||||
)}
|
)}
|
||||||
<MenuItemTitle width="calc(100% - 38px)">
|
<MenuItemTitle width="calc(100% - 38px)">
|
||||||
{item.name}
|
{item.sidebarLabel}
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</MenuItemTitle>
|
</MenuItemTitle>
|
||||||
</MenuItemLabel>
|
</MenuItemLabel>
|
||||||
|
|
|
@ -37,7 +37,6 @@ export class SideMenu extends React.Component<{ menu: MenuStore; className?: str
|
||||||
if (item && item.active && this.context.menuToggle) {
|
if (item && item.active && this.context.menuToggle) {
|
||||||
return item.expanded ? item.collapse() : item.expand();
|
return item.expanded ? item.collapse() : item.expand();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.menu.activateAndScroll(item, true);
|
this.props.menu.activateAndScroll(item, true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this._updateScroll) {
|
if (this._updateScroll) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ const { Provider, Consumer } = StoreContext;
|
||||||
export { Provider as StoreProvider, Consumer as StoreConsumer, StoreContext };
|
export { Provider as StoreProvider, Consumer as StoreConsumer, StoreContext };
|
||||||
|
|
||||||
export function StoreBuilder(props: StoreBuilderProps) {
|
export function StoreBuilder(props: StoreBuilderProps) {
|
||||||
const {spec, specUrl, options, onLoaded, children } = props;
|
const { spec, specUrl, options, onLoaded, children } = props;
|
||||||
|
|
||||||
const [resolvedSpec, setResolvedSpec] = React.useState<any>(null);
|
const [resolvedSpec, setResolvedSpec] = React.useState<any>(null);
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ export function StoreBuilder(props: StoreBuilderProps) {
|
||||||
setResolvedSpec(resolved);
|
setResolvedSpec(resolved);
|
||||||
}
|
}
|
||||||
load();
|
load();
|
||||||
}, [spec, specUrl])
|
}, [spec, specUrl]);
|
||||||
|
|
||||||
const store = React.useMemo(() => {
|
const store = React.useMemo(() => {
|
||||||
if (!resolvedSpec) return null;
|
if (!resolvedSpec) return null;
|
||||||
|
@ -62,7 +62,7 @@ export function StoreBuilder(props: StoreBuilderProps) {
|
||||||
if (store && onLoaded) {
|
if (store && onLoaded) {
|
||||||
onLoaded();
|
onLoaded();
|
||||||
}
|
}
|
||||||
}, [store, onLoaded])
|
}, [store, onLoaded]);
|
||||||
|
|
||||||
return children({
|
return children({
|
||||||
loading: !store,
|
loading: !store,
|
||||||
|
|
|
@ -26,7 +26,7 @@ describe('Components', () => {
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
const schemaViewElement = shallow(<Schema schema={schema} />).getElement();
|
const schemaViewElement = shallow(<Schema schema={schema} />).getElement();
|
||||||
expect(schemaViewElement.type).toEqual(ObjectSchema);
|
expect(schemaViewElement).toMatchSnapshot();
|
||||||
expect(schemaViewElement.props.discriminator).toBeDefined();
|
expect(schemaViewElement.props.discriminator).toBeDefined();
|
||||||
expect(schemaViewElement.props.discriminator.parentSchema).toBeDefined();
|
expect(schemaViewElement.props.discriminator.parentSchema).toBeDefined();
|
||||||
expect(schemaViewElement.props.discriminator.fieldName).toEqual('type');
|
expect(schemaViewElement.props.discriminator.fieldName).toEqual('type');
|
||||||
|
|
|
@ -10,14 +10,15 @@ const options = new RedocNormalizedOptions({});
|
||||||
describe('Components', () => {
|
describe('Components', () => {
|
||||||
describe('SecurityRequirement', () => {
|
describe('SecurityRequirement', () => {
|
||||||
describe('SecurityRequirement', () => {
|
describe('SecurityRequirement', () => {
|
||||||
it('should render \'None\' when empty object in security open api', () => {
|
it("should render 'None' when empty object in security open api", () => {
|
||||||
const parser = new OpenAPIParser({ openapi: '3.0', info: { title: 'test', version: '0' }, paths: {} },
|
const parser = new OpenAPIParser(
|
||||||
|
{ openapi: '3.0', info: { title: 'test', version: '0' }, paths: {} },
|
||||||
undefined,
|
undefined,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
const securityRequirement = new SecurityRequirementModel({}, parser);
|
const securityRequirement = new SecurityRequirementModel({}, parser);
|
||||||
const securityElement = shallow(
|
const securityElement = shallow(
|
||||||
<SecurityRequirement key={1} security={securityRequirement} />
|
<SecurityRequirement key={1} security={securityRequirement} />,
|
||||||
).getElement();
|
).getElement();
|
||||||
expect(securityElement.props.children.type.target).toEqual('span');
|
expect(securityElement.props.children.type.target).toEqual('span');
|
||||||
expect(securityElement.props.children.props.children).toEqual('None');
|
expect(securityElement.props.children.props.children).toEqual('None');
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,6 +10,7 @@ export interface LabelsConfig {
|
||||||
arrayOf: string;
|
arrayOf: string;
|
||||||
webhook: string;
|
webhook: string;
|
||||||
const: string;
|
const: string;
|
||||||
|
noResultsFound: string;
|
||||||
download: string;
|
download: string;
|
||||||
downloadSpecification: string;
|
downloadSpecification: string;
|
||||||
responses: string;
|
responses: string;
|
||||||
|
@ -32,6 +33,7 @@ const labels: LabelsConfig = {
|
||||||
arrayOf: 'Array of ',
|
arrayOf: 'Array of ',
|
||||||
webhook: 'Event',
|
webhook: 'Event',
|
||||||
const: 'Value',
|
const: 'Value',
|
||||||
|
noResultsFound: 'No results found',
|
||||||
download: 'Download',
|
download: 'Download',
|
||||||
downloadSpecification: 'Download OpenAPI specification',
|
downloadSpecification: 'Download OpenAPI specification',
|
||||||
responses: 'Responses',
|
responses: 'Responses',
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import * as React from 'react';
|
||||||
import * as marked from 'marked';
|
import * as marked from 'marked';
|
||||||
|
|
||||||
import { highlight, safeSlugify, unescapeHTMLChars } from '../utils';
|
import { highlight, safeSlugify, unescapeHTMLChars } from '../utils';
|
||||||
|
@ -56,10 +57,12 @@ export class MarkdownRenderer {
|
||||||
headings: MarkdownHeading[] = [];
|
headings: MarkdownHeading[] = [];
|
||||||
currentTopHeading: MarkdownHeading;
|
currentTopHeading: MarkdownHeading;
|
||||||
|
|
||||||
|
public parser: marked.Parser; // required initialization, `parser` is used by `marked.Renderer` instance under the hood
|
||||||
private headingEnhanceRenderer: marked.Renderer;
|
private headingEnhanceRenderer: marked.Renderer;
|
||||||
private originalHeadingRule: typeof marked.Renderer.prototype.heading;
|
private originalHeadingRule: typeof marked.Renderer.prototype.heading;
|
||||||
|
|
||||||
constructor(public options?: RedocNormalizedOptions) {
|
constructor(public options?: RedocNormalizedOptions) {
|
||||||
|
this.parser = new marked.Parser();
|
||||||
this.headingEnhanceRenderer = new marked.Renderer();
|
this.headingEnhanceRenderer = new marked.Renderer();
|
||||||
this.originalHeadingRule = this.headingEnhanceRenderer.heading.bind(
|
this.originalHeadingRule = this.headingEnhanceRenderer.heading.bind(
|
||||||
this.headingEnhanceRenderer,
|
this.headingEnhanceRenderer,
|
||||||
|
@ -98,7 +101,7 @@ export class MarkdownRenderer {
|
||||||
|
|
||||||
attachHeadingsDescriptions(rawText: string) {
|
attachHeadingsDescriptions(rawText: string) {
|
||||||
const buildRegexp = (heading: MarkdownHeading) => {
|
const buildRegexp = (heading: MarkdownHeading) => {
|
||||||
return new RegExp(`##?\\s+${heading.name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}`);
|
return new RegExp(`##?\\s+${heading.name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}\s*\n`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const flatHeadings = this.flattenHeadings(this.headings);
|
const flatHeadings = this.flattenHeadings(this.headings);
|
||||||
|
@ -121,10 +124,7 @@ export class MarkdownRenderer {
|
||||||
prevRegexp = regexp;
|
prevRegexp = regexp;
|
||||||
prevPos = currentPos;
|
prevPos = currentPos;
|
||||||
}
|
}
|
||||||
prevHeading.description = rawText
|
prevHeading.description = rawText.substring(prevPos).replace(prevRegexp, '').trim();
|
||||||
.substring(prevPos)
|
|
||||||
.replace(prevRegexp, '')
|
|
||||||
.trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
headingRule = (
|
headingRule = (
|
||||||
|
@ -132,7 +132,7 @@ export class MarkdownRenderer {
|
||||||
level: 1 | 2 | 3 | 4 | 5 | 6,
|
level: 1 | 2 | 3 | 4 | 5 | 6,
|
||||||
raw: string,
|
raw: string,
|
||||||
slugger: marked.Slugger,
|
slugger: marked.Slugger,
|
||||||
) => {
|
): string => {
|
||||||
if (level === 1) {
|
if (level === 1) {
|
||||||
this.currentTopHeading = this.saveHeading(text, level);
|
this.currentTopHeading = this.saveHeading(text, level);
|
||||||
} else if (level === 2) {
|
} else if (level === 2) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
SECURITY_DEFINITIONS_COMPONENT_NAME,
|
SECURITY_DEFINITIONS_COMPONENT_NAME,
|
||||||
setSecuritySchemePrefix,
|
setSecuritySchemePrefix,
|
||||||
JsonPointer,
|
JsonPointer,
|
||||||
|
alphabeticallyByProp,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { MarkdownRenderer } from './MarkdownRenderer';
|
import { MarkdownRenderer } from './MarkdownRenderer';
|
||||||
import { GroupModel, OperationModel } from './models';
|
import { GroupModel, OperationModel } from './models';
|
||||||
|
@ -130,9 +131,11 @@ export class MenuBuilder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns array of OperationsGroup items for the tags of the group or for all tags
|
* Returns array of OperationsGroup items for the tags of the group or for all tags
|
||||||
|
* @param parser
|
||||||
* @param tagsMap tags info returned from `getTagsWithOperations`
|
* @param tagsMap tags info returned from `getTagsWithOperations`
|
||||||
* @param parent parent item
|
* @param parent parent item
|
||||||
* @param group group which this tag belongs to. if not provided gets all tags
|
* @param group group which this tag belongs to. if not provided gets all tags
|
||||||
|
* @param options normalized options
|
||||||
*/
|
*/
|
||||||
static getTagsItems(
|
static getTagsItems(
|
||||||
parser: OpenAPIParser,
|
parser: OpenAPIParser,
|
||||||
|
@ -183,14 +186,21 @@ export class MenuBuilder {
|
||||||
|
|
||||||
res.push(item);
|
res.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.sortTagsAlphabetically) {
|
||||||
|
res.sort(alphabeticallyByProp<GroupModel | OperationModel>('name'));
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns array of Operation items for the tag
|
* Returns array of Operation items for the tag
|
||||||
|
* @param parser
|
||||||
* @param parent parent OperationsGroup
|
* @param parent parent OperationsGroup
|
||||||
* @param tag tag info returned from `getTagsWithOperations`
|
* @param tag tag info returned from `getTagsWithOperations`
|
||||||
* @param depth items depth
|
* @param depth items depth
|
||||||
|
* @param options - normalized options
|
||||||
*/
|
*/
|
||||||
static getOperationsItems(
|
static getOperationsItems(
|
||||||
parser: OpenAPIParser,
|
parser: OpenAPIParser,
|
||||||
|
@ -209,6 +219,11 @@ export class MenuBuilder {
|
||||||
operation.depth = depth;
|
operation.depth = depth;
|
||||||
res.push(operation);
|
res.push(operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.sortOperationsAlphabetically) {
|
||||||
|
res.sort(alphabeticallyByProp<OperationModel>('name'));
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +241,7 @@ export class MenuBuilder {
|
||||||
getTags(parser, webhooks, true);
|
getTags(parser, webhooks, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spec.paths){
|
if (spec.paths) {
|
||||||
getTags(parser, spec.paths);
|
getTags(parser, spec.paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ export interface IMenuItem {
|
||||||
id: string;
|
id: string;
|
||||||
absoluteIdx?: number;
|
absoluteIdx?: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
sidebarLabel: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
depth: number;
|
depth: number;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
|
|
@ -193,7 +193,10 @@ export class OpenAPIParser {
|
||||||
if (keys.length === 0) {
|
if (keys.length === 0) {
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
if (mergeAsAllOf && keys.some((k) => k !== 'description' && k !== 'title' && k !== 'externalDocs')) {
|
if (
|
||||||
|
mergeAsAllOf &&
|
||||||
|
keys.some(k => k !== 'description' && k !== 'title' && k !== 'externalDocs')
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
allOf: [rest, resolved],
|
allOf: [rest, resolved],
|
||||||
};
|
};
|
||||||
|
@ -244,7 +247,7 @@ export class OpenAPIParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
const allOfSchemas = schema.allOf
|
const allOfSchemas = schema.allOf
|
||||||
.map((subSchema) => {
|
.map(subSchema => {
|
||||||
if (subSchema && subSchema.$ref && used$Refs.has(subSchema.$ref)) {
|
if (subSchema && subSchema.$ref && used$Refs.has(subSchema.$ref)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -258,7 +261,7 @@ export class OpenAPIParser {
|
||||||
schema: subMerged,
|
schema: subMerged,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((child) => child !== undefined) as Array<{
|
.filter(child => child !== undefined) as Array<{
|
||||||
$ref: string | undefined;
|
$ref: string | undefined;
|
||||||
schema: MergedOpenAPISchema;
|
schema: MergedOpenAPISchema;
|
||||||
}>;
|
}>;
|
||||||
|
@ -337,7 +340,7 @@ export class OpenAPIParser {
|
||||||
const def = this.deref(schemas[defName]);
|
const def = this.deref(schemas[defName]);
|
||||||
if (
|
if (
|
||||||
def.allOf !== undefined &&
|
def.allOf !== undefined &&
|
||||||
def.allOf.find((obj) => obj.$ref !== undefined && $refs.indexOf(obj.$ref) > -1)
|
def.allOf.find(obj => obj.$ref !== undefined && $refs.indexOf(obj.$ref) > -1)
|
||||||
) {
|
) {
|
||||||
res['#/components/schemas/' + defName] = [def['x-discriminator-value'] || defName];
|
res['#/components/schemas/' + defName] = [def['x-discriminator-value'] || defName];
|
||||||
}
|
}
|
||||||
|
@ -363,7 +366,7 @@ export class OpenAPIParser {
|
||||||
const beforeAllOf = allOf.slice(0, i);
|
const beforeAllOf = allOf.slice(0, i);
|
||||||
const afterAllOf = allOf.slice(i + 1);
|
const afterAllOf = allOf.slice(i + 1);
|
||||||
return {
|
return {
|
||||||
oneOf: sub.oneOf.map((part) => {
|
oneOf: sub.oneOf.map(part => {
|
||||||
const merged = this.mergeAllOf({
|
const merged = this.mergeAllOf({
|
||||||
allOf: [...beforeAllOf, part, ...afterAllOf],
|
allOf: [...beforeAllOf, part, ...afterAllOf],
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,6 +5,11 @@ import { isNumeric, mergeObjects } from '../utils/helpers';
|
||||||
import { LabelsConfigRaw, setRedocLabels } from './Labels';
|
import { LabelsConfigRaw, setRedocLabels } from './Labels';
|
||||||
import { MDXComponentMeta } from './MarkdownRenderer';
|
import { MDXComponentMeta } from './MarkdownRenderer';
|
||||||
|
|
||||||
|
export enum SideNavStyleEnum {
|
||||||
|
SummaryOnly = 'summary-only',
|
||||||
|
PathOnly = 'path-only',
|
||||||
|
}
|
||||||
|
|
||||||
export interface RedocRawOptions {
|
export interface RedocRawOptions {
|
||||||
theme?: ThemeInterface;
|
theme?: ThemeInterface;
|
||||||
scrollYOffset?: number | string | (() => number);
|
scrollYOffset?: number | string | (() => number);
|
||||||
|
@ -13,6 +18,8 @@ export interface RedocRawOptions {
|
||||||
requiredPropsFirst?: boolean | string;
|
requiredPropsFirst?: boolean | string;
|
||||||
sortPropsAlphabetically?: boolean | string;
|
sortPropsAlphabetically?: boolean | string;
|
||||||
sortEnumValuesAlphabetically?: boolean | string;
|
sortEnumValuesAlphabetically?: boolean | string;
|
||||||
|
sortOperationsAlphabetically?: boolean | string;
|
||||||
|
sortTagsAlphabetically?: boolean | string;
|
||||||
noAutoAuth?: boolean | string;
|
noAutoAuth?: boolean | string;
|
||||||
nativeScrollbars?: boolean | string;
|
nativeScrollbars?: boolean | string;
|
||||||
pathInMiddlePanel?: boolean | string;
|
pathInMiddlePanel?: boolean | string;
|
||||||
|
@ -22,6 +29,7 @@ export interface RedocRawOptions {
|
||||||
disableSearch?: boolean | string;
|
disableSearch?: boolean | string;
|
||||||
onlyRequiredInSamples?: boolean | string;
|
onlyRequiredInSamples?: boolean | string;
|
||||||
showExtensions?: boolean | string | string[];
|
showExtensions?: boolean | string | string[];
|
||||||
|
sideNavStyle?: SideNavStyleEnum;
|
||||||
hideSingleRequestSampleTab?: boolean | string;
|
hideSingleRequestSampleTab?: boolean | string;
|
||||||
menuToggle?: boolean | string;
|
menuToggle?: boolean | string;
|
||||||
jsonSampleExpandLevel?: number | string | 'all';
|
jsonSampleExpandLevel?: number | string | 'all';
|
||||||
|
@ -29,6 +37,7 @@ export interface RedocRawOptions {
|
||||||
simpleOneOfTypeLabel?: boolean | string;
|
simpleOneOfTypeLabel?: boolean | string;
|
||||||
payloadSampleIdx?: number;
|
payloadSampleIdx?: number;
|
||||||
expandSingleSchemaField?: boolean | string;
|
expandSingleSchemaField?: boolean | string;
|
||||||
|
showObjectSchemaExamples?: boolean | string;
|
||||||
|
|
||||||
unstable_ignoreMimeParameters?: boolean;
|
unstable_ignoreMimeParameters?: boolean;
|
||||||
|
|
||||||
|
@ -72,7 +81,7 @@ export class RedocNormalizedOptions {
|
||||||
}
|
}
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
const res = {};
|
const res = {};
|
||||||
value.split(',').forEach((code) => {
|
value.split(',').forEach(code => {
|
||||||
res[code.trim()] = true;
|
res[code.trim()] = true;
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
|
@ -138,7 +147,23 @@ export class RedocNormalizedOptions {
|
||||||
case 'false':
|
case 'false':
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return value.split(',').map((ext) => ext.trim());
|
return value.split(',').map(ext => ext.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static normalizeSideNavStyle(value: RedocRawOptions['sideNavStyle']): SideNavStyleEnum {
|
||||||
|
const defaultValue = SideNavStyleEnum.SummaryOnly;
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (value) {
|
||||||
|
case defaultValue:
|
||||||
|
return value;
|
||||||
|
case SideNavStyleEnum.PathOnly:
|
||||||
|
return SideNavStyleEnum.PathOnly;
|
||||||
|
default:
|
||||||
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +206,8 @@ export class RedocNormalizedOptions {
|
||||||
requiredPropsFirst: boolean;
|
requiredPropsFirst: boolean;
|
||||||
sortPropsAlphabetically: boolean;
|
sortPropsAlphabetically: boolean;
|
||||||
sortEnumValuesAlphabetically: boolean;
|
sortEnumValuesAlphabetically: boolean;
|
||||||
|
sortOperationsAlphabetically: boolean;
|
||||||
|
sortTagsAlphabetically: boolean;
|
||||||
noAutoAuth: boolean;
|
noAutoAuth: boolean;
|
||||||
nativeScrollbars: boolean;
|
nativeScrollbars: boolean;
|
||||||
pathInMiddlePanel: boolean;
|
pathInMiddlePanel: boolean;
|
||||||
|
@ -189,6 +216,7 @@ export class RedocNormalizedOptions {
|
||||||
disableSearch: boolean;
|
disableSearch: boolean;
|
||||||
onlyRequiredInSamples: boolean;
|
onlyRequiredInSamples: boolean;
|
||||||
showExtensions: boolean | string[];
|
showExtensions: boolean | string[];
|
||||||
|
sideNavStyle: SideNavStyleEnum;
|
||||||
hideSingleRequestSampleTab: boolean;
|
hideSingleRequestSampleTab: boolean;
|
||||||
menuToggle: boolean;
|
menuToggle: boolean;
|
||||||
jsonSampleExpandLevel: number;
|
jsonSampleExpandLevel: number;
|
||||||
|
@ -197,6 +225,7 @@ export class RedocNormalizedOptions {
|
||||||
simpleOneOfTypeLabel: boolean;
|
simpleOneOfTypeLabel: boolean;
|
||||||
payloadSampleIdx: number;
|
payloadSampleIdx: number;
|
||||||
expandSingleSchemaField: boolean;
|
expandSingleSchemaField: boolean;
|
||||||
|
showObjectSchemaExamples: boolean;
|
||||||
|
|
||||||
/* tslint:disable-next-line */
|
/* tslint:disable-next-line */
|
||||||
unstable_ignoreMimeParameters: boolean;
|
unstable_ignoreMimeParameters: boolean;
|
||||||
|
@ -239,6 +268,8 @@ export class RedocNormalizedOptions {
|
||||||
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
|
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
|
||||||
this.sortPropsAlphabetically = argValueToBoolean(raw.sortPropsAlphabetically);
|
this.sortPropsAlphabetically = argValueToBoolean(raw.sortPropsAlphabetically);
|
||||||
this.sortEnumValuesAlphabetically = argValueToBoolean(raw.sortEnumValuesAlphabetically);
|
this.sortEnumValuesAlphabetically = argValueToBoolean(raw.sortEnumValuesAlphabetically);
|
||||||
|
this.sortOperationsAlphabetically = argValueToBoolean(raw.sortOperationsAlphabetically);
|
||||||
|
this.sortTagsAlphabetically = argValueToBoolean(raw.sortTagsAlphabetically);
|
||||||
this.noAutoAuth = argValueToBoolean(raw.noAutoAuth);
|
this.noAutoAuth = argValueToBoolean(raw.noAutoAuth);
|
||||||
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
|
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
|
||||||
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);
|
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);
|
||||||
|
@ -247,6 +278,7 @@ export class RedocNormalizedOptions {
|
||||||
this.disableSearch = argValueToBoolean(raw.disableSearch);
|
this.disableSearch = argValueToBoolean(raw.disableSearch);
|
||||||
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
|
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
|
||||||
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
|
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
|
||||||
|
this.sideNavStyle = RedocNormalizedOptions.normalizeSideNavStyle(raw.sideNavStyle);
|
||||||
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
|
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
|
||||||
this.menuToggle = argValueToBoolean(raw.menuToggle, true);
|
this.menuToggle = argValueToBoolean(raw.menuToggle, true);
|
||||||
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
|
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
|
||||||
|
@ -257,6 +289,7 @@ export class RedocNormalizedOptions {
|
||||||
this.simpleOneOfTypeLabel = argValueToBoolean(raw.simpleOneOfTypeLabel);
|
this.simpleOneOfTypeLabel = argValueToBoolean(raw.simpleOneOfTypeLabel);
|
||||||
this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx);
|
this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx);
|
||||||
this.expandSingleSchemaField = argValueToBoolean(raw.expandSingleSchemaField);
|
this.expandSingleSchemaField = argValueToBoolean(raw.expandSingleSchemaField);
|
||||||
|
this.showObjectSchemaExamples = argValueToBoolean(raw.showObjectSchemaExamples);
|
||||||
|
|
||||||
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);
|
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);
|
||||||
|
|
||||||
|
@ -266,7 +299,7 @@ export class RedocNormalizedOptions {
|
||||||
this.maxDisplayedEnumValues = argValueToNumber(raw.maxDisplayedEnumValues);
|
this.maxDisplayedEnumValues = argValueToNumber(raw.maxDisplayedEnumValues);
|
||||||
const ignoreNamedSchemas = Array.isArray(raw.ignoreNamedSchemas)
|
const ignoreNamedSchemas = Array.isArray(raw.ignoreNamedSchemas)
|
||||||
? raw.ignoreNamedSchemas
|
? raw.ignoreNamedSchemas
|
||||||
: raw.ignoreNamedSchemas?.split(',').map((s) => s.trim());
|
: raw.ignoreNamedSchemas?.split(',').map(s => s.trim());
|
||||||
this.ignoreNamedSchemas = new Set(ignoreNamedSchemas);
|
this.ignoreNamedSchemas = new Set(ignoreNamedSchemas);
|
||||||
this.hideSchemaPattern = argValueToBoolean(raw.hideSchemaPattern);
|
this.hideSchemaPattern = argValueToBoolean(raw.hideSchemaPattern);
|
||||||
this.generatedPayloadSamplesMaxDepth =
|
this.generatedPayloadSamplesMaxDepth =
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class SearchStore<T> {
|
||||||
|
|
||||||
fromExternalJS(path?: string, exportName?: string) {
|
fromExternalJS(path?: string, exportName?: string) {
|
||||||
if (path && exportName) {
|
if (path && exportName) {
|
||||||
this.searchWorker.fromExternalJS(path, exportName)
|
this.searchWorker.fromExternalJS(path, exportName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
import * as lunr from 'lunr';
|
import * as lunr from 'lunr';
|
||||||
|
|
||||||
try {
|
|
||||||
// tslint:disable-next-line
|
|
||||||
require('core-js/es/promise'); // bundle into worker
|
|
||||||
} catch (_) {
|
|
||||||
// nope
|
|
||||||
}
|
|
||||||
|
|
||||||
/* just for better typings */
|
/* just for better typings */
|
||||||
export default class Worker {
|
export default class Worker {
|
||||||
add: typeof add = add;
|
add: typeof add = add;
|
||||||
|
|
|
@ -28,7 +28,10 @@ export class SpecStore {
|
||||||
this.externalDocs = this.parser.spec.externalDocs;
|
this.externalDocs = this.parser.spec.externalDocs;
|
||||||
this.contentItems = MenuBuilder.buildStructure(this.parser, this.options);
|
this.contentItems = MenuBuilder.buildStructure(this.parser, this.options);
|
||||||
this.securitySchemes = new SecuritySchemesModel(this.parser);
|
this.securitySchemes = new SecuritySchemesModel(this.parser);
|
||||||
const webhookPath: Referenced<OpenAPIPath> = {...this.parser?.spec?.['x-webhooks'], ...this.parser?.spec.webhooks};
|
const webhookPath: Referenced<OpenAPIPath> = {
|
||||||
|
...this.parser?.spec?.['x-webhooks'],
|
||||||
|
...this.parser?.spec.webhooks,
|
||||||
|
};
|
||||||
this.webhooks = new WebhookModel(this.parser, options, webhookPath);
|
this.webhooks = new WebhookModel(this.parser, options, webhookPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,5 @@ describe('Models', () => {
|
||||||
|
|
||||||
expect(parser.shallowDeref(schemaOrRef)).toMatchSnapshot();
|
expect(parser.shallowDeref(schemaOrRef)).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -54,8 +54,8 @@ describe('Models', () => {
|
||||||
license: {
|
license: {
|
||||||
name: 'MIT',
|
name: 'MIT',
|
||||||
identifier: 'MIT',
|
identifier: 'MIT',
|
||||||
url: 'https://opensource.org/licenses/MIT'
|
url: 'https://opensource.org/licenses/MIT',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ describe('Models', () => {
|
||||||
expect(contentItems[0].id).toEqual('tag/pet');
|
expect(contentItems[0].id).toEqual('tag/pet');
|
||||||
expect(contentItems[0].name).toEqual('pet');
|
expect(contentItems[0].name).toEqual('pet');
|
||||||
expect(contentItems[0].type).toEqual('tag');
|
expect(contentItems[0].type).toEqual('tag');
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,24 +20,38 @@ describe('Models', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should calculate response type based on code', () => {
|
test('should calculate response type based on code', () => {
|
||||||
let resp = new ResponseModel({...props, code: '200' });
|
let resp = new ResponseModel({ ...props, code: '200' });
|
||||||
expect(resp.type).toEqual('success');
|
expect(resp.type).toEqual('success');
|
||||||
resp = new ResponseModel({...props, code: '120' });
|
resp = new ResponseModel({ ...props, code: '120' });
|
||||||
expect(resp.type).toEqual('info');
|
expect(resp.type).toEqual('info');
|
||||||
resp = new ResponseModel({...props, code: '301' });
|
resp = new ResponseModel({ ...props, code: '301' });
|
||||||
expect(resp.type).toEqual('redirect');
|
expect(resp.type).toEqual('redirect');
|
||||||
resp = new ResponseModel({...props, code: '400' });
|
resp = new ResponseModel({ ...props, code: '400' });
|
||||||
expect(resp.type).toEqual('error');
|
expect(resp.type).toEqual('error');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default should be successful by default', () => {
|
test('default should be successful by default', () => {
|
||||||
const resp = new ResponseModel({...props, code: 'default' });
|
const resp = new ResponseModel({ ...props, code: 'default' });
|
||||||
expect(resp.type).toEqual('success');
|
expect(resp.type).toEqual('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default should be error if defaultAsError is true', () => {
|
test('default should be error if defaultAsError is true', () => {
|
||||||
const resp = new ResponseModel({...props, code: 'default', defaultAsError: true });
|
const resp = new ResponseModel({ ...props, code: 'default', defaultAsError: true });
|
||||||
expect(resp.type).toEqual('error');
|
expect(resp.type).toEqual('error');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ensure extensions are shown if showExtensions is true', () => {
|
||||||
|
const options = new RedocNormalizedOptions({ showExtensions: true });
|
||||||
|
const resp = new ResponseModel({
|
||||||
|
parser,
|
||||||
|
code: 'default',
|
||||||
|
defaultAsError: true,
|
||||||
|
infoOrRef: { 'x-example': { a: 1 } },
|
||||||
|
options,
|
||||||
|
isEvent: true,
|
||||||
|
});
|
||||||
|
expect(Object.keys(resp.extensions).length).toEqual(1);
|
||||||
|
expect(resp.extensions['x-example']).toEqual({ a: 1 });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,7 @@ export class GroupModel implements IMenuItem {
|
||||||
id: string;
|
id: string;
|
||||||
absoluteIdx?: number;
|
absoluteIdx?: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
sidebarLabel: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
type: MenuItemGroupType;
|
type: MenuItemGroupType;
|
||||||
|
|
||||||
|
@ -43,6 +44,8 @@ export class GroupModel implements IMenuItem {
|
||||||
this.name = tagOrGroup['x-displayName'] || tagOrGroup.name;
|
this.name = tagOrGroup['x-displayName'] || tagOrGroup.name;
|
||||||
this.level = (tagOrGroup as MarkdownHeading).level || 1;
|
this.level = (tagOrGroup as MarkdownHeading).level || 1;
|
||||||
|
|
||||||
|
this.sidebarLabel = this.name;
|
||||||
|
|
||||||
// remove sections from markdown, same as in ApiInfo
|
// remove sections from markdown, same as in ApiInfo
|
||||||
this.description = tagOrGroup.description || '';
|
this.description = tagOrGroup.description || '';
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { FieldModel } from './Field';
|
||||||
import { MediaContentModel } from './MediaContent';
|
import { MediaContentModel } from './MediaContent';
|
||||||
import { RequestBodyModel } from './RequestBody';
|
import { RequestBodyModel } from './RequestBody';
|
||||||
import { ResponseModel } from './Response';
|
import { ResponseModel } from './Response';
|
||||||
|
import { SideNavStyleEnum } from '../RedocNormalizedOptions';
|
||||||
|
|
||||||
export interface XPayloadSample {
|
export interface XPayloadSample {
|
||||||
lang: 'payload';
|
lang: 'payload';
|
||||||
|
@ -49,6 +50,7 @@ export class OperationModel implements IMenuItem {
|
||||||
id: string;
|
id: string;
|
||||||
absoluteIdx?: number;
|
absoluteIdx?: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
sidebarLabel: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
type = 'operation' as const;
|
type = 'operation' as const;
|
||||||
|
|
||||||
|
@ -104,11 +106,13 @@ export class OperationModel implements IMenuItem {
|
||||||
|
|
||||||
this.name = getOperationSummary(operationSpec);
|
this.name = getOperationSummary(operationSpec);
|
||||||
|
|
||||||
|
this.sidebarLabel = options.sideNavStyle === SideNavStyleEnum.PathOnly ? this.path : this.name;
|
||||||
|
|
||||||
if (this.isCallback) {
|
if (this.isCallback) {
|
||||||
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
|
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
|
||||||
// Can be defined individually per-callback in the specification. Defaults to none.
|
// Can be defined individually per-callback in the specification. Defaults to none.
|
||||||
this.security = (operationSpec.security || []).map(
|
this.security = (operationSpec.security || []).map(
|
||||||
(security) => new SecurityRequirementModel(security, parser),
|
security => new SecurityRequirementModel(security, parser),
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: update getting pathInfo for overriding servers on path level
|
// TODO: update getting pathInfo for overriding servers on path level
|
||||||
|
@ -122,7 +126,7 @@ export class OperationModel implements IMenuItem {
|
||||||
: this.pointer;
|
: this.pointer;
|
||||||
|
|
||||||
this.security = (operationSpec.security || parser.spec.security || []).map(
|
this.security = (operationSpec.security || parser.spec.security || []).map(
|
||||||
(security) => new SecurityRequirementModel(security, parser),
|
security => new SecurityRequirementModel(security, parser),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.servers = normalizeServers(
|
this.servers = normalizeServers(
|
||||||
|
@ -173,7 +177,8 @@ export class OperationModel implements IMenuItem {
|
||||||
@memoize
|
@memoize
|
||||||
get requestBody() {
|
get requestBody() {
|
||||||
return (
|
return (
|
||||||
this.operationSpec.requestBody && new RequestBodyModel({
|
this.operationSpec.requestBody &&
|
||||||
|
new RequestBodyModel({
|
||||||
parser: this.parser,
|
parser: this.parser,
|
||||||
infoOrRef: this.operationSpec.requestBody,
|
infoOrRef: this.operationSpec.requestBody,
|
||||||
options: this.options,
|
options: this.options,
|
||||||
|
@ -218,7 +223,7 @@ export class OperationModel implements IMenuItem {
|
||||||
this.operationSpec.pathParameters,
|
this.operationSpec.pathParameters,
|
||||||
this.operationSpec.parameters,
|
this.operationSpec.parameters,
|
||||||
// TODO: fix pointer
|
// TODO: fix pointer
|
||||||
).map((paramOrRef) => new FieldModel(this.parser, paramOrRef, this.pointer, this.options));
|
).map(paramOrRef => new FieldModel(this.parser, paramOrRef, this.pointer, this.options));
|
||||||
|
|
||||||
if (this.options.sortPropsAlphabetically) {
|
if (this.options.sortPropsAlphabetically) {
|
||||||
return sortByField(_parameters, 'name');
|
return sortByField(_parameters, 'name');
|
||||||
|
@ -234,7 +239,7 @@ export class OperationModel implements IMenuItem {
|
||||||
get responses() {
|
get responses() {
|
||||||
let hasSuccessResponses = false;
|
let hasSuccessResponses = false;
|
||||||
return Object.keys(this.operationSpec.responses || [])
|
return Object.keys(this.operationSpec.responses || [])
|
||||||
.filter((code) => {
|
.filter(code => {
|
||||||
if (code === 'default') {
|
if (code === 'default') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -245,7 +250,7 @@ export class OperationModel implements IMenuItem {
|
||||||
|
|
||||||
return isStatusCode(code);
|
return isStatusCode(code);
|
||||||
}) // filter out other props (e.g. x-props)
|
}) // filter out other props (e.g. x-props)
|
||||||
.map((code) => {
|
.map(code => {
|
||||||
return new ResponseModel({
|
return new ResponseModel({
|
||||||
parser: this.parser,
|
parser: this.parser,
|
||||||
code,
|
code,
|
||||||
|
@ -259,7 +264,7 @@ export class OperationModel implements IMenuItem {
|
||||||
|
|
||||||
@memoize
|
@memoize
|
||||||
get callbacks() {
|
get callbacks() {
|
||||||
return Object.keys(this.operationSpec.callbacks || []).map((callbackEventName) => {
|
return Object.keys(this.operationSpec.callbacks || []).map(callbackEventName => {
|
||||||
return new CallbackModel(
|
return new CallbackModel(
|
||||||
this.parser,
|
this.parser,
|
||||||
callbackEventName,
|
callbackEventName,
|
||||||
|
|
|
@ -3,28 +3,30 @@ import { OpenAPIRequestBody, Referenced } from '../../types';
|
||||||
import { OpenAPIParser } from '../OpenAPIParser';
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
import { MediaContentModel } from './MediaContent';
|
import { MediaContentModel } from './MediaContent';
|
||||||
|
import { getContentWithLegacyExamples } from '../../utils';
|
||||||
|
|
||||||
type RequestBodyProps = {
|
type RequestBodyProps = {
|
||||||
parser: OpenAPIParser;
|
parser: OpenAPIParser;
|
||||||
infoOrRef: Referenced<OpenAPIRequestBody>;
|
infoOrRef: Referenced<OpenAPIRequestBody>;
|
||||||
options: RedocNormalizedOptions;
|
options: RedocNormalizedOptions;
|
||||||
isEvent: boolean;
|
isEvent: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class RequestBodyModel {
|
export class RequestBodyModel {
|
||||||
description: string;
|
description: string;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
content?: MediaContentModel;
|
content?: MediaContentModel;
|
||||||
|
|
||||||
constructor(props: RequestBodyProps) {
|
constructor({ parser, infoOrRef, options, isEvent }: RequestBodyProps) {
|
||||||
const { parser, infoOrRef, options, isEvent } = props;
|
const isRequest = !isEvent;
|
||||||
const isRequest = isEvent ? false : true;
|
|
||||||
const info = parser.deref(infoOrRef);
|
const info = parser.deref(infoOrRef);
|
||||||
this.description = info.description || '';
|
this.description = info.description || '';
|
||||||
this.required = !!info.required;
|
this.required = !!info.required;
|
||||||
parser.exitRef(infoOrRef);
|
parser.exitRef(infoOrRef);
|
||||||
if (info.content !== undefined) {
|
|
||||||
this.content = new MediaContentModel(parser, info.content, isRequest, options);
|
const mediaContent = getContentWithLegacyExamples(info);
|
||||||
|
if (mediaContent !== undefined) {
|
||||||
|
this.content = new MediaContentModel(parser, mediaContent, isRequest, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,20 @@ import { action, observable, makeObservable } from 'mobx';
|
||||||
|
|
||||||
import { OpenAPIResponse, Referenced } from '../../types';
|
import { OpenAPIResponse, Referenced } from '../../types';
|
||||||
|
|
||||||
import { getStatusCodeType } from '../../utils';
|
import { getStatusCodeType, extractExtensions } from '../../utils';
|
||||||
import { OpenAPIParser } from '../OpenAPIParser';
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
import { FieldModel } from './Field';
|
import { FieldModel } from './Field';
|
||||||
import { MediaContentModel } from './MediaContent';
|
import { MediaContentModel } from './MediaContent';
|
||||||
|
|
||||||
type ResponseProps = {
|
type ResponseProps = {
|
||||||
parser: OpenAPIParser,
|
parser: OpenAPIParser;
|
||||||
code: string,
|
code: string;
|
||||||
defaultAsError: boolean,
|
defaultAsError: boolean;
|
||||||
infoOrRef: Referenced<OpenAPIResponse>,
|
infoOrRef: Referenced<OpenAPIResponse>;
|
||||||
options: RedocNormalizedOptions,
|
options: RedocNormalizedOptions;
|
||||||
isEvent: boolean,
|
isEvent: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class ResponseModel {
|
export class ResponseModel {
|
||||||
@observable
|
@observable
|
||||||
|
@ -27,10 +27,16 @@ export class ResponseModel {
|
||||||
description: string;
|
description: string;
|
||||||
type: string;
|
type: string;
|
||||||
headers: FieldModel[] = [];
|
headers: FieldModel[] = [];
|
||||||
|
extensions: Record<string, any>;
|
||||||
|
|
||||||
constructor(props: ResponseProps) {
|
constructor({
|
||||||
const { parser, code, defaultAsError, infoOrRef, options, isEvent } = props;
|
parser,
|
||||||
const isRequest = isEvent ? true : false;
|
code,
|
||||||
|
defaultAsError,
|
||||||
|
infoOrRef,
|
||||||
|
options,
|
||||||
|
isEvent: isRequest,
|
||||||
|
}: ResponseProps) {
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
|
|
||||||
this.expanded = options.expandResponses === 'all' || options.expandResponses[code];
|
this.expanded = options.expandResponses === 'all' || options.expandResponses[code];
|
||||||
|
@ -59,6 +65,10 @@ export class ResponseModel {
|
||||||
return new FieldModel(parser, { ...header, name }, '', options);
|
return new FieldModel(parser, { ...header, name }, '', options);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.showExtensions) {
|
||||||
|
this.extensions = extractExtensions(info, options.showExtensions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|
|
@ -134,7 +134,10 @@ export class SchemaModel {
|
||||||
this.maxItems = schema.maxItems;
|
this.maxItems = schema.maxItems;
|
||||||
|
|
||||||
if (!!schema.nullable || schema['x-nullable']) {
|
if (!!schema.nullable || schema['x-nullable']) {
|
||||||
if (Array.isArray(this.type) && !this.type.some((value) => value === null || value === 'null')) {
|
if (
|
||||||
|
Array.isArray(this.type) &&
|
||||||
|
!this.type.some(value => value === null || value === 'null')
|
||||||
|
) {
|
||||||
this.type = [...this.type, 'null'];
|
this.type = [...this.type, 'null'];
|
||||||
} else if (!Array.isArray(this.type) && (this.type !== null || this.type !== 'null')) {
|
} else if (!Array.isArray(this.type) && (this.type !== null || this.type !== 'null')) {
|
||||||
this.type = [this.type, 'null'];
|
this.type = [this.type, 'null'];
|
||||||
|
@ -142,7 +145,7 @@ export class SchemaModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.displayType = Array.isArray(this.type)
|
this.displayType = Array.isArray(this.type)
|
||||||
? this.type.map(item => item === null ? 'null' : item).join(' or ')
|
? this.type.map(item => (item === null ? 'null' : item)).join(' or ')
|
||||||
: this.type;
|
: this.type;
|
||||||
|
|
||||||
if (this.isCircular) {
|
if (this.isCircular) {
|
||||||
|
@ -155,7 +158,7 @@ export class SchemaModel {
|
||||||
} else if (
|
} else if (
|
||||||
isChild &&
|
isChild &&
|
||||||
Array.isArray(schema.oneOf) &&
|
Array.isArray(schema.oneOf) &&
|
||||||
schema.oneOf.find((s) => s.$ref === this.pointer)
|
schema.oneOf.find(s => s.$ref === this.pointer)
|
||||||
) {
|
) {
|
||||||
// we hit allOf of the schema with the parent discriminator
|
// we hit allOf of the schema with the parent discriminator
|
||||||
delete schema.oneOf;
|
delete schema.oneOf;
|
||||||
|
@ -195,8 +198,7 @@ export class SchemaModel {
|
||||||
}
|
}
|
||||||
if (Array.isArray(this.type)) {
|
if (Array.isArray(this.type)) {
|
||||||
const filteredType = this.type.filter(item => item !== 'array');
|
const filteredType = this.type.filter(item => item !== 'array');
|
||||||
if (filteredType.length)
|
if (filteredType.length) this.displayType += ` or ${filteredType.join(' or ')}`;
|
||||||
this.displayType += ` or ${filteredType.join(' or ')}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +217,7 @@ export class SchemaModel {
|
||||||
const title =
|
const title =
|
||||||
isNamedDefinition(variant.$ref) && !merged.title
|
isNamedDefinition(variant.$ref) && !merged.title
|
||||||
? JsonPointer.baseName(variant.$ref)
|
? JsonPointer.baseName(variant.$ref)
|
||||||
: `${(merged.title || '')}${(merged.const && JSON.stringify(merged.const)) || ''}`;
|
: `${merged.title || ''}${(merged.const && JSON.stringify(merged.const)) || ''}`;
|
||||||
|
|
||||||
const schema = new SchemaModel(
|
const schema = new SchemaModel(
|
||||||
parser,
|
parser,
|
||||||
|
@ -243,7 +245,7 @@ export class SchemaModel {
|
||||||
this.displayType = types.join(' or ');
|
this.displayType = types.join(' or ');
|
||||||
} else {
|
} else {
|
||||||
this.displayType = this.oneOf
|
this.displayType = this.oneOf
|
||||||
.map((schema) => {
|
.map(schema => {
|
||||||
let name =
|
let name =
|
||||||
schema.typePrefix +
|
schema.typePrefix +
|
||||||
(schema.title ? `${schema.title} (${schema.displayType})` : schema.displayType);
|
(schema.title ? `${schema.title} (${schema.displayType})` : schema.displayType);
|
||||||
|
@ -364,7 +366,7 @@ function buildFields(
|
||||||
const props = schema.properties || {};
|
const props = schema.properties || {};
|
||||||
const additionalProps = schema.additionalProperties;
|
const additionalProps = schema.additionalProperties;
|
||||||
const defaults = schema.default;
|
const defaults = schema.default;
|
||||||
let fields = Object.keys(props || []).map((fieldName) => {
|
let fields = Object.keys(props || []).map(fieldName => {
|
||||||
let field = props[fieldName];
|
let field = props[fieldName];
|
||||||
|
|
||||||
if (!field) {
|
if (!field) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { hydrate as hydrateComponent, render } from 'react-dom';
|
import { hydrate as hydrateComponent, render } from 'react-dom';
|
||||||
import { configure } from "mobx"
|
import { configure } from 'mobx';
|
||||||
|
|
||||||
import { Redoc, RedocStandalone } from './components/';
|
import { Redoc, RedocStandalone } from './components/';
|
||||||
import { AppStore, StoreState } from './services/AppStore';
|
import { AppStore, StoreState } from './services/AppStore';
|
||||||
|
@ -8,8 +8,8 @@ import { debugTime, debugTimeEnd } from './utils/debug';
|
||||||
import { querySelector } from './utils/dom';
|
import { querySelector } from './utils/dom';
|
||||||
|
|
||||||
configure({
|
configure({
|
||||||
useProxies: 'ifavailable'
|
useProxies: 'ifavailable',
|
||||||
})
|
});
|
||||||
|
|
||||||
export { Redoc, AppStore } from '.';
|
export { Redoc, AppStore } from '.';
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const media = {
|
||||||
lessThan(breakpoint, print?: boolean, extra?: string) {
|
lessThan(breakpoint, print?: boolean, extra?: string) {
|
||||||
return (...args) => css`
|
return (...args) => css`
|
||||||
@media ${print ? 'print, ' : ''} screen and (max-width: ${props =>
|
@media ${print ? 'print, ' : ''} screen and (max-width: ${props =>
|
||||||
props.theme.breakpoints[breakpoint]})${extra || ''} {
|
props.theme.breakpoints[breakpoint]}) ${extra || ''} {
|
||||||
${(css as any)(...args)};
|
${(css as any)(...args)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -186,17 +186,20 @@ export interface OpenAPIRequestBody {
|
||||||
description?: string;
|
description?: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
content: { [mime: string]: OpenAPIMediaType };
|
content: { [mime: string]: OpenAPIMediaType };
|
||||||
|
|
||||||
|
'x-examples'?: { [mime: string]: { [name: string]: Referenced<OpenAPIExample> } };
|
||||||
|
'x-example'?: { [mime: string]: any };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenAPIResponses {
|
export interface OpenAPIResponses {
|
||||||
[code: string]: OpenAPIResponse;
|
[code: string]: Referenced<OpenAPIResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenAPIResponse {
|
export interface OpenAPIResponse
|
||||||
description?: string;
|
extends Pick<OpenAPIRequestBody, 'description' | 'x-examples' | 'x-example'> {
|
||||||
headers?: { [name: string]: Referenced<OpenAPIHeader> };
|
headers?: { [name: string]: Referenced<OpenAPIHeader> };
|
||||||
content?: { [mime: string]: OpenAPIMediaType };
|
|
||||||
links?: { [name: string]: Referenced<OpenAPILink> };
|
links?: { [name: string]: Referenced<OpenAPILink> };
|
||||||
|
content?: { [mime: string]: OpenAPIMediaType };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenAPILink {
|
export interface OpenAPILink {
|
||||||
|
|
|
@ -11,7 +11,9 @@ describe('#loadAndBundleSpec', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load And Bundle Spec demo/openapi-3-1.yaml', async () => {
|
it('should load And Bundle Spec demo/openapi-3-1.yaml', async () => {
|
||||||
const spec = yaml.load(readFileSync(resolve(__dirname, '../../../demo/openapi-3-1.yaml'), 'utf-8'));
|
const spec = yaml.load(
|
||||||
|
readFileSync(resolve(__dirname, '../../../demo/openapi-3-1.yaml'), 'utf-8'),
|
||||||
|
);
|
||||||
const bundledSpec = await loadAndBundleSpec(spec);
|
const bundledSpec = await loadAndBundleSpec(spec);
|
||||||
expect(bundledSpec).toMatchSnapshot();
|
expect(bundledSpec).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,10 +10,17 @@ import {
|
||||||
pluralizeType,
|
pluralizeType,
|
||||||
serializeParameterValue,
|
serializeParameterValue,
|
||||||
sortByRequired,
|
sortByRequired,
|
||||||
|
humanizeNumberRange,
|
||||||
|
getContentWithLegacyExamples,
|
||||||
} from '../';
|
} from '../';
|
||||||
|
|
||||||
import { FieldModel, OpenAPIParser, RedocNormalizedOptions } from '../../services';
|
import { FieldModel, OpenAPIParser, RedocNormalizedOptions } from '../../services';
|
||||||
import { OpenAPIParameter, OpenAPIParameterLocation, OpenAPIParameterStyle } from '../../types';
|
import {
|
||||||
|
OpenAPIMediaType,
|
||||||
|
OpenAPIParameter,
|
||||||
|
OpenAPIParameterLocation,
|
||||||
|
OpenAPIParameterStyle,
|
||||||
|
} from '../../types';
|
||||||
import { expandDefaultServerVariables } from '../openapi';
|
import { expandDefaultServerVariables } from '../openapi';
|
||||||
|
|
||||||
describe('Utils', () => {
|
describe('Utils', () => {
|
||||||
|
@ -103,7 +110,7 @@ describe('Utils', () => {
|
||||||
|
|
||||||
it('Should return pathName if no summary, operationId, description', () => {
|
it('Should return pathName if no summary, operationId, description', () => {
|
||||||
const operation = {
|
const operation = {
|
||||||
pathName: '/sandbox/test'
|
pathName: '/sandbox/test',
|
||||||
};
|
};
|
||||||
expect(getOperationSummary(operation as any)).toBe('/sandbox/test');
|
expect(getOperationSummary(operation as any)).toBe('/sandbox/test');
|
||||||
});
|
});
|
||||||
|
@ -174,7 +181,7 @@ describe('Utils', () => {
|
||||||
expect(isPrimitiveType(schema)).toEqual(false);
|
expect(isPrimitiveType(schema)).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true for array contains object and schema hasn\'t properties', () => {
|
it("should return true for array contains object and schema hasn't properties", () => {
|
||||||
const schema = {
|
const schema = {
|
||||||
type: ['object', 'string'],
|
type: ['object', 'string'],
|
||||||
};
|
};
|
||||||
|
@ -231,10 +238,10 @@ describe('Utils', () => {
|
||||||
const schema = {
|
const schema = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
type: 'string'
|
type: 'string',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
expect(isPrimitiveType(schema)).toEqual(true);
|
expect(isPrimitiveType(schema)).toEqual(true);
|
||||||
|
@ -410,12 +417,82 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('openapi humanizeNumberRange', () => {
|
||||||
|
it('should return `>=` when only minimum value present or exclusiveMinimum = false', () => {
|
||||||
|
const expected = '>= 0';
|
||||||
|
expect(humanizeNumberRange({ minimum: 0 })).toEqual(expected);
|
||||||
|
expect(humanizeNumberRange({ minimum: 0, exclusiveMinimum: false })).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return `>` when minimum value present and exclusiveMinimum set to true', () => {
|
||||||
|
expect(humanizeNumberRange({ minimum: 0, exclusiveMinimum: true })).toEqual('> 0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return `<=` when only maximum value present or exclusiveMinimum = false', () => {
|
||||||
|
const expected = '<= 10';
|
||||||
|
expect(humanizeNumberRange({ maximum: 10 })).toEqual(expected);
|
||||||
|
expect(humanizeNumberRange({ maximum: 10, exclusiveMaximum: false })).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return `<` when maximum value present and exclusiveMaximum set to true', () => {
|
||||||
|
expect(humanizeNumberRange({ maximum: 10, exclusiveMaximum: true })).toEqual('< 10');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct range for minimum and maximum values and with different exclusive set', () => {
|
||||||
|
expect(humanizeNumberRange({ minimum: 0, maximum: 10 })).toEqual('[ 0 .. 10 ]');
|
||||||
|
expect(
|
||||||
|
humanizeNumberRange({
|
||||||
|
minimum: 0,
|
||||||
|
exclusiveMinimum: true,
|
||||||
|
maximum: 10,
|
||||||
|
exclusiveMaximum: true,
|
||||||
|
}),
|
||||||
|
).toEqual('( 0 .. 10 )');
|
||||||
|
expect(
|
||||||
|
humanizeNumberRange({
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 10,
|
||||||
|
exclusiveMaximum: true,
|
||||||
|
}),
|
||||||
|
).toEqual('[ 0 .. 10 )');
|
||||||
|
expect(
|
||||||
|
humanizeNumberRange({
|
||||||
|
minimum: 0,
|
||||||
|
exclusiveMinimum: true,
|
||||||
|
maximum: 10,
|
||||||
|
}),
|
||||||
|
).toEqual('( 0 .. 10 ]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct range exclusive values only', () => {
|
||||||
|
expect(humanizeNumberRange({ exclusiveMinimum: 0 })).toEqual('> 0');
|
||||||
|
expect(humanizeNumberRange({ exclusiveMaximum: 10 })).toEqual('< 10');
|
||||||
|
expect(humanizeNumberRange({ exclusiveMinimum: 0, exclusiveMaximum: 10 })).toEqual(
|
||||||
|
'( 0 .. 10 )',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct min value', () => {
|
||||||
|
expect(humanizeNumberRange({ minimum: 5, exclusiveMinimum: 10 })).toEqual('> 5');
|
||||||
|
expect(humanizeNumberRange({ minimum: -5, exclusiveMinimum: -10 })).toEqual('> -10');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct max value', () => {
|
||||||
|
expect(humanizeNumberRange({ maximum: 10, exclusiveMaximum: 15 })).toEqual('< 15');
|
||||||
|
expect(humanizeNumberRange({ maximum: -10, exclusiveMaximum: -15 })).toEqual('< -10');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined', () => {
|
||||||
|
expect(humanizeNumberRange({})).toEqual(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('openapi humanizeConstraints', () => {
|
describe('openapi humanizeConstraints', () => {
|
||||||
const itemConstraintSchema = (
|
const itemConstraintSchema = (
|
||||||
min?: number,
|
min?: number,
|
||||||
max?: number,
|
max?: number,
|
||||||
multipleOf?: number,
|
multipleOf?: number,
|
||||||
uniqueItems?: boolean
|
uniqueItems?: boolean,
|
||||||
) => ({ type: 'array', minItems: min, maxItems: max, multipleOf, uniqueItems });
|
) => ({ type: 'array', minItems: min, maxItems: max, multipleOf, uniqueItems });
|
||||||
|
|
||||||
it('should not have a humanized constraint without schema constraints', () => {
|
it('should not have a humanized constraint without schema constraints', () => {
|
||||||
|
@ -455,9 +532,9 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have a humanized constraint when uniqueItems is set', () => {
|
it('should have a humanized constraint when uniqueItems is set', () => {
|
||||||
expect(humanizeConstraints(itemConstraintSchema(undefined, undefined, undefined, true))).toContain(
|
expect(
|
||||||
'unique',
|
humanizeConstraints(itemConstraintSchema(undefined, undefined, undefined, true)),
|
||||||
);
|
).toContain('unique');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1090,4 +1167,106 @@ describe('Utils', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('OpenAPI getContentWithLegacyExamples', () => {
|
||||||
|
it('should return undefined if no x-examples/x-example and no content', () => {
|
||||||
|
expect(getContentWithLegacyExamples({})).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return unmodified object if no x-examples or x-example', () => {
|
||||||
|
const info = {
|
||||||
|
content: {
|
||||||
|
'application/json': {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = getContentWithLegacyExamples(info);
|
||||||
|
expect(content).toStrictEqual(info.content);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a new content object if no content and x-examples', () => {
|
||||||
|
const info = {
|
||||||
|
'x-examples': {
|
||||||
|
'application/json': {
|
||||||
|
name: {
|
||||||
|
value: 'test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = getContentWithLegacyExamples(info);
|
||||||
|
expect(content).toEqual({
|
||||||
|
'application/json': {
|
||||||
|
examples: {
|
||||||
|
name: {
|
||||||
|
value: 'test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a new content object if no content and x-example', () => {
|
||||||
|
const info = {
|
||||||
|
'x-example': {
|
||||||
|
'application/json': 'test',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = getContentWithLegacyExamples(info);
|
||||||
|
expect(content).toEqual({
|
||||||
|
'application/json': { example: 'test' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return copy of content with injected x-example', () => {
|
||||||
|
const info = {
|
||||||
|
'x-example': {
|
||||||
|
'application/json': 'test',
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: { type: 'string' },
|
||||||
|
},
|
||||||
|
'text/plain': { schema: { type: 'string' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = getContentWithLegacyExamples(info) as { [mime: string]: OpenAPIMediaType };
|
||||||
|
expect(content).toEqual({
|
||||||
|
'application/json': { schema: { type: 'string' }, example: 'test' },
|
||||||
|
'text/plain': { schema: { type: 'string' } },
|
||||||
|
});
|
||||||
|
expect(content).not.toStrictEqual(info.content);
|
||||||
|
expect(content['application/json']).not.toStrictEqual(info.content['application/json']);
|
||||||
|
expect(content['text/plain']).toStrictEqual(info.content['text/plain']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prefer x-examples over x-example', () => {
|
||||||
|
const info = {
|
||||||
|
'x-example': {
|
||||||
|
'application/json': 'test',
|
||||||
|
},
|
||||||
|
'x-examples': {
|
||||||
|
'application/json': { name: { value: 'test' } },
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: { type: 'string' },
|
||||||
|
},
|
||||||
|
'text/plain': { schema: { type: 'string' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = getContentWithLegacyExamples(info) as { [mime: string]: OpenAPIMediaType };
|
||||||
|
expect(content).toEqual({
|
||||||
|
'application/json': { schema: { type: 'string' }, examples: { name: { value: 'test' } } },
|
||||||
|
'text/plain': { schema: { type: 'string' } },
|
||||||
|
});
|
||||||
|
expect(content).not.toStrictEqual(info.content);
|
||||||
|
expect(content['application/json']).not.toStrictEqual(info.content['application/json']);
|
||||||
|
expect(content['text/plain']).toStrictEqual(info.content['text/plain']);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,7 @@ function throttle(func, wait) {
|
||||||
context = args = null;
|
context = args = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return function() {
|
return function () {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
const remaining = wait - (now - previous);
|
const remaining = wait - (now - previous);
|
||||||
context = this;
|
context = this;
|
||||||
|
|
|
@ -24,52 +24,54 @@ export function html2Str(html: string): string {
|
||||||
.join(' ');
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// scrollIntoViewIfNeeded polyfill
|
// Alternate scrollIntoViewIfNeeded implementation.
|
||||||
|
// Used in all cases, since it seems Chrome's implementation is buggy
|
||||||
|
// when "Experimental Web Platform Features" is enabled (at least of version 96).
|
||||||
|
// See #1714, #1742
|
||||||
|
|
||||||
if (typeof Element !== 'undefined' && !(Element as any).prototype.scrollIntoViewIfNeeded) {
|
export function scrollIntoViewIfNeeded(el: HTMLElement, centerIfNeeded = true) {
|
||||||
(Element as any).prototype.scrollIntoViewIfNeeded = function(centerIfNeeded) {
|
const parent = el.parentNode as HTMLElement | null;
|
||||||
centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;
|
if (!parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const parentComputedStyle = window.getComputedStyle(parent, undefined);
|
||||||
|
const parentBorderTopWidth = parseInt(
|
||||||
|
parentComputedStyle.getPropertyValue('border-top-width'),
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
const parentBorderLeftWidth = parseInt(
|
||||||
|
parentComputedStyle.getPropertyValue('border-left-width'),
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
const overTop = el.offsetTop - parent.offsetTop < parent.scrollTop;
|
||||||
|
const overBottom =
|
||||||
|
el.offsetTop - parent.offsetTop + el.clientHeight - parentBorderTopWidth >
|
||||||
|
parent.scrollTop + parent.clientHeight;
|
||||||
|
const overLeft = el.offsetLeft - parent.offsetLeft < parent.scrollLeft;
|
||||||
|
const overRight =
|
||||||
|
el.offsetLeft - parent.offsetLeft + el.clientWidth - parentBorderLeftWidth >
|
||||||
|
parent.scrollLeft + parent.clientWidth;
|
||||||
|
const alignWithTop = overTop && !overBottom;
|
||||||
|
|
||||||
const parent = this.parentNode;
|
if ((overTop || overBottom) && centerIfNeeded) {
|
||||||
const parentComputedStyle = window.getComputedStyle(parent, undefined);
|
parent.scrollTop =
|
||||||
const parentBorderTopWidth = parseInt(
|
el.offsetTop -
|
||||||
parentComputedStyle.getPropertyValue('border-top-width'),
|
parent.offsetTop -
|
||||||
10,
|
parent.clientHeight / 2 -
|
||||||
);
|
parentBorderTopWidth +
|
||||||
const parentBorderLeftWidth = parseInt(
|
el.clientHeight / 2;
|
||||||
parentComputedStyle.getPropertyValue('border-left-width'),
|
}
|
||||||
10,
|
|
||||||
);
|
|
||||||
const overTop = this.offsetTop - parent.offsetTop < parent.scrollTop;
|
|
||||||
const overBottom =
|
|
||||||
this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth >
|
|
||||||
parent.scrollTop + parent.clientHeight;
|
|
||||||
const overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft;
|
|
||||||
const overRight =
|
|
||||||
this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth >
|
|
||||||
parent.scrollLeft + parent.clientWidth;
|
|
||||||
const alignWithTop = overTop && !overBottom;
|
|
||||||
|
|
||||||
if ((overTop || overBottom) && centerIfNeeded) {
|
if ((overLeft || overRight) && centerIfNeeded) {
|
||||||
parent.scrollTop =
|
parent.scrollLeft =
|
||||||
this.offsetTop -
|
el.offsetLeft -
|
||||||
parent.offsetTop -
|
parent.offsetLeft -
|
||||||
parent.clientHeight / 2 -
|
parent.clientWidth / 2 -
|
||||||
parentBorderTopWidth +
|
parentBorderLeftWidth +
|
||||||
this.clientHeight / 2;
|
el.clientWidth / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((overLeft || overRight) && centerIfNeeded) {
|
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
|
||||||
parent.scrollLeft =
|
el.scrollIntoView(alignWithTop);
|
||||||
this.offsetLeft -
|
}
|
||||||
parent.offsetLeft -
|
|
||||||
parent.clientWidth / 2 -
|
|
||||||
parentBorderLeftWidth +
|
|
||||||
this.clientWidth / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
|
|
||||||
this.scrollIntoView(alignWithTop);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ export function flattenByProp<T extends object, P extends keyof T>(
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
res.push(item);
|
res.push(item);
|
||||||
if (item[prop]) {
|
if (item[prop]) {
|
||||||
iterate((item[prop] as any) as T[]);
|
iterate(item[prop] as any as T[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,3 +8,4 @@ export * from './dom';
|
||||||
export * from './decorators';
|
export * from './decorators';
|
||||||
export * from './debug';
|
export * from './debug';
|
||||||
export * from './memoize';
|
export * from './memoize';
|
||||||
|
export * from './sort';
|
||||||
|
|
|
@ -14,8 +14,8 @@ export async function loadAndBundleSpec(specUrlOrObject: object | string): Promi
|
||||||
const config = new Config({});
|
const config = new Config({});
|
||||||
const bundleOpts = {
|
const bundleOpts = {
|
||||||
config,
|
config,
|
||||||
base: IS_BROWSER ? window.location.href : process.cwd()
|
base: IS_BROWSER ? window.location.href : process.cwd(),
|
||||||
}
|
};
|
||||||
|
|
||||||
if (IS_BROWSER) {
|
if (IS_BROWSER) {
|
||||||
config.resolve.http.customFetch = global.fetch;
|
config.resolve.http.customFetch = global.fetch;
|
||||||
|
@ -24,13 +24,15 @@ export async function loadAndBundleSpec(specUrlOrObject: object | string): Promi
|
||||||
if (typeof specUrlOrObject === 'object' && specUrlOrObject !== null) {
|
if (typeof specUrlOrObject === 'object' && specUrlOrObject !== null) {
|
||||||
bundleOpts['doc'] = {
|
bundleOpts['doc'] = {
|
||||||
source: { absoluteRef: '' } as Source,
|
source: { absoluteRef: '' } as Source,
|
||||||
parsed: specUrlOrObject
|
parsed: specUrlOrObject,
|
||||||
} as Document
|
} as Document;
|
||||||
} else {
|
} else {
|
||||||
bundleOpts['ref'] = specUrlOrObject;
|
bundleOpts['ref'] = specUrlOrObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { bundle: { parsed } } = await bundle(bundleOpts);
|
const {
|
||||||
|
bundle: { parsed },
|
||||||
|
} = await bundle(bundleOpts);
|
||||||
return parsed.swagger !== undefined ? convertSwagger2OpenAPI(parsed) : parsed;
|
return parsed.swagger !== undefined ? convertSwagger2OpenAPI(parsed) : parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ const SENTINEL = {};
|
||||||
|
|
||||||
export function memoize<T>(target: any, name: string, descriptor: TypedPropertyDescriptor<T>) {
|
export function memoize<T>(target: any, name: string, descriptor: TypedPropertyDescriptor<T>) {
|
||||||
if (typeof descriptor.value === 'function') {
|
if (typeof descriptor.value === 'function') {
|
||||||
return (_memoizeMethod(target, name, descriptor) as any) as TypedPropertyDescriptor<T>;
|
return _memoizeMethod(target, name, descriptor) as any as TypedPropertyDescriptor<T>;
|
||||||
} else if (typeof descriptor.get === 'function') {
|
} else if (typeof descriptor.get === 'function') {
|
||||||
return _memoizeGetter(target, name, descriptor) as TypedPropertyDescriptor<T>;
|
return _memoizeGetter(target, name, descriptor) as TypedPropertyDescriptor<T>;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {
|
||||||
OpenAPIMediaType,
|
OpenAPIMediaType,
|
||||||
OpenAPIParameter,
|
OpenAPIParameter,
|
||||||
OpenAPIParameterStyle,
|
OpenAPIParameterStyle,
|
||||||
|
OpenAPIRequestBody,
|
||||||
|
OpenAPIResponse,
|
||||||
OpenAPISchema,
|
OpenAPISchema,
|
||||||
OpenAPIServer,
|
OpenAPIServer,
|
||||||
Referenced,
|
Referenced,
|
||||||
|
@ -113,7 +115,10 @@ export function detectType(schema: OpenAPISchema): string {
|
||||||
return 'any';
|
return 'any';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPrimitiveType(schema: OpenAPISchema, type: string | string[] | undefined = schema.type) {
|
export function isPrimitiveType(
|
||||||
|
schema: OpenAPISchema,
|
||||||
|
type: string | string[] | undefined = schema.type,
|
||||||
|
) {
|
||||||
if (schema.oneOf !== undefined || schema.anyOf !== undefined) {
|
if (schema.oneOf !== undefined || schema.anyOf !== undefined) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -122,9 +127,10 @@ export function isPrimitiveType(schema: OpenAPISchema, type: string | string[] |
|
||||||
const isArray = Array.isArray(type);
|
const isArray = Array.isArray(type);
|
||||||
|
|
||||||
if (type === 'object' || (isArray && type?.includes('object'))) {
|
if (type === 'object' || (isArray && type?.includes('object'))) {
|
||||||
isPrimitive = schema.properties !== undefined
|
isPrimitive =
|
||||||
? Object.keys(schema.properties).length === 0
|
schema.properties !== undefined
|
||||||
: schema.additionalProperties === undefined;
|
? Object.keys(schema.properties).length === 0
|
||||||
|
: schema.additionalProperties === undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.items !== undefined && (type === 'array' || (isArray && type?.includes('array')))) {
|
if (schema.items !== undefined && (type === 'array' || (isArray && type?.includes('array')))) {
|
||||||
|
@ -385,7 +391,7 @@ export function isNamedDefinition(pointer?: string): boolean {
|
||||||
export function getDefinitionName(pointer?: string): string | undefined {
|
export function getDefinitionName(pointer?: string): string | undefined {
|
||||||
if (!pointer) return undefined;
|
if (!pointer) return undefined;
|
||||||
const match = pointer.match(/^#\/components\/(schemas|pathItems)\/([^\/]+)$/);
|
const match = pointer.match(/^#\/components\/(schemas|pathItems)\/([^\/]+)$/);
|
||||||
return match === null ? undefined : match[1]
|
return match === null ? undefined : match[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
function humanizeMultipleOfConstraint(multipleOf: number | undefined): string | undefined {
|
function humanizeMultipleOfConstraint(multipleOf: number | undefined): string | undefined {
|
||||||
|
@ -424,6 +430,29 @@ function humanizeRangeConstraint(
|
||||||
return stringRange;
|
return stringRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function humanizeNumberRange(schema: OpenAPISchema): string | undefined {
|
||||||
|
const minimum =
|
||||||
|
typeof schema.exclusiveMinimum === 'number'
|
||||||
|
? Math.min(schema.exclusiveMinimum, schema.minimum ?? Infinity)
|
||||||
|
: schema.minimum;
|
||||||
|
const maximum =
|
||||||
|
typeof schema.exclusiveMaximum === 'number'
|
||||||
|
? Math.max(schema.exclusiveMaximum, schema.maximum ?? -Infinity)
|
||||||
|
: schema.maximum;
|
||||||
|
const exclusiveMinimum = typeof schema.exclusiveMinimum === 'number' || schema.exclusiveMinimum;
|
||||||
|
const exclusiveMaximum = typeof schema.exclusiveMaximum === 'number' || schema.exclusiveMaximum;
|
||||||
|
|
||||||
|
if (minimum !== undefined && maximum !== undefined) {
|
||||||
|
return `${exclusiveMinimum ? '( ' : '[ '}${minimum} .. ${maximum}${
|
||||||
|
exclusiveMaximum ? ' )' : ' ]'
|
||||||
|
}`;
|
||||||
|
} else if (maximum !== undefined) {
|
||||||
|
return `${exclusiveMaximum ? '< ' : '<= '}${maximum}`;
|
||||||
|
} else if (minimum !== undefined) {
|
||||||
|
return `${exclusiveMinimum ? '> ' : '>= '}${minimum}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function humanizeConstraints(schema: OpenAPISchema): string[] {
|
export function humanizeConstraints(schema: OpenAPISchema): string[] {
|
||||||
const res: string[] = [];
|
const res: string[] = [];
|
||||||
|
|
||||||
|
@ -442,33 +471,7 @@ export function humanizeConstraints(schema: OpenAPISchema): string[] {
|
||||||
res.push(multipleOfConstraint);
|
res.push(multipleOfConstraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
let numberRange;
|
const numberRange = humanizeNumberRange(schema);
|
||||||
if (schema.minimum !== undefined && schema.maximum !== undefined) {
|
|
||||||
numberRange = schema.exclusiveMinimum ? '( ' : '[ ';
|
|
||||||
numberRange += schema.minimum;
|
|
||||||
numberRange += ' .. ';
|
|
||||||
numberRange += schema.maximum;
|
|
||||||
numberRange += schema.exclusiveMaximum ? ' )' : ' ]';
|
|
||||||
} else if (schema.maximum !== undefined) {
|
|
||||||
numberRange = schema.exclusiveMaximum ? '< ' : '<= ';
|
|
||||||
numberRange += schema.maximum;
|
|
||||||
} else if (schema.minimum !== undefined) {
|
|
||||||
numberRange = schema.exclusiveMinimum ? '> ' : '>= ';
|
|
||||||
numberRange += schema.minimum;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof schema.exclusiveMinimum === 'number' || typeof schema.exclusiveMaximum === 'number') {
|
|
||||||
let minimum = 0;
|
|
||||||
let maximum = 0;
|
|
||||||
if (schema.minimum) minimum = schema.minimum;
|
|
||||||
if (typeof schema.exclusiveMinimum === 'number') minimum = minimum <= schema.exclusiveMinimum ? minimum : schema.exclusiveMinimum;
|
|
||||||
|
|
||||||
if (schema.maximum) maximum = schema.maximum;
|
|
||||||
if (typeof schema.exclusiveMaximum === 'number') maximum = maximum > schema.exclusiveMaximum ? maximum : schema.exclusiveMaximum;
|
|
||||||
|
|
||||||
numberRange = `[${minimum} .. ${maximum}]`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numberRange !== undefined) {
|
if (numberRange !== undefined) {
|
||||||
res.push(numberRange);
|
res.push(numberRange);
|
||||||
}
|
}
|
||||||
|
@ -598,10 +601,10 @@ export function setSecuritySchemePrefix(prefix: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const shortenHTTPVerb = verb =>
|
export const shortenHTTPVerb = verb =>
|
||||||
({
|
({
|
||||||
delete: 'del',
|
delete: 'del',
|
||||||
options: 'opts',
|
options: 'opts',
|
||||||
}[verb] || verb);
|
}[verb] || verb);
|
||||||
|
|
||||||
export function isRedocExtension(key: string): boolean {
|
export function isRedocExtension(key: string): boolean {
|
||||||
const redocExtensions = {
|
const redocExtensions = {
|
||||||
|
@ -646,3 +649,33 @@ export function pluralizeType(displayType: string): string {
|
||||||
.map(type => type.replace(/^(string|object|number|integer|array|boolean)s?( ?.*)/, '$1s$2'))
|
.map(type => type.replace(/^(string|object|number|integer|array|boolean)s?( ?.*)/, '$1s$2'))
|
||||||
.join(' or ');
|
.join(' or ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getContentWithLegacyExamples(
|
||||||
|
info: OpenAPIRequestBody | OpenAPIResponse,
|
||||||
|
): { [mime: string]: OpenAPIMediaType } | undefined {
|
||||||
|
let mediaContent = info.content;
|
||||||
|
const xExamples = info['x-examples']; // converted from OAS2 body param
|
||||||
|
const xExample = info['x-example']; // converted from OAS2 body param
|
||||||
|
|
||||||
|
if (xExamples) {
|
||||||
|
mediaContent = { ...mediaContent };
|
||||||
|
for (const mime of Object.keys(xExamples)) {
|
||||||
|
const examples = xExamples[mime];
|
||||||
|
mediaContent[mime] = {
|
||||||
|
...mediaContent[mime],
|
||||||
|
examples,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (xExample) {
|
||||||
|
mediaContent = { ...mediaContent };
|
||||||
|
for (const mime of Object.keys(xExample)) {
|
||||||
|
const example = xExample[mime];
|
||||||
|
mediaContent[mime] = {
|
||||||
|
...mediaContent[mime],
|
||||||
|
example,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mediaContent;
|
||||||
|
}
|
||||||
|
|
21
src/utils/sort.ts
Normal file
21
src/utils/sort.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* Function that returns a comparator for sorting objects by some specific key alphabetically.
|
||||||
|
*
|
||||||
|
* @param {String} property key of the object to sort, if starts from `-` - reverse
|
||||||
|
*/
|
||||||
|
export function alphabeticallyByProp<T>(property: string): (a: T, b: T) => number {
|
||||||
|
let sortOrder = 1;
|
||||||
|
|
||||||
|
if (property[0] === '-') {
|
||||||
|
sortOrder = -1;
|
||||||
|
property = property.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (a: T, b: T) => {
|
||||||
|
if (sortOrder == -1) {
|
||||||
|
return b[property].localeCompare(a[property]);
|
||||||
|
} else {
|
||||||
|
return a[property].localeCompare(b[property]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -32,10 +32,14 @@ const BANNER = `ReDoc - OpenAPI/Swagger-generated API Reference Documentation
|
||||||
Version: ${VERSION}
|
Version: ${VERSION}
|
||||||
Repo: https://github.com/Redocly/redoc`;
|
Repo: https://github.com/Redocly/redoc`;
|
||||||
|
|
||||||
export default (env: { standalone?: boolean } = {}) => ({
|
export default (env: { standalone?: boolean; browser?: boolean } = {}) => ({
|
||||||
entry: env.standalone ? ['./src/polyfills.ts', './src/standalone.tsx'] : './src/index.ts',
|
entry: env.standalone ? ['./src/polyfills.ts', './src/standalone.tsx'] : './src/index.ts',
|
||||||
output: {
|
output: {
|
||||||
filename: env.standalone ? 'redoc.standalone.js' : 'redoc.lib.js',
|
filename: env.standalone
|
||||||
|
? 'redoc.standalone.js'
|
||||||
|
: env.browser
|
||||||
|
? 'redoc.browser.lib.js'
|
||||||
|
: 'redoc.lib.js',
|
||||||
path: path.join(__dirname, '/bundles'),
|
path: path.join(__dirname, '/bundles'),
|
||||||
library: 'Redoc',
|
library: 'Redoc',
|
||||||
libraryTarget: 'umd',
|
libraryTarget: 'umd',
|
||||||
|
@ -47,13 +51,13 @@ export default (env: { standalone?: boolean } = {}) => ({
|
||||||
fallback: {
|
fallback: {
|
||||||
path: require.resolve('path-browserify'),
|
path: require.resolve('path-browserify'),
|
||||||
http: false,
|
http: false,
|
||||||
fs: false,
|
fs: path.resolve(__dirname, 'src/empty.js'),
|
||||||
os: false,
|
os: path.resolve(__dirname, 'src/empty.js'),
|
||||||
}
|
tty: path.resolve(__dirname, 'src/empty.js'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
performance: false,
|
performance: false,
|
||||||
// target: 'node',
|
externalsPresets: env.standalone || env.browser ? {} : { node: true },
|
||||||
externalsPresets: env.standalone ? {} : { node: true },
|
|
||||||
externals: env.standalone
|
externals: env.standalone
|
||||||
? {
|
? {
|
||||||
esprima: 'null',
|
esprima: 'null',
|
||||||
|
@ -74,7 +78,7 @@ export default (env: { standalone?: boolean } = {}) => ({
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.(tsx?|[cm]?js)$/,
|
test: /\.(tsx?|[cm]?js)$/,
|
||||||
use: [getBabelLoader({useBuiltIns: !!env.standalone})],
|
use: [getBabelLoader({ useBuiltIns: !!env.standalone })],
|
||||||
exclude: {
|
exclude: {
|
||||||
and: [/node_modules/],
|
and: [/node_modules/],
|
||||||
not: {
|
not: {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user