mirror of
https://github.com/Redocly/redoc.git
synced 2025-02-07 13:30:33 +03:00
Make ReDoc render sync + separate standalone componenet
- refactored usage of store so now <Redoc> is sync - other minor refactors
This commit is contained in:
parent
0061a87496
commit
109d135959
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"editor.formatOnSave": false
|
"editor.formatOnSave": true
|
||||||
}
|
}
|
|
@ -71,14 +71,12 @@
|
||||||
"preact": "^8.2.5",
|
"preact": "^8.2.5",
|
||||||
"preact-compat": "^3.17.0",
|
"preact-compat": "^3.17.0",
|
||||||
"prismjs": "^1.8.1",
|
"prismjs": "^1.8.1",
|
||||||
"prop-types": "^15.6.0",
|
|
||||||
"react": "^16.0.0",
|
"react": "^16.0.0",
|
||||||
"react-dom": "^16.0.0",
|
"react-dom": "^16.0.0",
|
||||||
"react-dropdown": "^1.3.0",
|
"react-dropdown": "^1.3.0",
|
||||||
"react-markdown": "^2.5.0",
|
"react-markdown": "^2.5.0",
|
||||||
"react-perfect-scrollbar": "^0.2.2",
|
"react-perfect-scrollbar": "^0.2.2",
|
||||||
"react-tabs": "^2.0.0",
|
"react-tabs": "^2.0.0",
|
||||||
"recompose": "^0.25.1",
|
|
||||||
"remarkable": "^1.7.1",
|
"remarkable": "^1.7.1",
|
||||||
"slugify": "^1.2.1",
|
"slugify": "^1.2.1",
|
||||||
"styled-components": "^2.2.1"
|
"styled-components": "^2.2.1"
|
||||||
|
|
|
@ -11,3 +11,12 @@ export const RightPanel = styled.div`
|
||||||
bckground-color: ${props => props.theme.rightPanel.backgroundColor};
|
bckground-color: ${props => props.theme.rightPanel.backgroundColor};
|
||||||
padding: ${props => props.theme.spacingUnit * 2}px;
|
padding: ${props => props.theme.spacingUnit * 2}px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const DarkRightPanel = styled(RightPanel)`
|
||||||
|
background-color: ${props => props.theme.rightPanel.backgroundColor};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Row = styled.div`
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
|
@ -7,8 +7,9 @@ import { OpenAPIExternalDocumentation } from '../../types';
|
||||||
import { ApiInfoModel } from '../../services/models';
|
import { ApiInfoModel } from '../../services/models';
|
||||||
import { SecurityDefs } from '../SecurityDefs/SecurityDefs';
|
import { SecurityDefs } from '../SecurityDefs/SecurityDefs';
|
||||||
|
|
||||||
|
import { MiddlePanel, DarkRightPanel, Row } from '../../common-elements/';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ApiInfoWrap,
|
|
||||||
ApiHeader,
|
ApiHeader,
|
||||||
DownloadButton,
|
DownloadButton,
|
||||||
InfoSpan,
|
InfoSpan,
|
||||||
|
@ -18,7 +19,7 @@ import {
|
||||||
|
|
||||||
interface ApiInfoProps {
|
interface ApiInfoProps {
|
||||||
info: ApiInfoModel;
|
info: ApiInfoModel;
|
||||||
externalDocs: OpenAPIExternalDocumentation;
|
externalDocs?: OpenAPIExternalDocumentation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
|
@ -65,7 +66,8 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
null;
|
null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApiInfoWrap className="api-info">
|
<Row>
|
||||||
|
<MiddlePanel className="api-info">
|
||||||
<ApiHeader>
|
<ApiHeader>
|
||||||
{info.title} <span>({info.version})</span>
|
{info.title} <span>({info.version})</span>
|
||||||
</ApiHeader>
|
</ApiHeader>
|
||||||
|
@ -101,7 +103,9 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
components={{ 'security-definitions': SecurityDefs }}
|
components={{ 'security-definitions': SecurityDefs }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ApiInfoWrap>
|
</MiddlePanel>
|
||||||
|
<DarkRightPanel />
|
||||||
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { observer } from 'mobx-react';
|
|
||||||
import * as PropTypes from 'prop-types';
|
|
||||||
import { getContext } from 'recompose';
|
|
||||||
|
|
||||||
import { BaseContainerProps } from '../../types/components';
|
|
||||||
import { ApiInfo } from './ApiInfo';
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class ApiInfoContainer extends React.Component<BaseContainerProps> {
|
|
||||||
render() {
|
|
||||||
const { info, externalDocs } = this.props.store.spec;
|
|
||||||
return <ApiInfo info={info!} externalDocs={externalDocs!} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getContext<BaseContainerProps>({
|
|
||||||
store: PropTypes.object,
|
|
||||||
})(ApiInfoContainer);
|
|
|
@ -1,18 +1,14 @@
|
||||||
|
import { OpenAPIInfo } from '../../types';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import * as PropTypes from 'prop-types';
|
|
||||||
import { getContext } from 'recompose';
|
|
||||||
|
|
||||||
import { BaseContainerProps } from '../../types/components';
|
|
||||||
import { LogoImgEl } from './styled.elements';
|
import { LogoImgEl } from './styled.elements';
|
||||||
|
|
||||||
const LinkWrap = url => Component => <a href={url}>{Component}</a>;
|
const LinkWrap = url => Component => <a href={url}>{Component}</a>;
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class ApiLogo extends React.Component<BaseContainerProps> {
|
export class ApiLogo extends React.Component<{ info: OpenAPIInfo }> {
|
||||||
render() {
|
render() {
|
||||||
const { spec } = this.props.store;
|
const { info } = this.props;
|
||||||
const info = spec.info!;
|
|
||||||
const logoInfo = info['x-logo'];
|
const logoInfo = info['x-logo'];
|
||||||
if (!logoInfo || !logoInfo.url) return null;
|
if (!logoInfo || !logoInfo.url) return null;
|
||||||
|
|
||||||
|
@ -22,7 +18,3 @@ class ApiLogo extends React.Component<BaseContainerProps> {
|
||||||
return info.contact && info.contact.url ? LinkWrap(info.contact.url)(logo) : logo;
|
return info.contact && info.contact.url ? LinkWrap(info.contact.url)(logo) : logo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getContext<BaseContainerProps>({
|
|
||||||
store: PropTypes.object,
|
|
||||||
})(ApiLogo);
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import * as PropTypes from 'prop-types';
|
|
||||||
import { getContext } from 'recompose';
|
|
||||||
import { observer } from 'mobx-react';
|
|
||||||
|
|
||||||
import { BaseContainerProps } from '../../types/components';
|
|
||||||
import { ContentItems } from './ContentItems';
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class ContentContainer extends React.Component<BaseContainerProps> {
|
|
||||||
render() {
|
|
||||||
const items = this.props.store.menu.items;
|
|
||||||
return <ContentItems items={items as any} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getContext<BaseContainerProps>({
|
|
||||||
store: PropTypes.object,
|
|
||||||
})(ContentContainer);
|
|
|
@ -4,7 +4,7 @@ import { observer } from 'mobx-react';
|
||||||
import { SECTION_ATTR } from '../../services/MenuStore';
|
import { SECTION_ATTR } from '../../services/MenuStore';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
|
|
||||||
import { H1, MiddlePanel, ShareLink } from '../../common-elements';
|
import { DarkRightPanel, H1, MiddlePanel, ShareLink, Row } from '../../common-elements';
|
||||||
import { Operation } from '../Operation/Operation';
|
import { Operation } from '../Operation/Operation';
|
||||||
import { ContentItemModel } from '../../services/MenuBuilder';
|
import { ContentItemModel } from '../../services/MenuBuilder';
|
||||||
import { OperationModel } from '../../services/models';
|
import { OperationModel } from '../../services/models';
|
||||||
|
@ -60,13 +60,16 @@ export class TagItem extends React.Component<ContentItemProps> {
|
||||||
render() {
|
render() {
|
||||||
const { name, description } = this.props.item;
|
const { name, description } = this.props.item;
|
||||||
return (
|
return (
|
||||||
<MiddlePanel>
|
<Row>
|
||||||
|
<MiddlePanel key="middle">
|
||||||
<H1>
|
<H1>
|
||||||
<ShareLink href={'#' + this.props.item.getHash()} />
|
<ShareLink href={'#' + this.props.item.getHash()} />
|
||||||
{name}
|
{name}
|
||||||
</H1>
|
</H1>
|
||||||
{description !== undefined && <Markdown source={description} />}
|
{description !== undefined && <Markdown source={description} />}
|
||||||
</MiddlePanel>
|
</MiddlePanel>
|
||||||
|
<DarkRightPanel key="right" />
|
||||||
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
import ApiInfoContainer from '../ApiInfo/ApiInfoContainer';
|
|
||||||
import { RedocWrap, MenuContent, ApiContent, Background } from './elements';
|
|
||||||
import ApiLogo from '../ApiLogo/ApiLogo';
|
|
||||||
import SideMenu from '../SideMenu/SideMenu';
|
|
||||||
import ContentContainer from '../ContentItems/ContentContainer';
|
|
||||||
|
|
||||||
export class ContentRoot extends React.Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<RedocWrap className="redoc-wrap">
|
|
||||||
<MenuContent className="menu-content">
|
|
||||||
<ApiLogo />
|
|
||||||
<SideMenu />
|
|
||||||
</MenuContent>
|
|
||||||
<Background className="background-wrap">
|
|
||||||
<div className="redoc-background" />
|
|
||||||
</Background>
|
|
||||||
<ApiContent className="api-content">
|
|
||||||
<ApiInfoContainer />
|
|
||||||
<ContentContainer />
|
|
||||||
</ApiContent>
|
|
||||||
</RedocWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,7 @@
|
||||||
import * as PropTypes from 'prop-types';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Children } from 'react';
|
import { Children } from 'react';
|
||||||
import { getContext } from 'recompose';
|
|
||||||
import { observer } from 'mobx-react';
|
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
import { AppStore } from '../../services';
|
|
||||||
import { Spinner } from './Spinner.svg';
|
import { Spinner } from './Spinner.svg';
|
||||||
|
|
||||||
const LoadingMessage = styled.div`
|
const LoadingMessage = styled.div`
|
||||||
|
@ -17,10 +13,9 @@ const LoadingMessage = styled.div`
|
||||||
color: ${props => props.theme.colors.main};
|
color: ${props => props.theme.colors.main};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@observer
|
export class LoadingWrap extends React.Component<{ loading: boolean }> {
|
||||||
class LoadingWrap extends React.Component<{ store: AppStore }> {
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.store.spec.loaded) {
|
if (this.props.loading) {
|
||||||
return Children.only(this.props.children);
|
return Children.only(this.props.children);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@ -31,7 +26,3 @@ class LoadingWrap extends React.Component<{ store: AppStore }> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getContext<{ store: AppStore }>({
|
|
||||||
store: PropTypes.object,
|
|
||||||
})(LoadingWrap);
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import styled from '../../styled-components';
|
||||||
|
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
import { H2, MiddlePanel, RightPanel, Badge } from '../../common-elements';
|
import { H2, MiddlePanel, DarkRightPanel, Badge, Row } from '../../common-elements';
|
||||||
|
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { Parameters } from '../Parameters/Parameters';
|
import { Parameters } from '../Parameters/Parameters';
|
||||||
|
@ -15,14 +15,19 @@ import { Endpoint } from '../Endpoint/Endpoint';
|
||||||
|
|
||||||
import { OperationModel as OperationType } from '../../services/models';
|
import { OperationModel as OperationType } from '../../services/models';
|
||||||
|
|
||||||
const OperationRow = styled.div`
|
const OperationRow = styled(Row)`
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
|
|
||||||
margin-bottom: 30px;
|
|
||||||
padding-bottom: 30px;
|
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
display: flex;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
positioin: relative;
|
positioin: relative;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
content: '';
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface OperationProps {
|
interface OperationProps {
|
||||||
|
@ -47,11 +52,11 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
||||||
<ResponsesList responses={operation.responses} />
|
<ResponsesList responses={operation.responses} />
|
||||||
</MiddlePanel>
|
</MiddlePanel>
|
||||||
<RightPanel>
|
<DarkRightPanel>
|
||||||
<Endpoint operation={operation} />
|
<Endpoint operation={operation} />
|
||||||
<RequestSamples operation={operation} />
|
<RequestSamples operation={operation} />
|
||||||
<ResponseSamples operation={operation} />
|
<ResponseSamples operation={operation} />
|
||||||
</RightPanel>
|
</DarkRightPanel>
|
||||||
</OperationRow>
|
</OperationRow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
import { ContentRoot } from './ContentRoot/ContentRoot';
|
|
||||||
|
|
||||||
import { ThemeProvider } from '../styled-components';
|
|
||||||
import defaultTheme from '../theme';
|
|
||||||
|
|
||||||
import LoadingWrap from './LoadingWrap/LoadingWrap';
|
|
||||||
import { StoreProvider } from './StoreProvider';
|
|
||||||
import { ErrorBoundary } from './ErrorBoundary';
|
|
||||||
|
|
||||||
export interface RedocProps {
|
|
||||||
specUrl?: string;
|
|
||||||
spec?: object;
|
|
||||||
theme?: any;
|
|
||||||
store?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Redoc extends React.Component<RedocProps> {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<ErrorBoundary>
|
|
||||||
<StoreProvider {...this.props}>
|
|
||||||
<ThemeProvider theme={this.props.theme || defaultTheme}>
|
|
||||||
<LoadingWrap>
|
|
||||||
<ContentRoot />
|
|
||||||
</LoadingWrap>
|
|
||||||
</ThemeProvider>
|
|
||||||
</StoreProvider>
|
|
||||||
</ErrorBoundary>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
44
src/components/Redoc/Redoc.tsx
Normal file
44
src/components/Redoc/Redoc.tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { ThemeInterface } from '../../theme';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { ThemeProvider } from '../../styled-components';
|
||||||
|
|
||||||
|
import { ApiInfo } from '../ApiInfo/ApiInfo';
|
||||||
|
import { RedocWrap, MenuContent, ApiContent } from './elements';
|
||||||
|
import { ApiLogo } from '../ApiLogo/ApiLogo';
|
||||||
|
import { SideMenu } from '../SideMenu/SideMenu';
|
||||||
|
import { ContentItems } from '../ContentItems/ContentItems';
|
||||||
|
import { AppStore } from '../../services';
|
||||||
|
|
||||||
|
import defaultTheme from '../../theme';
|
||||||
|
|
||||||
|
interface RedocProps {
|
||||||
|
store: AppStore;
|
||||||
|
options?: {
|
||||||
|
theme?: ThemeInterface;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Redoc extends React.Component<RedocProps> {
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.store.menu.updateOnHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { store: { spec, menu }, options = {} } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={options.theme || defaultTheme}>
|
||||||
|
<RedocWrap className="redoc-wrap">
|
||||||
|
<MenuContent className="menu-content">
|
||||||
|
<ApiLogo info={spec.info} />
|
||||||
|
<SideMenu menu={menu} />
|
||||||
|
</MenuContent>
|
||||||
|
<ApiContent className="api-content">
|
||||||
|
<ApiInfo info={spec.info} externalDocs={spec.externalDocs} />
|
||||||
|
<ContentItems items={menu.items as any} />;
|
||||||
|
</ApiContent>
|
||||||
|
</RedocWrap>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -49,21 +49,3 @@ export const ApiContent = styled.div`
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
position: relative;
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Background = styled.div`
|
|
||||||
position: absolute;
|
|
||||||
left: ${props => props.theme.menu.width};
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
.redoc-background {
|
|
||||||
background-color: ${props => props.theme.rightPanel.backgroundColor};
|
|
||||||
left: ${props => 100 - props.theme.rightPanel.width}%;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
`;
|
|
41
src/components/RedocStandalone.tsx
Normal file
41
src/components/RedocStandalone.tsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { ThemeInterface } from '../theme';
|
||||||
|
|
||||||
|
import { LoadingWrap } from './LoadingWrap/LoadingWrap';
|
||||||
|
import { StoreProvider } from './StoreProvider';
|
||||||
|
import { ErrorBoundary } from './ErrorBoundary';
|
||||||
|
import { Redoc } from './Redoc/Redoc';
|
||||||
|
|
||||||
|
export interface RedocProps {
|
||||||
|
specOrSpecUrl: string | object;
|
||||||
|
options?: {
|
||||||
|
theme?: ThemeInterface;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RedocStandalone extends React.Component<RedocProps> {
|
||||||
|
render() {
|
||||||
|
const { specOrSpecUrl, options } = this.props;
|
||||||
|
let specUrl;
|
||||||
|
let spec;
|
||||||
|
|
||||||
|
if (typeof specOrSpecUrl === 'string') {
|
||||||
|
specUrl = specOrSpecUrl;
|
||||||
|
} else if (typeof specOrSpecUrl === 'object') {
|
||||||
|
spec = specOrSpecUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<StoreProvider spec={spec} specUrl={specUrl}>
|
||||||
|
{({ loading, store }) => (
|
||||||
|
<LoadingWrap loading={loading}>
|
||||||
|
<Redoc store={store} options={options} />
|
||||||
|
</LoadingWrap>
|
||||||
|
)}
|
||||||
|
</StoreProvider>
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,15 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import * as PropTypes from 'prop-types';
|
|
||||||
import { getContext } from 'recompose';
|
|
||||||
|
|
||||||
import { BaseContainerProps } from '../../types/components';
|
import { MenuStore, IMenuItem } from '../../services/MenuStore';
|
||||||
import { IMenuItem } from '../../services/MenuStore';
|
|
||||||
|
|
||||||
import { PerfectScrollbar } from '../../common-elements/perfect-scrollbar';
|
|
||||||
import { MenuItems } from './MenuItems';
|
import { MenuItems } from './MenuItems';
|
||||||
|
|
||||||
|
import { PerfectScrollbar } from '../../common-elements/perfect-scrollbar';
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class SideMenu extends React.Component<BaseContainerProps> {
|
export class SideMenu extends React.Component<{ menu: MenuStore }> {
|
||||||
render() {
|
render() {
|
||||||
const store = this.props.store.menu;
|
const store = this.props.menu;
|
||||||
return (
|
return (
|
||||||
<PerfectScrollbar>
|
<PerfectScrollbar>
|
||||||
<MenuItems items={store.items} onActivate={this.activate} />
|
<MenuItems items={store.items} onActivate={this.activate} />
|
||||||
|
@ -21,10 +18,6 @@ class SideMenu extends React.Component<BaseContainerProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
activate = (item: IMenuItem) => {
|
activate = (item: IMenuItem) => {
|
||||||
this.props.store.menu.activateAndScroll(item, true);
|
this.props.menu.activateAndScroll(item, true);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getContext<BaseContainerProps>({
|
|
||||||
store: PropTypes.object,
|
|
||||||
})(SideMenu);
|
|
||||||
|
|
|
@ -1,34 +1,51 @@
|
||||||
import { Component, Children } from 'react';
|
import { Component } from 'react';
|
||||||
import * as PropTypes from 'prop-types';
|
import { AppStore } from '../services/';
|
||||||
import { AppStore, HistoryService } from '../services/';
|
import { loadSpec } from '../utils';
|
||||||
|
|
||||||
interface SpecProps {
|
interface SpecProps {
|
||||||
specUrl?: string;
|
specUrl?: string;
|
||||||
spec?: object;
|
spec?: object;
|
||||||
store?: AppStore;
|
store?: AppStore;
|
||||||
|
|
||||||
|
children?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StoreProvider extends Component<SpecProps, { error?: Error }> {
|
interface SpecState {
|
||||||
store: AppStore;
|
error?: Error;
|
||||||
|
loading: boolean;
|
||||||
|
store?: AppStore;
|
||||||
|
}
|
||||||
|
|
||||||
static childContextTypes = {
|
export class StoreProvider extends Component<SpecProps, SpecState> {
|
||||||
store: PropTypes.object.isRequired,
|
store: AppStore;
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: SpecProps) {
|
constructor(props: SpecProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
|
||||||
|
|
||||||
this.store = props.store || new AppStore();
|
this.state = {
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
|
||||||
if (!this.store.spec.loaded) {
|
this.load();
|
||||||
this.store.spec
|
}
|
||||||
.load(props.spec! || props.specUrl)
|
|
||||||
.then(() => {
|
async load() {
|
||||||
HistoryService.emit();
|
let { specUrl, spec } = this.props;
|
||||||
this.setError();
|
|
||||||
})
|
this.setState({
|
||||||
.catch(e => this.setError(e));
|
loading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resolvedSpec = await loadSpec(spec || specUrl!);
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
store: new AppStore(resolvedSpec, specUrl),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
this.setState({
|
||||||
|
error: e,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,12 +55,8 @@ export class StoreProvider extends Component<SpecProps, { error?: Error }> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildContext() {
|
|
||||||
return { store: this.props.store || this.store };
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.error) throw this.state.error;
|
if (this.state.error) throw this.state.error;
|
||||||
return Children.only(this.props.children);
|
return this.props.children(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,6 @@
|
||||||
export * from './Redoc';
|
export * from './RedocStandalone';
|
||||||
|
export * from './Redoc/Redoc';
|
||||||
|
export * from './Redoc/elements';
|
||||||
|
export * from './Schema/';
|
||||||
|
export * from './Operation/Operation';
|
||||||
|
// re-export the rest of components
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
|
import { AppContainer } from 'react-hot-loader';
|
||||||
// import DevTools from 'mobx-react-devtools';
|
// import DevTools from 'mobx-react-devtools';
|
||||||
|
|
||||||
import { AppContainer } from 'react-hot-loader';
|
import { Redoc } from './components/Redoc/Redoc';
|
||||||
import { Redoc, RedocProps } from './components/Redoc';
|
|
||||||
import { AppStore } from './services/AppStore';
|
import { AppStore } from './services/AppStore';
|
||||||
|
import { loadSpec } from './utils/loadSpec';
|
||||||
|
|
||||||
const renderRoot = (Component: typeof Redoc, props: RedocProps) =>
|
const renderRoot = (Component: typeof Redoc, props: { store: AppStore }) =>
|
||||||
render(
|
render(
|
||||||
<div>
|
<div>
|
||||||
<AppContainer>
|
<AppContainer>
|
||||||
|
@ -17,26 +18,32 @@ const renderRoot = (Component: typeof Redoc, props: RedocProps) =>
|
||||||
);
|
);
|
||||||
|
|
||||||
const big = window.location.search.indexOf('big') > -1;
|
const big = window.location.search.indexOf('big') > -1;
|
||||||
const props = {
|
|
||||||
specUrl: big ? 'big-swagger.json' : 'swagger.yaml',
|
|
||||||
store: new AppStore(),
|
|
||||||
};
|
|
||||||
|
|
||||||
renderRoot(Redoc, props);
|
const specUrl = big ? 'big-swagger.json' : 'swagger.yaml';
|
||||||
|
|
||||||
|
let store;
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
const spec = await loadSpec(specUrl);
|
||||||
|
store = new AppStore(spec, specUrl);
|
||||||
|
renderRoot(Redoc, { store: store });
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
const reload = (reloadStore = false) => () => {
|
const reload = (reloadStore = false) => () => {
|
||||||
if (reloadStore) {
|
if (reloadStore) {
|
||||||
// create a new Store
|
// create a new Store
|
||||||
props.store.dispose();
|
store.dispose();
|
||||||
|
|
||||||
const state = props.store.toJS();
|
const state = store.toJS();
|
||||||
props.store = AppStore.fromJS(state);
|
store = AppStore.fromJS(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderRoot(Redoc, props);
|
renderRoot(Redoc, { store: store });
|
||||||
};
|
};
|
||||||
|
|
||||||
module.hot.accept(['./components/Redoc'], reload());
|
module.hot.accept(['./components/Redoc/Redoc'], reload());
|
||||||
module.hot.accept(['./services/AppStore'], reload(true));
|
module.hot.accept(['./services/AppStore'], reload(true));
|
||||||
}
|
}
|
||||||
|
|
18
src/index.ts
18
src/index.ts
|
@ -1,2 +1,20 @@
|
||||||
|
import { render } from 'react-dom';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { RedocStandalone } from './components/Redoc';
|
||||||
|
|
||||||
export * from './components';
|
export * from './components';
|
||||||
export * from './services';
|
export * from './services';
|
||||||
|
|
||||||
|
export function init(specOrSpecUrl: string | any, options?: any, element?: Element) {
|
||||||
|
render(
|
||||||
|
React.createElement(
|
||||||
|
RedocStandalone,
|
||||||
|
{
|
||||||
|
specOrSpecUrl,
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
element || document.querySelector('redoc'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,18 +1,27 @@
|
||||||
|
import { OpenAPISpec } from '../types';
|
||||||
import { SpecStore } from './models';
|
import { SpecStore } from './models';
|
||||||
import { MenuStore } from './MenuStore';
|
import { MenuStore } from './MenuStore';
|
||||||
import { ScrollService } from './ScrollService';
|
import { ScrollService } from './ScrollService';
|
||||||
|
|
||||||
|
type StoreData = {
|
||||||
|
menu: {
|
||||||
|
activeItemIdx: number;
|
||||||
|
};
|
||||||
|
spec: {
|
||||||
|
url: string;
|
||||||
|
data: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export class AppStore {
|
export class AppStore {
|
||||||
menu: MenuStore;
|
menu: MenuStore;
|
||||||
scroll: ScrollService;
|
|
||||||
spec: SpecStore;
|
spec: SpecStore;
|
||||||
static i = 25;
|
|
||||||
|
|
||||||
// TODO: store serialization ???
|
private scroll: ScrollService;
|
||||||
|
|
||||||
constructor() {
|
constructor(spec: OpenAPISpec, specUrl?: string) {
|
||||||
this.scroll = new ScrollService();
|
this.scroll = new ScrollService();
|
||||||
this.spec = new SpecStore();
|
this.spec = new SpecStore(spec, specUrl);
|
||||||
this.menu = new MenuStore(this.spec, this.scroll);
|
this.menu = new MenuStore(this.spec, this.scroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,16 +35,14 @@ export class AppStore {
|
||||||
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
|
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
|
||||||
*/
|
*/
|
||||||
// TODO:
|
// TODO:
|
||||||
toJS() {
|
toJS(): StoreData {
|
||||||
return {
|
return {
|
||||||
menu: {
|
menu: {
|
||||||
activeMenuIdx: this.menu.activeItemIdx,
|
activeItemIdx: this.menu.activeItemIdx,
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
parser: {
|
url: this.spec.parser.specUrl,
|
||||||
specUrl: this.spec.parser.specUrl,
|
data: this.spec.parser.spec,
|
||||||
spec: this.spec.parser.spec,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -44,10 +51,8 @@ export class AppStore {
|
||||||
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
|
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
|
||||||
*/
|
*/
|
||||||
// TODO:
|
// TODO:
|
||||||
static fromJS(state): AppStore {
|
static fromJS(state: StoreData): AppStore {
|
||||||
const inst = new AppStore();
|
const inst = new AppStore(state.spec.data, state.spec.url);
|
||||||
inst.spec.parser.specUrl = state.spec.parser.specUrl;
|
|
||||||
inst.spec.parser.spec = state.spec.parser.spec;
|
|
||||||
inst.menu.activeItemIdx = state.menu.activeItemIdx || 0;
|
inst.menu.activeItemIdx = state.menu.activeItemIdx || 0;
|
||||||
inst.menu.activate(inst.menu.flatItems[inst.menu.activeItemIdx]);
|
inst.menu.activate(inst.menu.flatItems[inst.menu.activeItemIdx]);
|
||||||
return inst;
|
return inst;
|
||||||
|
|
|
@ -30,7 +30,7 @@ export class MenuBuilder {
|
||||||
* Builds page content structure based on tags
|
* Builds page content structure based on tags
|
||||||
*/
|
*/
|
||||||
static buildStructure(parser: OpenAPIParser): ContentItemModel[] {
|
static buildStructure(parser: OpenAPIParser): ContentItemModel[] {
|
||||||
const spec = parser.spec!;
|
const spec = parser.spec;
|
||||||
|
|
||||||
const items: ContentItemModel[] = [];
|
const items: ContentItemModel[] = [];
|
||||||
const tagsMap = MenuBuilder.getTagsWithOperations(spec);
|
const tagsMap = MenuBuilder.getTagsWithOperations(spec);
|
||||||
|
|
|
@ -102,7 +102,7 @@ export class MenuStore {
|
||||||
* @param hash current hash
|
* @param hash current hash
|
||||||
*/
|
*/
|
||||||
@action.bound
|
@action.bound
|
||||||
updateOnHash(hash: string): boolean {
|
updateOnHash(hash: string = HistoryService.hash): boolean {
|
||||||
if (!hash) return false;
|
if (!hash) return false;
|
||||||
let item: IMenuItem | undefined;
|
let item: IMenuItem | undefined;
|
||||||
hash = hash.substr(1);
|
hash = hash.substr(1);
|
||||||
|
@ -155,9 +155,8 @@ export class MenuStore {
|
||||||
get flatItems(): IMenuItem[] {
|
get flatItems(): IMenuItem[] {
|
||||||
if (!this._flatItems) {
|
if (!this._flatItems) {
|
||||||
this._flatItems = flattenByProp(this.items, 'items');
|
this._flatItems = flattenByProp(this.items, 'items');
|
||||||
}
|
|
||||||
|
|
||||||
this._flatItems.forEach((item, idx) => (item.absoluteIdx = idx));
|
this._flatItems.forEach((item, idx) => (item.absoluteIdx = idx));
|
||||||
|
}
|
||||||
|
|
||||||
return this._flatItems;
|
return this._flatItems;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { action, computed, observable } from 'mobx';
|
import { observable } from 'mobx';
|
||||||
import * as JsonSchemaRefParser from 'json-schema-ref-parser';
|
|
||||||
import { resolve as urlResolve } from 'url';
|
import { resolve as urlResolve } from 'url';
|
||||||
|
|
||||||
import { OpenAPIRef, OpenAPISchema, OpenAPISpec, Referenced } from '../types';
|
import { OpenAPIRef, OpenAPISchema, OpenAPISpec, Referenced } from '../types';
|
||||||
|
@ -38,44 +37,21 @@ class RefCounter {
|
||||||
*/
|
*/
|
||||||
export class OpenAPIParser {
|
export class OpenAPIParser {
|
||||||
@observable specUrl: string;
|
@observable specUrl: string;
|
||||||
@observable.ref spec?: OpenAPISpec;
|
@observable.ref spec: OpenAPISpec;
|
||||||
|
|
||||||
private _parser: JsonSchemaRefParser;
|
|
||||||
private _refCounter: RefCounter = new RefCounter();
|
|
||||||
|
|
||||||
@computed
|
|
||||||
get loaded(): boolean {
|
|
||||||
return this.spec !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* loads and bundles the spec via url to spec or by providing spec itself.
|
|
||||||
* Async as bundling is async as spec may contain extrenal refs.
|
|
||||||
* @param urlOrObject url to the spec or the spec itself
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
async load(urlOrObject: string | object): Promise<OpenAPISpec> {
|
|
||||||
if (this.loaded) {
|
|
||||||
return this.spec!;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._parser = new JsonSchemaRefParser();
|
|
||||||
if (typeof urlOrObject === 'string') {
|
|
||||||
this.specUrl = urlResolve(window.location.href, urlOrObject);
|
|
||||||
} else {
|
|
||||||
this.specUrl = window.location.href;
|
|
||||||
}
|
|
||||||
|
|
||||||
const spec = await this._parser.bundle(urlOrObject, {
|
|
||||||
resolve: { http: { withCredentials: false } },
|
|
||||||
} as object);
|
|
||||||
|
|
||||||
|
constructor(spec: OpenAPISpec, specUrl?: string) {
|
||||||
this.validate(spec);
|
this.validate(spec);
|
||||||
|
|
||||||
this.spec = spec;
|
this.spec = spec;
|
||||||
|
|
||||||
return this.spec!;
|
if (typeof specUrl === 'string') {
|
||||||
|
this.specUrl = urlResolve(window.location.href, specUrl);
|
||||||
|
} else {
|
||||||
|
this.specUrl = window.location.href;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _refCounter: RefCounter = new RefCounter();
|
||||||
|
|
||||||
validate(spec: any) {
|
validate(spec: any) {
|
||||||
// TODO: validate
|
// TODO: validate
|
||||||
|
@ -90,17 +66,12 @@ export class OpenAPIParser {
|
||||||
byRef = <T extends any = any>(ref: string): T | undefined => {
|
byRef = <T extends any = any>(ref: string): T | undefined => {
|
||||||
let res;
|
let res;
|
||||||
if (this.spec === undefined) return;
|
if (this.spec === undefined) return;
|
||||||
|
if (ref.charAt(0) !== '#') ref = '#' + ref;
|
||||||
try {
|
try {
|
||||||
res = JsonPointer.get(this.spec, decodeURIComponent(ref));
|
res = JsonPointer.get(this.spec, decodeURIComponent(ref));
|
||||||
} catch (e) {
|
|
||||||
// if resolved from outer files simple jsonpointer.get fails to get correct schema
|
|
||||||
if (ref.charAt(0) !== '#') ref = '#' + ref;
|
|
||||||
try {
|
|
||||||
res = this._parser.$refs.get(decodeURIComponent(ref));
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -227,7 +198,7 @@ export class OpenAPIParser {
|
||||||
*/
|
*/
|
||||||
findDerived($refs: string[]): Dict<string> {
|
findDerived($refs: string[]): Dict<string> {
|
||||||
const res: Dict<string> = {};
|
const res: Dict<string> = {};
|
||||||
const schemas = (this.spec!.components && this.spec!.components!.schemas) || {};
|
const schemas = (this.spec.components && this.spec.components.schemas) || {};
|
||||||
for (let defName in schemas) {
|
for (let defName in schemas) {
|
||||||
const def = this.deref(schemas[defName]);
|
const def = this.deref(schemas[defName]);
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { OpenAPISpec } from '../types';
|
||||||
import { observable, computed } from 'mobx';
|
import { observable, computed } from 'mobx';
|
||||||
|
|
||||||
// import { OpenAPIExternalDocumentation, OpenAPIInfo } from '../types';
|
// import { OpenAPIExternalDocumentation, OpenAPIInfo } from '../types';
|
||||||
|
@ -12,34 +13,22 @@ import { ApiInfoModel } from './models/ApiInfo';
|
||||||
export class SpecStore {
|
export class SpecStore {
|
||||||
@observable.ref parser: OpenAPIParser;
|
@observable.ref parser: OpenAPIParser;
|
||||||
|
|
||||||
constructor() {
|
constructor(spec: OpenAPISpec, specUrl?: string) {
|
||||||
this.parser = new OpenAPIParser();
|
this.parser = new OpenAPIParser(spec, specUrl);
|
||||||
}
|
|
||||||
|
|
||||||
load(specOrUrl: string | object) {
|
|
||||||
return this.parser.load(specOrUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
get loaded() {
|
get info(): ApiInfoModel {
|
||||||
return this.parser.loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed
|
|
||||||
get info() {
|
|
||||||
if (!this.parser.loaded) return;
|
|
||||||
return new ApiInfoModel(this.parser);
|
return new ApiInfoModel(this.parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
get externalDocs() {
|
get externalDocs() {
|
||||||
if (this.parser.loaded) return;
|
return this.parser.spec.externalDocs;
|
||||||
return this.parser.spec!.externalDocs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
get operationGroups() {
|
get operationGroups() {
|
||||||
if (!this.parser.loaded) return [];
|
|
||||||
return MenuBuilder.buildStructure(this.parser);
|
return MenuBuilder.buildStructure(this.parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ export class ApiInfoModel implements OpenAPIInfo {
|
||||||
license?: OpenAPILicense;
|
license?: OpenAPILicense;
|
||||||
|
|
||||||
constructor(public parser: OpenAPIParser) {
|
constructor(public parser: OpenAPIParser) {
|
||||||
Object.assign(this, parser.spec!.info);
|
Object.assign(this, parser.spec.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
get downloadLink() {
|
get downloadLink() {
|
||||||
|
|
|
@ -79,13 +79,15 @@ export class OperationModel implements IMenuItem {
|
||||||
if (parseInt(code) >= 100 && parseInt(code) <= 399) {
|
if (parseInt(code) >= 100 && parseInt(code) <= 399) {
|
||||||
hasSuccessResponses = true;
|
hasSuccessResponses = true;
|
||||||
}
|
}
|
||||||
return isNumeric(code) || code === 'default'
|
return isNumeric(code) || code === 'default';
|
||||||
}) // filter out other props (e.g. x-props)
|
}) // filter out other props (e.g. x-props)
|
||||||
.map(code => new ResponseModel(parser, code, hasSuccessResponses, operationSpec.responses[code]));
|
.map(
|
||||||
|
code => new ResponseModel(parser, code, hasSuccessResponses, operationSpec.responses[code]),
|
||||||
|
);
|
||||||
|
|
||||||
this.servers = normalizeServers(
|
this.servers = normalizeServers(
|
||||||
parser.specUrl,
|
parser.specUrl,
|
||||||
operationSpec.servers || parser.spec!.servers || [],
|
operationSpec.servers || parser.spec.servers || [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,3 +4,4 @@ export * from './styled';
|
||||||
export * from './openapi';
|
export * from './openapi';
|
||||||
export * from './helpers';
|
export * from './helpers';
|
||||||
export * from './highlight';
|
export * from './highlight';
|
||||||
|
export * from './loadSpec';
|
||||||
|
|
10
src/utils/loadSpec.ts
Normal file
10
src/utils/loadSpec.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { OpenAPISpec } from '../types';
|
||||||
|
|
||||||
|
import * as JsonSchemaRefParser from 'json-schema-ref-parser';
|
||||||
|
|
||||||
|
export async function loadSpec(specUrlOrObject: object | string): Promise<OpenAPISpec> {
|
||||||
|
const _parser = new JsonSchemaRefParser();
|
||||||
|
return await _parser.bundle(specUrlOrObject, {
|
||||||
|
resolve: { http: { withCredentials: false } },
|
||||||
|
} as object);
|
||||||
|
}
|
19
yarn.lock
19
yarn.lock
|
@ -1036,10 +1036,6 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0:
|
||||||
escape-string-regexp "^1.0.5"
|
escape-string-regexp "^1.0.5"
|
||||||
supports-color "^4.0.0"
|
supports-color "^4.0.0"
|
||||||
|
|
||||||
change-emitter@^0.1.2:
|
|
||||||
version "0.1.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515"
|
|
||||||
|
|
||||||
cheerio@^1.0.0-rc.2:
|
cheerio@^1.0.0-rc.2:
|
||||||
version "1.0.0-rc.2"
|
version "1.0.0-rc.2"
|
||||||
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db"
|
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db"
|
||||||
|
@ -2431,7 +2427,7 @@ fb-watchman@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
bser "^2.0.0"
|
bser "^2.0.0"
|
||||||
|
|
||||||
fbjs@^0.8.1, fbjs@^0.8.16, fbjs@^0.8.5, fbjs@^0.8.9:
|
fbjs@^0.8.16, fbjs@^0.8.5, fbjs@^0.8.9:
|
||||||
version "0.8.16"
|
version "0.8.16"
|
||||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
|
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5927,15 +5923,6 @@ readdirp@^2.0.0:
|
||||||
readable-stream "^2.0.2"
|
readable-stream "^2.0.2"
|
||||||
set-immediate-shim "^1.0.1"
|
set-immediate-shim "^1.0.1"
|
||||||
|
|
||||||
recompose@^0.25.1:
|
|
||||||
version "0.25.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.25.1.tgz#5eb9d6cf6e25a9ffad73cbbae5658b5b55d6e728"
|
|
||||||
dependencies:
|
|
||||||
change-emitter "^0.1.2"
|
|
||||||
fbjs "^0.8.1"
|
|
||||||
hoist-non-react-statics "^2.3.1"
|
|
||||||
symbol-observable "^1.0.4"
|
|
||||||
|
|
||||||
recursive-readdir@2.2.1:
|
recursive-readdir@2.2.1:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99"
|
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99"
|
||||||
|
@ -6835,10 +6822,6 @@ svgo@^0.7.0:
|
||||||
sax "~1.2.1"
|
sax "~1.2.1"
|
||||||
whet.extend "~0.9.9"
|
whet.extend "~0.9.9"
|
||||||
|
|
||||||
symbol-observable@^1.0.4:
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
|
|
||||||
|
|
||||||
symbol-tree@^3.2.1:
|
symbol-tree@^3.2.1:
|
||||||
version "3.2.2"
|
version "3.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
|
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user