Merge branch 'master-with-console' into 'master'

BBL-162

See merge request babel/babel-api-doc!2
This commit is contained in:
m.savalanpour 2020-01-22 11:47:25 +03:30
commit 883bb206c9
29 changed files with 516 additions and 178 deletions

21
.github/workflows/unit-tests.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Unit Tests
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: 10.x
- name: yarn install, build, and test
run: |
npm install -g yarn
yarn install
yarn bundle
yarn test

View File

@ -1,3 +1,36 @@
# [2.0.0-rc.16](https://github.com/Redocly/redoc/compare/v2.0.0-rc.15...v2.0.0-rc.16) (2019-09-30)
### Bug Fixes
* fix scrollYOffset when SSR ([d09c1c1](https://github.com/Redocly/redoc/commit/d09c1c1))
# [2.0.0-rc.15](https://github.com/Redocly/redoc/compare/v2.0.0-rc.14...v2.0.0-rc.15) (2019-09-30)
### Bug Fixes
* auth section appears twice ([5aa7784](https://github.com/Redocly/redoc/commit/5aa7784)), closes [#818](https://github.com/Redocly/redoc/issues/818)
* clicking on group title breaks first tag ([4649683](https://github.com/Redocly/redoc/commit/4649683)), closes [#1034](https://github.com/Redocly/redoc/issues/1034)
* do not crash on empty scopes ([e787d9e](https://github.com/Redocly/redoc/commit/e787d9e)), closes [#1044](https://github.com/Redocly/redoc/issues/1044)
* false-positive recursive detection with allOf at the same level ([faa74d6](https://github.com/Redocly/redoc/commit/faa74d6))
* fix scrollYOffset when SSR ([21258a5](https://github.com/Redocly/redoc/commit/21258a5))
* left menu item before group is not highligted ([67e2a8f](https://github.com/Redocly/redoc/commit/67e2a8f)), closes [#1033](https://github.com/Redocly/redoc/issues/1033)
* remove excessive whitespace between md sections on small screens ([e318fb3](https://github.com/Redocly/redoc/commit/e318fb3)), closes [#874](https://github.com/Redocly/redoc/issues/874)
* use url-template dependency ([#1008](https://github.com/Redocly/redoc/issues/1008)) ([32a464a](https://github.com/Redocly/redoc/commit/32a464a)), closes [#1007](https://github.com/Redocly/redoc/issues/1007)
### Features
* **cli:** added support for JSON string value for --options CLI argument ([#1047](https://github.com/Redocly/redoc/issues/1047)) ([2a28130](https://github.com/Redocly/redoc/commit/2a28130)), closes [#797](https://github.com/Redocly/redoc/issues/797)
* **cli:** add `disableGoogleFont` parameter to cli ([#1045](https://github.com/Redocly/redoc/issues/1045)) ([aceb343](https://github.com/Redocly/redoc/commit/aceb343))
* new option expandDefaultServerVariables ([#1014](https://github.com/Redocly/redoc/issues/1014)) ([0360dce](https://github.com/Redocly/redoc/commit/0360dce))
# [2.0.0-rc.14](https://github.com/Redocly/redoc/compare/v2.0.0-rc.13...v2.0.0-rc.14) (2019-08-07)

View File

@ -139,7 +139,7 @@ For npm:
Install peer dependencies required by ReDoc if you don't have them installed already:
npm i react react-dom mobx@^4.2.0 styled-components
npm i react react-dom mobx@^4.2.0 styled-components core-js
Import `RedocStandalone` component from 'redoc' module:
@ -246,6 +246,7 @@ You can use all of the following options with standalone version on <redoc> tag
* `onlyRequiredInSamples` - shows only required fields in request samples.
* `jsonSampleExpandLevel` - set the default expand level for JSON payload samples (responses and request body). Special value 'all' expands all levels. The default value is `2`.
* `menuToggle` - if true clicking second time on expanded menu item will collapse it, default `false`
* `expandDefaultServerVariables` - enable expanding default server variables, default `false`
* `theme` - ReDoc theme. Not documented yet. For details check source code: [theme.ts](https://github.com/Redocly/redoc/blob/master/src/theme.ts)
## Advanced usage of standalone version

View File

@ -25,6 +25,7 @@ interface Options {
cdn?: boolean;
output?: string;
title?: string;
disableGoogleFont?: boolean;
port?: number;
templateFileName?: string;
templateOptions?: any;
@ -68,9 +69,11 @@ YargsParser.command(
watch: argv.watch as boolean,
templateFileName: argv.template as string,
templateOptions: argv.templateOptions || {},
redocOptions: argv.options || {},
redocOptions: getObjectOrJSON(argv.options),
};
console.log(config);
try {
await serve(argv.port as number, argv.spec as string, config);
} catch (e) {
@ -99,6 +102,12 @@ YargsParser.command(
default: 'ReDoc documentation',
});
yargs.options('disableGoogleFont', {
describe: 'Disable Google Font',
type: 'boolean',
default: false,
});
yargs.option('cdn', {
describe: 'Do not include ReDoc source code into html page, use link to CDN instead',
type: 'boolean',
@ -108,15 +117,16 @@ YargsParser.command(
yargs.demandOption('spec');
return yargs;
},
async argv => {
const config: Options = {
async (argv: any) => {
const config = {
ssr: true,
output: argv.o as string,
cdn: argv.cdn as boolean,
title: argv.title as string,
disableGoogleFont: argv.disableGoogleFont as boolean,
templateFileName: argv.template as string,
templateOptions: argv.templateOptions || {},
redocOptions: argv.options || {},
redocOptions: getObjectOrJSON(argv.options),
};
try {
@ -180,21 +190,34 @@ async function serve(port: number, pathToSpec: string, options: Options = {}) {
if (options.watch && existsSync(pathToSpec)) {
const pathToSpecDirectory = resolve(dirname(pathToSpec));
const watchOptions = {
ignored: /(^|[\/\\])\../,
ignored: [/(^|[\/\\])\../, /___jb_[a-z]+___$/],
ignoreInitial: true,
};
const watcher = watch(pathToSpecDirectory, watchOptions);
const log = console.log.bind(console);
const handlePath = async path => {
try {
spec = await loadAndBundleSpec(pathToSpec);
pageHTML = await getPageHTML(spec, pathToSpec, options);
log('Updated successfully');
} catch (e) {
console.error('Error while updating: ', e.message);
}
};
watcher
.on('change', async path => {
log(`${path} changed, updating docs`);
try {
spec = await loadAndBundleSpec(pathToSpec);
pageHTML = await getPageHTML(spec, pathToSpec, options);
log('Updated successfully');
} catch (e) {
console.error('Error while updating: ', e.message);
}
handlePath(path);
})
.on('add', async path => {
log(`File ${path} added, updating docs`);
handlePath(path);
})
.on('addDir', path => {
log(`↗ Directory ${path} added. Files in here will trigger reload.`);
})
.on('error', error => console.error(`Watcher error: ${error}`))
.on('ready', () => log(`👀 Watching ${pathToSpecDirectory} for changes...`));
@ -218,7 +241,15 @@ async function bundle(pathToSpec, options: Options = {}) {
async function getPageHTML(
spec: any,
pathToSpec: string,
{ ssr, cdn, title, templateFileName, templateOptions, redocOptions = {} }: Options,
{
ssr,
cdn,
title,
disableGoogleFont,
templateFileName,
templateOptions,
redocOptions = {},
}: Options,
) {
let html;
let css;
@ -261,6 +292,7 @@ async function getPageHTML(
: `<script>${redocStandaloneSrc}</script>`) + css
: '<script src="redoc.standalone.js"></script>',
title,
disableGoogleFont,
templateOptions,
});
}
@ -323,3 +355,15 @@ function handleError(error: Error) {
console.error(error.stack);
process.exit(1);
}
function getObjectOrJSON(options) {
try {
return options && typeof options === 'string'
? JSON.parse(options) : options
? options
: {};
} catch (e) {
console.log(`Encountered error:\n${options}\nis not a valid JSON.`);
handleError(e);
}
}

View File

@ -1,6 +1,6 @@
{
"name": "redoc-cli",
"version": "0.8.6",
"version": "0.9.2",
"description": "ReDoc's Command Line Interface",
"main": "index.js",
"bin": "index.js",
@ -19,7 +19,7 @@
"node-libs-browser": "^2.2.1",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"redoc": "2.0.0-rc.13",
"redoc": "2.0.0-rc.16",
"styled-components": "^4.3.2",
"tslib": "^1.10.0",
"yargs": "^13.3.0"

View File

@ -13,7 +13,7 @@
}
</style>
{{{redocHead}}}
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
{{#unless disableGoogleFont}}<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">{{/unless}}
<link href="https://cdn.fontcdn.ir/Font/Persian/Vazir/Vazir.css" rel="stylesheet">
</head>

View File

@ -629,10 +629,10 @@ domain-browser@^1.1.1:
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
dompurify@^1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-1.0.11.tgz#fe0f4a40d147f7cebbe31a50a1357539cfc1eb4d"
integrity sha512-XywCTXZtc/qCX3iprD1pIklRVk/uhl8BKpkTxr+ZyMVUzSUg7wkQXRBp/euJ5J5moa1QvfpvaPQVP71z1O59dQ==
dompurify@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.0.3.tgz#5cc4965a487d54aedba6ba9634b137cfbd7eb50d"
integrity sha512-q006uOkD2JGSJgF0qBt7rVhUvUPBWCxpGayALmHvXx2iNlMfNVz7PDGeXEUjNGgIDjADz59VZCv6UE3U8XRWVw==
elliptic@^6.0.0:
version "6.5.0"
@ -1092,7 +1092,7 @@ mem@^4.0.0:
mimic-fn "^2.0.0"
p-is-promise "^2.0.0"
memoize-one@^5.0.0, memoize-one@^5.0.5:
memoize-one@^5.0.0, memoize-one@~5.0.5:
version "5.0.5"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.5.tgz#8cd3809555723a07684afafcd6f756072ac75d7e"
integrity sha512-ey6EpYv0tEaIbM/nTDOpHciXUvd+ackQrJgEzBwemhZZIWZjcyodqEcrmqDy2BKRTM3a65kKBV4WtLXJDt26SQ==
@ -1161,10 +1161,10 @@ mobx-react-lite@1.4.0:
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-1.4.0.tgz#193beb5fdddf17ae61542f65ff951d84db402351"
integrity sha512-5xCuus+QITQpzKOjAOIQ/YxNhOl/En+PlNJF+5QU4Qxn9gnNMJBbweAdEW3HnuVQbfqDYEUnkGs5hmkIIStehg==
mobx-react@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.1.1.tgz#24a2c8a3393890fa732b4efd34cc6dcccf6e0e7a"
integrity sha512-hjACWCTpxZf9Sv1YgWF/r6HS6Nsly1SYF22qBJeUE3j+FMfoptgjf8Zmcx2d6uzA07Cezwap5Cobq9QYa0MKUw==
mobx-react@^6.1.3:
version "6.1.3"
resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.1.3.tgz#ad07880ea60cdcdb2a7e2a0d54e01379710cf00a"
integrity sha512-eT/jO9dYIoB1AlZwI2VC3iX0gPOeOIqZsiwg7tDJV1B7Z69h+TZZL3dgOE0UeS2zoHhGeKbP+K+OLeLMnnkGnA==
dependencies:
mobx-react-lite "1.4.0"
@ -1540,10 +1540,10 @@ react-dropdown@^1.6.4:
dependencies:
classnames "^2.2.3"
react-hot-loader@^4.12.10:
version "4.12.10"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.12.10.tgz#b3457c0f733423c4827c6d2672e50c9f8bedaf6b"
integrity sha512-dX+ZUigxQijWLsKPnxc0khuCt2sYiZ1W59LgSBMOLeGSG3+HkknrTlnJu6BCNdhYxbEQkGvBsr7zXlNWYUIhAQ==
react-hot-loader@^4.12.14:
version "4.12.14"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.12.14.tgz#81ca06ffda0b90aad15d6069339f73ed6428340a"
integrity sha512-ecxH4eBvEaJ9onT8vkEmK1FAAJUh1PqzGqds9S3k+GeihSp7nKAp4fOxytO+Ghr491LiBD38jaKyDXYnnpI9pQ==
dependencies:
fast-levenshtein "^2.0.6"
global "^4.3.0"
@ -1602,35 +1602,35 @@ readdirp@^3.1.1:
dependencies:
picomatch "^2.0.4"
redoc@2.0.0-rc.13:
version "2.0.0-rc.13"
resolved "https://registry.yarnpkg.com/redoc/-/redoc-2.0.0-rc.13.tgz#243e4d003ca9bd45006c215d8856a3b1229ca8bb"
integrity sha512-t0vlss1TIUknYXTI9RIZ1nRMyIW/pjo4KMMDFOMdRq5/8jopkNyf37q25BwBuAJfDxQV+tIUoy6o+rAAffeDkQ==
redoc@2.0.0-rc.16:
version "2.0.0-rc.16"
resolved "https://registry.yarnpkg.com/redoc/-/redoc-2.0.0-rc.16.tgz#01d5dafba6ae266a5934dc9904b87bc8a175b222"
integrity sha512-5YWk7NBebYZ8xMbKXA1sD++QsSh7NbnB2sStJRKLeP/rU4oX586SIqHXl+MW1OhIZW44mYFMHpYzxpZKCllk9w==
dependencies:
classnames "^2.2.6"
decko "^1.2.0"
dompurify "^1.0.11"
dompurify "^2.0.3"
eventemitter3 "^4.0.0"
json-pointer "^0.6.0"
json-schema-ref-parser "^6.1.0"
lunr "2.3.6"
mark.js "^8.11.1"
marked "^0.7.0"
memoize-one "^5.0.5"
mobx-react "^6.1.1"
memoize-one "~5.0.5"
mobx-react "^6.1.3"
openapi-sampler "1.0.0-beta.15"
perfect-scrollbar "^1.4.0"
polished "^3.4.1"
prismjs "^1.17.1"
prop-types "^15.7.2"
react-dropdown "^1.6.4"
react-hot-loader "^4.12.10"
react-hot-loader "^4.12.14"
react-tabs "^3.0.0"
slugify "^1.3.4"
slugify "^1.3.5"
stickyfill "^1.1.1"
swagger2openapi "^5.3.1"
tslib "^1.10.0"
uri-template-lite "^19.4.0"
url-template "^2.0.8"
reftools@^1.0.8:
version "1.0.8"
@ -1782,10 +1782,10 @@ signal-exit@^3.0.0:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
slugify@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.4.tgz#78d2792d7222b55cd9fc81fa018df99af779efeb"
integrity sha512-KP0ZYk5hJNBS8/eIjGkFDCzGQIoZ1mnfQRYS5WM3273z+fxGWXeN0fkwf2ebEweydv9tioZIHGZKoF21U07/nw==
slugify@^1.3.5:
version "1.3.5"
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.5.tgz#90210678818b6d533cb060083aed0e8238133508"
integrity sha512-5VCnH7aS13b0UqWOs7Ef3E5rkhFe8Od+cp7wybFv5mv/sYSRkucZlJX0bamAJky7b2TTtGvrJBWVdpdEicsSrA==
source-map@^0.5.0:
version "0.5.7"
@ -2002,10 +2002,10 @@ uglify-js@^3.1.4:
commander "~2.20.0"
source-map "~0.6.1"
uri-template-lite@^19.4.0:
version "19.4.0"
resolved "https://registry.yarnpkg.com/uri-template-lite/-/uri-template-lite-19.4.0.tgz#cbc2c072cf4931428a2f9d3aea36b8254a33cce5"
integrity sha512-VY8dgwyMwnCztkzhq0cA/YhNmO+YZqow//5FdmgE2fZU/JPi+U0rPL7MRDi0F+Ch4vJ7nYidWzeWAeY7uywe9g==
url-template@^2.0.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21"
integrity sha1-/FZaPMy/93MMd19WQflVV5FDnyE=
url@^0.11.0:
version "0.11.0"

View File

@ -1,6 +1,6 @@
{
"name": "redoc",
"version": "2.0.0-rc.14",
"version": "2.0.0-rc.16",
"description": "ReDoc",
"repository": {
"type": "git",
@ -135,10 +135,10 @@
"styled-components": "^4.1.1"
},
"dependencies": {
"classnames": "^2.2.6",
"ajv": "^6.4.0",
"ajv-errors": "^1.0.0",
"brace": "^0.11.1",
"classnames": "^2.2.6",
"decko": "^1.2.0",
"dompurify": "^1.0.11",
"eventemitter3": "^4.0.0",
@ -154,16 +154,18 @@
"polished": "^3.4.1",
"prismjs": "^1.17.1",
"prop-types": "^15.7.2",
"react-dropdown": "^1.6.4",
"react-hot-loader": "^4.12.10",
"react-tabs": "^3.0.0",
"slugify": "^1.3.4",
"qs": "^6.5.2",
"react-ace": "^6.0.0",
"react-dropdown": "^1.6.4",
"react-hot-loader": "^4.12.10",
"react-switch": "^5.0.1",
"react-tabs": "^3.0.0",
"slugify": "^1.3.4",
"stickyfill": "^1.1.1",
"swagger2openapi": "^5.3.1",
"tslib": "^1.10.0",
"uri-template-lite": "^19.4.0"
"uri-template-lite": "^19.4.0",
"url-template": "^2.0.8"
},
"bundlesize": [
{

View File

@ -0,0 +1,43 @@
import * as React from 'react';
import Switch from 'react-switch';
import {FlexLayout} from './index';
import styled from '../styled-components';
const CustomFlexLayout = styled(FlexLayout)`
align-items: center;
`;
interface LabelProps {
active: boolean;
}
const Label = styled.label<LabelProps>`
color: ${props => props.active ? props.theme.colors.success.main : props.theme.colors.text.secondary}
margin-left: 10px;
font-size: 120%;
`;
interface TryItOutProps {
label: string;
checked: boolean;
onClick: () => void;
}
export class SwitchBox extends React.PureComponent<TryItOutProps> {
id = 'toggle-id-' + Date.now();
render() {
const { label, checked, onClick } = this.props;
return (
<CustomFlexLayout>
<Switch
id={this.id}
onChange={onClick}
checked={checked}
uncheckedIcon={false}
/>
<Label active={checked} htmlFor={this.id}>{label}</Label>
</CustomFlexLayout>
);
}
}

View File

@ -9,12 +9,15 @@ export const Button = styled.button`
padding: 5px;
`;
export const SendButton = styled(Button)`
background: #B0045E;
`;
export const ConsoleButton = styled(Button)`
background: #e2e2e2;
color: black;
float: right;
export const SubmitButton = styled(Button)`
background: ${props => props.theme.colors.primary.main}
padding: 10px 30px;
border-radius: 4px;
cursor: pointer;
text-align: center;
outline: none;
margin: 1em 0;
min-width: 60px;
font-weight: bold;
order: 1;
`;

View File

@ -1,16 +1,18 @@
import { SECTION_ATTR } from '../services/MenuStore';
import styled, { media } from '../styled-components';
export const MiddlePanel = styled.div`
export const MiddlePanel = styled.div<{ compact?: boolean }>`
width: calc(100% - ${props => props.theme.rightPanel.width});
padding: 0 ${props => props.theme.spacing.sectionHorizontal}px;
direction: ${props => props.theme.typography.direction || 'ltr'};
text-align: ${props => (props.theme.typography.direction === 'rtl') ? 'right' : 'inherit'};
${media.lessThan('medium', true)`
${({ compact, theme }) =>
media.lessThan('medium', true)`
width: 100%;
padding: ${props =>
`${props.theme.spacing.sectionVertical}px ${props.theme.spacing.sectionHorizontal}px`};
padding: ${`${compact ? 0 : theme.spacing.sectionVertical}px ${
theme.spacing.sectionHorizontal
}px`};
`};
`;
@ -80,10 +82,6 @@ export const FlexLayout = styled.div`
width: 100%;
`;
export const ConsoleActionsRow = styled(FlexLayout)`
padding: 5px 0px;
`;
export const FlexLayoutReverse = styled(FlexLayout)`
flex-direction: row-reverse;
`;

View File

@ -8,7 +8,8 @@ import 'brace/mode/json';
import 'brace/theme/github';
import 'brace/theme/monokai';
import { MediaTypeModel } from '../../services/models';
import {MediaTypeModel} from '../../services/models';
import {ConsoleEditorWrapper} from './ConsoleEditorWrapper';
export interface ConsoleEditorProps {
mediaTypes: MediaTypeModel[];
@ -37,7 +38,7 @@ export class ConsoleEditor extends React.Component<ConsoleEditorProps> {
}
return (
<div>
<ConsoleEditorWrapper>
<AceEditor
setOptions={{
enableBasicAutocompletion: true,
@ -46,17 +47,14 @@ export class ConsoleEditor extends React.Component<ConsoleEditorProps> {
showLineNumbers: true,
tabSize: 2,
}}
fontSize={10}
fontSize={15}
mode="json"
theme="monokai"
name="request-builder-editor"
editorProps={{ $blockScrolling: true }}
value={JSON.stringify(sample, null, 2)}
ref={(ace: AceEditor) => (this.editor = ace)}
width="100%"
height="400px"
/>
</div>
</ConsoleEditorWrapper>
);
}

View File

@ -0,0 +1,75 @@
import {lighten} from 'polished';
import styled from '../../styled-components';
export const ConsoleEditorWrapper = styled.div`
font-family: ${props => props.theme.typography.code.fontFamily};
font-size: ${props => props.theme.typography.code.fontSize} !important;
direction: ltr;
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
contain: content;
overflow-x: auto;
background: #11171a !important;
padding: 5px 0;
& .ace_editor {
background: #11171a !important;
width: 100% !important;
}
& .ace_editor .ace_marker-layer .ace_selection {
background: ${lighten(0.05, '#11171a')} !important;
}
& .ace_editor .ace_marker-layer .ace_active-line {
background: rgba(0, 0, 0, 0.2);
}
& .ace_editor .ace_line, & .ace_editor .ace_cursor {
color: #aaa;
}
& .ace_editor .ace_marker-layer .ace_bracket {
border: none !important;
}
& .ace_editor .ace_line .ace_fold {
background: none !important;
color: #aaa;
}
& .ace_editor .ace_line .ace_fold:hover {
background: none !important;
}
& .ace_editor .ace_string {
color: #71e4ff;
}
& .ace_editor .ace_variable {
color: #a0fbaa;
}
& .ace_editor .ace_indent-guide {
background: none;
color: rgba(255, 255, 255, 0.3)
}
& .ace_editor .ace_indent-guide::after {
content: "|";
}
& .ace_editor .ace_gutter {
background: ${lighten(0.01, '#11171a')} !important;
color: #fff !important;
}
& .ace_editor .ace_gutter .ace_fold-widget {
background-image: none;
}
& .ace_editor .ace_gutter .ace_fold-widget.ace_open::after {
content: "-";
}
& .ace_editor .ace_gutter .ace_fold-widget.ace_closed::after {
content: "+";
}
& .ace_editor .ace_gutter .ace_gutter-active-line {
background: rgba(0, 0, 0, 0.2) !important;
}
& .ace_editor .ace_gutter .ace_gutter-cell.ace_error {
background: none !important;
}
& .ace_editor .ace_gutter .ace_gutter-cell.ace_error::before {
position: absolute;
color: red;
content: "X";
left: 0.5em;
}
`;

View File

@ -1,7 +1,7 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { SendButton } from '../../common-elements/buttons';
import { ConsoleActionsRow } from '../../common-elements/panels';
import { SubmitButton } from '../../common-elements/buttons';
import { FlexLayoutReverse } from '../../common-elements/panels';
import { FieldModel, OperationModel } from '../../services/models';
import { OpenAPISchema } from '../../types';
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
@ -30,7 +30,7 @@ export class ConsoleViewer extends React.Component<ConsoleViewerProps, ConsoleVi
operation: OperationModel;
additionalHeaders: object;
visited = new Set();
private consoleEditor: ConsoleEditor;
private consoleEditor: any;
constructor(props) {
super(props);
@ -153,24 +153,20 @@ export class ConsoleViewer extends React.Component<ConsoleViewerProps, ConsoleVi
const { operation } = this.props;
const requestBodyContent = operation.requestBody && operation.requestBody.content && operation.requestBody.content;
const hasBodySample = requestBodyContent && requestBodyContent.hasSample;
const samples = operation.codeSamples;
const mediaTypes = (requestBodyContent && requestBodyContent.mediaTypes) ? requestBodyContent.mediaTypes : [];
const { result } = this.state;
return (
<div>
<h3> Console </h3>
<h3> Request </h3>
{hasBodySample && (
<ConsoleEditor
mediaTypes={mediaTypes}
ref={(editor: ConsoleEditor) => (this.consoleEditor = editor)}
ref={(editor: any) => (this.consoleEditor = editor)}
/>
)}
{false && samples.map(sample => (
<SourceCodeWithCopy lang={sample.lang} source={sample.source} />
))}
<ConsoleActionsRow>
<SendButton onClick={this.onClickSend} >Send Request</SendButton>
</ConsoleActionsRow>
<FlexLayoutReverse>
<SubmitButton onClick={this.onClickSend} >Send Request</SubmitButton>
</FlexLayoutReverse>
{result &&
<SourceCodeWithCopy lang="json" source={JSON.stringify(result, null, 2)} />
}

View File

@ -60,7 +60,7 @@ export class ContentItem extends React.Component<ContentItemProps> {
}
}
const middlePanelWrap = component => <MiddlePanel>{component}</MiddlePanel>;
const middlePanelWrap = component => <MiddlePanel compact={true}>{component}</MiddlePanel>;
@observer
export class SectionItem extends React.Component<ContentItemProps> {
@ -71,7 +71,7 @@ export class SectionItem extends React.Component<ContentItemProps> {
return (
<>
<Row>
<MiddlePanel>
<MiddlePanel compact={level !== 1}>
<Header>
<ShareLink to={this.props.item.id} />
{name}

View File

@ -5,7 +5,7 @@ import { Markdown } from '../Markdown/Markdown';
import { OptionsContext } from '../OptionsProvider';
import { SelectOnClick } from '../SelectOnClick/SelectOnClick';
import { getBasePath } from '../../utils';
import { expandDefaultServerVariables, getBasePath } from '../../utils';
import {
EndpointInfo,
HttpVerb,
@ -61,15 +61,18 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
</EndpointInfo>
<ServersOverlay expanded={expanded}>
{operation.servers.map(server => {
const normalizedUrl = options.expandDefaultServerVariables
? expandDefaultServerVariables(server.url, server.variables)
: server.url;
return (
<ServerItem key={server.url}>
<ServerItem key={normalizedUrl}>
<Markdown source={server.description || ''} compact={true} />
<SelectOnClick>
<ServerUrl>
<span>
{hideHostname || options.hideHostname
? getBasePath(server.url)
: server.url}
? getBasePath(normalizedUrl)
: normalizedUrl}
</span>
{operation.path}
</ServerUrl>

View File

@ -1,25 +1,25 @@
import * as React from 'react';
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Badge, ConsoleButton, DarkRightPanel, FlexLayoutReverse, H2, MiddlePanel, Row } from '../../common-elements';
import { OptionsContext } from '../OptionsProvider';
import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elements';
import { ShareLink } from '../../common-elements/linkify';
import { ConsoleViewer } from '../Console/ConsoleViewer';
import { Endpoint } from '../Endpoint/Endpoint';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { Markdown } from '../Markdown/Markdown';
import { Parameters } from '../Parameters/Parameters';
import { RequestSamples } from '../RequestSamples/RequestSamples';
import { ResponsesList } from '../Responses/ResponsesList';
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
import { OperationModel as OperationType } from '../../services/models';
import styled from '../../styled-components';
import { ConsoleViewer } from '../Console/ConsoleViewer';
import { Endpoint } from '../Endpoint/Endpoint';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { Extensions } from '../Fields/Extensions';
import { Markdown } from '../Markdown/Markdown';
import {SwitchBox} from '../../common-elements/SwitchBox';
import {OptionsContext } from '../OptionsProvider';
import {Parameters } from '../Parameters/Parameters';
import {RequestSamples } from '../RequestSamples/RequestSamples';
import {ResponsesList } from '../Responses/ResponsesList';
import {ResponseSamples } from '../ResponseSamples/ResponseSamples';
import {SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
const OperationRow = styled(Row)`
backface-visibility: hidden;
@ -62,7 +62,6 @@ export class Operation extends React.Component<OperationProps, OperationState> {
const { name: summary, description, deprecated, externalDocs } = operation;
const hasDescription = !!(description || externalDocs);
const consoleButtonLabel = (executeMode) ? 'Hide Console' : 'Show Console';
return (
<OptionsContext.Consumer>
@ -74,9 +73,11 @@ export class Operation extends React.Component<OperationProps, OperationState> {
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
</H2>
{options.enableConsole &&
<FlexLayoutReverse>
<ConsoleButton onClick={this.onConsoleClick}>{consoleButtonLabel}</ConsoleButton>
</FlexLayoutReverse>
<SwitchBox
onClick={this.onConsoleClick}
checked={this.state.executeMode}
label="Try it out!"
/>
}
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
{hasDescription && (

View File

@ -44,9 +44,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
if (discriminatorProp !== undefined) {
if (!oneOf || !oneOf.length) {
throw new Error(
`Looks like you are using discriminator wrong: you don't have any definition inherited from the ${
schema.title
}`,
`Looks like you are using discriminator wrong: you don't have any definition inherited from the ${schema.title}`,
);
}
return (
@ -66,9 +64,9 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
switch (type) {
case 'object':
return <ObjectSchema {...this.props as any} />;
return <ObjectSchema {...(this.props as any)} />;
case 'array':
return <ArraySchema {...this.props as any} />;
return <ArraySchema {...(this.props as any)} />;
}
// TODO: maybe adjust FieldDetails to accept schema

View File

@ -52,7 +52,7 @@ export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
<strong> Scopes: </strong>
</div>
<ul>
{Object.keys(flow!.scopes).map(scope => (
{Object.keys(flow!.scopes || {}).map(scope => (
<li key={scope}>
<code>{scope}</code> - <Markdown inline={true} source={flow!.scopes[scope] || ''} />
</li>

View File

@ -19,6 +19,10 @@ export interface StickySidebarProps {
menu: MenuStore;
}
export interface StickySidebarState {
offsetTop?: string;
}
const stickyfill = Stickyfill && Stickyfill();
const StyledStickySidebar = styled.div<{ open?: boolean }>`
@ -77,13 +81,26 @@ const FloatingButton = styled.div`
`;
@observer
export class StickyResponsiveSidebar extends React.Component<StickySidebarProps> {
export class StickyResponsiveSidebar extends React.Component<
StickySidebarProps,
StickySidebarState
> {
static contextType = OptionsContext;
context!: React.ContextType<typeof OptionsContext>;
state: StickySidebarState = { offsetTop: '0px' };
stickyElement: Element;
componentDidMount() {
if (stickyfill) {
stickyfill.add(this.stickyElement);
}
// rerender when hydrating from SSR
// see https://github.com/facebook/react/issues/8017#issuecomment-256351955
this.setState({
offsetTop: this.getScrollYOffset(this.context),
});
}
componentWillUnmount() {
@ -92,7 +109,7 @@ export class StickyResponsiveSidebar extends React.Component<StickySidebarProps>
}
}
getScrollYOffset(options) {
getScrollYOffset(options: RedocNormalizedOptions) {
let top;
if (this.props.scrollYOffset !== undefined) {
top = RedocNormalizedOptions.normalizeScrollYOffset(this.props.scrollYOffset)();
@ -105,43 +122,32 @@ export class StickyResponsiveSidebar extends React.Component<StickySidebarProps>
render() {
const open = this.props.menu.sideBarOpened;
const style = options => {
const top = this.getScrollYOffset(options);
return {
top,
height: `calc(100vh - ${top})`,
};
};
const top = this.state.offsetTop;
return (
<OptionsContext.Consumer>
{options => (
<>
<StyledStickySidebar
open={open}
className={this.props.className}
style={style(options)}
// tslint:disable-next-line
ref={el => {
this.stickyElement = el as any;
}}
>
{this.props.children}
</StyledStickySidebar>
<FloatingButton onClick={this.toggleNavMenu}>
<AnimatedChevronButton open={open} />
</FloatingButton>
</>
)}
</OptionsContext.Consumer>
<>
<StyledStickySidebar
open={open}
className={this.props.className}
style={{
top,
height: `calc(100vh - ${top})`,
}}
// tslint:disable-next-line
ref={el => {
this.stickyElement = el as any;
}}
>
{this.props.children}
</StyledStickySidebar>
<FloatingButton onClick={this.toggleNavMenu}>
<AnimatedChevronButton open={open} />
</FloatingButton>
</>
);
}
private toggleNavMenu = () => {
this.props.menu.toggleSidebar();
};
// private closeNavMenu = () => {
// this.setState({ open: false });
// };
}

View File

@ -6,6 +6,7 @@ exports[`Components SchemaView discriminator should correctly render discriminat
<Field
field={
FieldModel {
"$value": "",
"deprecated": false,
"description": "",
"example": undefined,
@ -56,6 +57,7 @@ exports[`Components SchemaView discriminator should correctly render discriminat
<Field
field={
FieldModel {
"$value": "",
"deprecated": false,
"description": "",
"example": undefined,

View File

@ -116,7 +116,7 @@ export class MenuStore {
}
if (isScrolledDown) {
const el = this.getElementAt(itemIdx + 1);
const el = this.getElementAtOrFirstChild(itemIdx + 1);
if (this.scroll.isElementBellow(el)) {
break;
}
@ -163,6 +163,18 @@ export class MenuStore {
return (item && querySelector(`[${SECTION_ATTR}="${item.id}"]`)) || null;
}
/**
* get section/operation DOM Node related to the item or if it is group item, returns first item of the group
* @param idx item absolute index
*/
getElementAtOrFirstChild(idx: number): Element | null {
let item = this.flatItems[idx];
if (item && item.type === 'group') {
item = item.items[0];
}
return (item && querySelector(`[${SECTION_ATTR}="${item.id}"]`)) || null;
}
/**
* current active item
*/
@ -189,6 +201,11 @@ export class MenuStore {
if ((this.activeItem && this.activeItem.id) === (item && item.id)) {
return;
}
if (item && item.type === 'group') {
return;
}
this.deactivate(this.activeItem);
if (!item) {
this.history.replace('', rewriteHistory);

View File

@ -4,7 +4,11 @@ import { OpenAPIRef, OpenAPISchema, OpenAPISpec, Referenced } from '../types';
import { appendToMdHeading, IS_BROWSER } from '../utils/';
import { JsonPointer } from '../utils/JsonPointer';
import { isNamedDefinition, SECURITY_DEFINITIONS_COMPONENT_NAME } from '../utils/openapi';
import {
isNamedDefinition,
SECURITY_DEFINITIONS_COMPONENT_NAME,
SECURITY_DEFINITIONS_JSX_NAME,
} from '../utils/openapi';
import { buildComponentComment, MarkdownRenderer } from './MarkdownRenderer';
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
@ -40,6 +44,7 @@ class RefCounter {
export class OpenAPIParser {
specUrl?: string;
spec: OpenAPISpec;
mergeRefs: Set<string>;
private _refCounter: RefCounter = new RefCounter();
@ -53,6 +58,8 @@ export class OpenAPIParser {
this.spec = spec;
this.mergeRefs = new Set();
const href = IS_BROWSER ? window.location.href : '';
if (typeof specUrl === 'string') {
this.specUrl = urlResolve(href, specUrl);
@ -74,7 +81,10 @@ export class OpenAPIParser {
) {
// Automatically inject Authentication section with SecurityDefinitions component
const description = spec.info.description || '';
if (!MarkdownRenderer.containsComponent(description, SECURITY_DEFINITIONS_COMPONENT_NAME)) {
if (
!MarkdownRenderer.containsComponent(description, SECURITY_DEFINITIONS_COMPONENT_NAME) &&
!MarkdownRenderer.containsComponent(description, SECURITY_DEFINITIONS_JSX_NAME)
) {
const comment = buildComponentComment(SECURITY_DEFINITIONS_COMPONENT_NAME);
spec.info.description = appendToMdHeading(description, 'Authentication', comment);
}
@ -176,7 +186,12 @@ export class OpenAPIParser {
schema: OpenAPISchema,
$ref?: string,
forceCircular: boolean = false,
used$Refs = new Set<string>(),
): MergedOpenAPISchema {
if ($ref) {
used$Refs.add($ref);
}
schema = this.hoistOneOfs(schema);
if (schema.allOf === undefined) {
@ -198,16 +213,25 @@ export class OpenAPIParser {
receiver.items = { ...receiver.items };
}
const allOfSchemas = schema.allOf.map(subSchema => {
const resolved = this.deref(subSchema, forceCircular);
const subRef = subSchema.$ref || undefined;
const subMerged = this.mergeAllOf(resolved, subRef, forceCircular);
receiver.parentRefs!.push(...(subMerged.parentRefs || []));
return {
$ref: subRef,
schema: subMerged,
};
});
const allOfSchemas = schema.allOf
.map(subSchema => {
if (subSchema && subSchema.$ref && used$Refs.has(subSchema.$ref)) {
return undefined;
}
const resolved = this.deref(subSchema, forceCircular);
const subRef = subSchema.$ref || undefined;
const subMerged = this.mergeAllOf(resolved, subRef, forceCircular, used$Refs);
receiver.parentRefs!.push(...(subMerged.parentRefs || []));
return {
$ref: subRef,
schema: subMerged,
};
})
.filter(child => child !== undefined) as Array<{
$ref: string | undefined;
schema: MergedOpenAPISchema;
}>;
for (const { $ref: subSchemaRef, schema: subSchema } of allOfSchemas) {
if (

View File

@ -37,7 +37,10 @@ export interface RedocRawOptions {
allowedMdComponents?: Dict<MDXComponentMeta>;
labels?: LabelsConfigRaw;
enumSkipQuotes?: boolean | string;
expandDefaultServerVariables?: boolean;
}
function argValueToBoolean(val?: string | boolean): boolean {
@ -159,6 +162,8 @@ export class RedocNormalizedOptions {
unstable_ignoreMimeParameters: boolean;
allowedMdComponents: Dict<MDXComponentMeta>;
expandDefaultServerVariables: boolean;
constructor(raw: RedocRawOptions, defaults: RedocRawOptions = {}) {
raw = { ...defaults, ...raw };
const hook = raw.theme && raw.theme.extensionsHook;
@ -200,5 +205,7 @@ export class RedocNormalizedOptions {
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);
this.allowedMdComponents = raw.allowedMdComponents || {};
this.expandDefaultServerVariables = argValueToBoolean(raw.expandDefaultServerVariables);
}
}

View File

@ -60,7 +60,7 @@ describe('Utils', () => {
test('should behave like Object.assign on the top level', () => {
const obj1 = { a: { a1: 'A1' }, c: 'C' };
const obj2 = { a: undefined, b: { b1: 'B1' } };
expect(mergeObjects({}, obj1, obj2)).toEqual(Object.assign({}, obj1, obj2));
expect(mergeObjects({}, obj1, obj2)).toEqual({ ...obj1, ...obj2 });
});
test('should not merge array values, just override', () => {
const obj1 = { a: ['A', 'B'] };

View File

@ -13,6 +13,7 @@ import {
import { FieldModel, OpenAPIParser, RedocNormalizedOptions } from '../../services';
import { OpenAPIParameter, OpenAPIParameterLocation, OpenAPIParameterStyle } from '../../types';
import { expandDefaultServerVariables } from '../openapi';
describe('Utils', () => {
describe('openapi getStatusCode', () => {
@ -293,6 +294,39 @@ describe('Utils', () => {
]);
expect(res).toEqual([{ url: 'https://base.com/sandbox/test', description: 'test' }]);
});
it('should expand variables', () => {
const servers = normalizeServers('', [
{
url: 'http://{host}{basePath}',
variables: {
host: {
default: '127.0.0.1',
},
basePath: {
default: '/path/to/endpoint',
},
},
},
{
url: 'http://127.0.0.2:{port}',
variables: {},
},
{
url: 'http://127.0.0.3',
},
]);
expect(expandDefaultServerVariables(servers[0].url, servers[0].variables)).toEqual(
'http://127.0.0.1/path/to/endpoint',
);
expect(expandDefaultServerVariables(servers[1].url, servers[1].variables)).toEqual(
'http://127.0.0.2:{port}',
);
expect(expandDefaultServerVariables(servers[2].url, servers[2].variables)).toEqual(
'http://127.0.0.3',
);
});
});
describe('openapi humanizeConstraints', () => {
@ -404,7 +438,7 @@ describe('Utils', () => {
{ style: 'simple', explode: false, expected: 'role,admin,firstName,Alex' },
{ style: 'simple', explode: true, expected: 'role=admin,firstName=Alex' },
{ style: 'label', explode: false, expected: '.role,admin,firstName,Alex' },
{ style: 'label', explode: true, expected: '.role=admin,firstName=Alex' },
{ style: 'label', explode: true, expected: '.role=admin.firstName=Alex' },
{ style: 'matrix', explode: false, expected: ';id=role,admin,firstName,Alex' },
{ style: 'matrix', explode: true, expected: ';role=admin;firstName=Alex' },
],
@ -516,9 +550,7 @@ describe('Utils', () => {
locationTestGroup.cases.forEach(valueTypeTestGroup => {
describe(valueTypeTestGroup.description, () => {
valueTypeTestGroup.cases.forEach(testCase => {
it(`should serialize correctly when style is ${testCase.style} and explode is ${
testCase.explode
}`, () => {
it(`should serialize correctly when style is ${testCase.style} and explode is ${testCase.explode}`, () => {
const parameter: OpenAPIParameter = {
name: locationTestGroup.name,
in: locationTestGroup.location,

View File

@ -83,7 +83,7 @@ export function appendToMdHeading(md: string, heading: string, content: string)
}
// credits https://stackoverflow.com/a/46973278/1749888
export const mergeObjects = <T extends object = object>(target: T, ...sources: T[]): T => {
export const mergeObjects = (target: any, ...sources: any[]): any => {
if (!sources.length) {
return target;
}

View File

@ -1,5 +1,5 @@
import { dirname } from 'path';
import { URI } from 'uri-template-lite';
const URLtemplate = require('url-template');
import { OpenAPIParser } from '../services/OpenAPIParser';
import {
@ -168,7 +168,7 @@ function serializeFormValue(name: string, explode: boolean, value: any) {
// e.g. URI.template doesn't parse names with hypen (-) which are valid query param names
const safeName = '__redoc_param_name__';
const suffix = explode ? '*' : '';
const template = new URI.Template(`{?${safeName}${suffix}}`);
const template = URLtemplate.parse(`{?${safeName}${suffix}}`);
return template
.expand({ [safeName]: value })
.substring(1)
@ -227,7 +227,7 @@ function serializePathParameter(
// Use RFC6570 safe name ([a-zA-Z0-9_]) and replace with our name later
// e.g. URI.template doesn't parse names with hypen (-) which are valid query param names
const safeName = '__redoc_param_name__';
const template = new URI.Template(`{${prefix}${safeName}${suffix}}`);
const template = URLtemplate.parse(`{${prefix}${safeName}${suffix}}`);
return template.expand({ [safeName]: value }).replace(/__redoc_param_name__/g, name);
}
@ -263,7 +263,7 @@ function serializeQueryParameter(
return `${name}=${value.join('|')}`;
case 'deepObject':
if (!explode || Array.isArray(value) || typeof value !== 'object') {
console.warn('The style deepObject is only applicable for objects with expolde=true');
console.warn('The style deepObject is only applicable for objects with explode=true');
return '';
}
@ -285,7 +285,7 @@ function serializeHeaderParameter(
// name is not important here, so use RFC6570 safe name ([a-zA-Z0-9_])
const name = '__redoc_param_name__';
const template = new URI.Template(`{${name}${suffix}}`);
const template = URLtemplate.parse(`{${name}${suffix}}`);
return decodeURIComponent(template.expand({ [name]: value }));
default:
console.warn('Unexpected style for header: ' + style);
@ -487,6 +487,13 @@ export function mergeSimilarMediaTypes(types: Dict<OpenAPIMediaType>): Dict<Open
return mergedTypes;
}
export function expandDefaultServerVariables(url: string, variables: object = {}) {
return url.replace(
/(?:{)(\w+)(?:})/g,
(match, name) => (variables[name] && variables[name].default) || match,
);
}
export function normalizeServers(
specUrl: string | undefined,
servers: OpenAPIServer[],

View File

@ -1467,7 +1467,7 @@ ajv@^5.5.2:
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
ajv@^6.1.0, ajv@^6.10.2, ajv@^6.4.0, ajv@^6.5.5, ajv@^6.9.1:
ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.5, ajv@^6.9.1:
version "6.10.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
@ -1477,6 +1477,16 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.4.0, ajv@^6.5.5, ajv@^6.9.1:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^6.4.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9"
integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ansi-colors@^3.0.0:
version "3.2.4"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
@ -4055,6 +4065,11 @@ fast-deep-equal@^2.0.1:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
fast-deep-equal@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
@ -8072,6 +8087,13 @@ react-lifecycles-compat@^3.0.4:
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-switch@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-switch/-/react-switch-5.0.1.tgz#449277f4c3aed5286fffd0f50d5cbc2a23330406"
integrity sha512-Pa5kvqRfX85QUCK1Jv0rxyeElbC3aNpCP5hV0LoJpU/Y6kydf0t4kRriQ6ZYA4kxWwAYk/cH51T4/sPzV9mCgQ==
dependencies:
prop-types "^15.6.2"
react-tabs@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/react-tabs/-/react-tabs-3.0.0.tgz#60311a17c755eb6aa9b3310123e67db421605127"
@ -9901,6 +9923,11 @@ url-polyfill@^1.1.7:
resolved "https://registry.yarnpkg.com/url-polyfill/-/url-polyfill-1.1.7.tgz#402ee84360eb549bbeb585f4c7971e79a31de9e3"
integrity sha512-ZrAxYWCREjmMtL8gSbSiKKLZZticgihCvVBtrFbUVpyoETt8GQJeG2okMWA8XryDAaHMjJfhnc+rnhXRbI4DXA==
url-template@^2.0.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21"
integrity sha1-/FZaPMy/93MMd19WQflVV5FDnyE=
url@0.11.0, url@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"