mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-07 21:54:53 +03:00
feat: Add bundles
This commit is contained in:
parent
49f04d321f
commit
422ec09771
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -24,7 +24,7 @@ node_modules
|
||||||
lib/
|
lib/
|
||||||
stats.json
|
stats.json
|
||||||
cypress/
|
cypress/
|
||||||
bundles/
|
# bundles/
|
||||||
typings/*
|
typings/*
|
||||||
!typings/styled-patch.d.ts
|
!typings/styled-patch.d.ts
|
||||||
cli/index.js
|
cli/index.js
|
||||||
|
|
1
bundles/6ab91c58bf337b7164c3.worker.js.map
Normal file
1
bundles/6ab91c58bf337b7164c3.worker.js.map
Normal file
File diff suppressed because one or more lines are too long
1
bundles/95952f773ec942db46dc.worker.js.map
Normal file
1
bundles/95952f773ec942db46dc.worker.js.map
Normal file
File diff suppressed because one or more lines are too long
14845
bundles/redoc.lib.js
Normal file
14845
bundles/redoc.lib.js
Normal file
File diff suppressed because one or more lines are too long
1
bundles/redoc.lib.js.map
Normal file
1
bundles/redoc.lib.js.map
Normal file
File diff suppressed because one or more lines are too long
110
bundles/redoc.standalone.js
Normal file
110
bundles/redoc.standalone.js
Normal file
File diff suppressed because one or more lines are too long
1
bundles/redoc.standalone.js.map
Normal file
1
bundles/redoc.standalone.js.map
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import styled from 'styled-components';
|
import styled from '../src/styled-components';
|
||||||
import { resolve as urlResolve } from 'url';
|
import { resolve as urlResolve } from 'url';
|
||||||
import { RedocStandalone } from '../src';
|
import { RedocStandalone } from '../src';
|
||||||
import ComboBox from './ComboBox';
|
import ComboBox from './ComboBox';
|
||||||
|
|
|
@ -30,14 +30,6 @@ info:
|
||||||
And that allows cross-domain communication from the browser.
|
And that allows cross-domain communication from the browser.
|
||||||
All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site.
|
All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site.
|
||||||
|
|
||||||
# Authentication
|
|
||||||
|
|
||||||
Petstore offers two forms of authentication:
|
|
||||||
- API Key
|
|
||||||
- OAuth2
|
|
||||||
OAuth2 - an open protocol to allow secure authorization in a simple
|
|
||||||
and standard method from web, mobile and desktop applications.
|
|
||||||
|
|
||||||
<SecurityDefinitions />
|
<SecurityDefinitions />
|
||||||
|
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { Redoc, RedocProps } from '../../src/components/Redoc/Redoc';
|
||||||
import { AppStore } from '../../src/services/AppStore';
|
import { AppStore } from '../../src/services/AppStore';
|
||||||
import { RedocRawOptions } from '../../src/services/RedocNormalizedOptions';
|
import { RedocRawOptions } from '../../src/services/RedocNormalizedOptions';
|
||||||
import { loadAndBundleSpec } from '../../src/utils/loadAndBundleSpec';
|
import { loadAndBundleSpec } from '../../src/utils/loadAndBundleSpec';
|
||||||
|
import markdown from './markdown';
|
||||||
|
|
||||||
const renderRoot = (props: RedocProps) =>
|
const renderRoot = (props: RedocProps) =>
|
||||||
render(
|
render(
|
||||||
|
@ -32,11 +33,12 @@ const options: RedocRawOptions = {
|
||||||
hideShelfIcon: true,
|
hideShelfIcon: true,
|
||||||
maxDisplayedEnumValues: 3,
|
maxDisplayedEnumValues: 3,
|
||||||
nativeScrollbars: false,
|
nativeScrollbars: false,
|
||||||
|
noAutoAuth: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const spec = await loadAndBundleSpec(specUrl);
|
const spec = await loadAndBundleSpec(specUrl);
|
||||||
store = new AppStore(spec, specUrl, options);
|
store = new AppStore(spec, specUrl, options, true, markdown);
|
||||||
renderRoot({ store });
|
renderRoot({ store });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { HFlex } from '../common-elements/flex';
|
import { HFlex } from '../../../src/common-elements/flex';
|
||||||
import styled from 'styled-components';
|
import styled from '../../../src/styled-components';
|
||||||
|
|
||||||
const Button = styled.button`
|
const Button = styled.button`
|
||||||
margin-left: auto;
|
margin-left: auto;
|
|
@ -4,17 +4,17 @@
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
BSDEX_API_KEY=put-your-api-key-here
|
YOUR_API_KEY=put-your-api-key-here
|
||||||
BSDEX_API_SECRET=put-your-api-secret-here
|
YOUR_SECRET=put-your-api-secret-here
|
||||||
DATE="`date -u '+%a, %d %b %Y %T %Z'`"
|
DATE="`date -u '+%a, %d %b %Y %T %Z'`"
|
||||||
# The signature is in fact HMAC signature of a few fields, currently only "date", using your API secret.
|
# The signature is in fact HMAC signature of a few fields, currently only "date", using your API secret.
|
||||||
AUTHORIZATION="hmac username=\"$BSDEX_API_KEY\", algorithm=\"hmac-sha1\", headers=\"date\", signature=\"`/bin/echo -n "date: ${DATE}" | openssl sha1 -binary -hmac "${BSDEX_API_SECRET}" | base64 `\""
|
AUTHORIZATION="hmac username=\"$YOUR_API_KEY\", algorithm=\"hmac-sha1\", headers=\"date\", signature=\"`/bin/echo -n "date: ${DATE}" | openssl sha1 -binary -hmac "${YOUR_API_SECRET}" | base64 `\""
|
||||||
|
|
||||||
curl -v \
|
curl -v \
|
||||||
-H "Date: $DATE" \
|
-H "Date: $DATE" \
|
||||||
-H "ApiKey: $BSDEX_API_KEY" \
|
-H "ApiKey: $YOUR_API_KEY" \
|
||||||
-H "Authorization: $AUTHORIZATION" \
|
-H "Authorization: $AUTHORIZATION" \
|
||||||
-X GET "https://api-public.prelive.cex.tribe.sh/api/v1/balance"
|
-X GET "https://example.com/api/v1/pets"
|
||||||
```
|
```
|
||||||
|
|
||||||
> And you'll receive a response such as:
|
> And you'll receive a response such as:
|
||||||
|
@ -22,14 +22,10 @@ curl -v \
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"asset_id": "btc",
|
"foo": "bar"
|
||||||
"available": "0",
|
|
||||||
"locked": "0"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"asset_id": "eur",
|
"bar": "foo"
|
||||||
"available": "34163",
|
|
||||||
"locked": "123"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -49,7 +45,7 @@ where `signed_date` is the base64-encoded HMAC-SHA1 signature for the expression
|
||||||
|
|
||||||
Have a look at the code example to understand the HMAC signing process better.
|
Have a look at the code example to understand the HMAC signing process better.
|
||||||
|
|
||||||
<aside class="notice">
|
<aside className="notice">
|
||||||
It is important to note that the value of the <em>Date</em> header must match the signed date value.
|
It is important to note that the value of the <em>Date</em> header must match the signed date value.
|
||||||
</aside>
|
</aside>
|
||||||
|
|
15
demo/playground/markdown/index.ts
Normal file
15
demo/playground/markdown/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { loadMarkdownIndexFromComponents } from '../../../src/markdown';
|
||||||
|
|
||||||
|
export const loadMarkdownIndex = (names) => {
|
||||||
|
const mdxComponents = names.map((include) => require(`./${include}.mdx`));
|
||||||
|
|
||||||
|
return loadMarkdownIndexFromComponents(mdxComponents)
|
||||||
|
};
|
||||||
|
|
||||||
|
const NAMES = [
|
||||||
|
'welcome',
|
||||||
|
'auth',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
export default loadMarkdownIndex(NAMES)
|
3
demo/playground/markdown/manifest.json
Normal file
3
demo/playground/markdown/manifest.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"names": ["welcome", "auth"]
|
||||||
|
}
|
|
@ -26,14 +26,7 @@ info:
|
||||||
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
|
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
|
||||||
And that allows cross-domain communication from the browser.
|
And that allows cross-domain communication from the browser.
|
||||||
All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site.
|
All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site.
|
||||||
# Authentication
|
|
||||||
Petstore offers two forms of authentication:
|
|
||||||
- API Key
|
|
||||||
- OAuth2
|
|
||||||
|
|
||||||
OAuth2 - an open protocol to allow secure authorization in a simple
|
|
||||||
and standard method from web, mobile and desktop applications.
|
|
||||||
<!-- ReDoc-Inject: <security-definitions> -->
|
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
title: Swagger Petstore
|
title: Swagger Petstore
|
||||||
termsOfService: 'http://swagger.io/terms/'
|
termsOfService: 'http://swagger.io/terms/'
|
||||||
|
|
|
@ -28,7 +28,7 @@ const tsLoader = (env) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const babelLoader = (mode) => ({
|
const babelLoader = (mode, transformJsx = false) => ({
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
generatorOpts: {
|
generatorOpts: {
|
||||||
|
@ -38,7 +38,7 @@ const babelLoader = (mode) => ({
|
||||||
['@babel/plugin-syntax-typescript', { isTSX: true }],
|
['@babel/plugin-syntax-typescript', { isTSX: true }],
|
||||||
['@babel/plugin-syntax-decorators', { legacy: true }],
|
['@babel/plugin-syntax-decorators', { legacy: true }],
|
||||||
'@babel/plugin-syntax-dynamic-import',
|
'@babel/plugin-syntax-dynamic-import',
|
||||||
'@babel/plugin-transform-react-jsx',
|
...(transformJsx ? ['@babel/plugin-transform-react-jsx'] : []),
|
||||||
'@babel/plugin-syntax-jsx',
|
'@babel/plugin-syntax-jsx',
|
||||||
[
|
[
|
||||||
'babel-plugin-styled-components',
|
'babel-plugin-styled-components',
|
||||||
|
@ -114,7 +114,7 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
{
|
{
|
||||||
test: /\.mdx?$/,
|
test: /\.mdx?$/,
|
||||||
use: [
|
use: [
|
||||||
babelLoader(mode),
|
babelLoader(mode, true),
|
||||||
{
|
{
|
||||||
loader: '@mdx-js/loader',
|
loader: '@mdx-js/loader',
|
||||||
options: {
|
options: {
|
||||||
|
|
22
package-lock.json
generated
22
package-lock.json
generated
|
@ -2366,9 +2366,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/react-native": {
|
"@types/react-native": {
|
||||||
"version": "0.63.2",
|
"version": "0.63.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.9.tgz",
|
||||||
"integrity": "sha512-oxbp084lUsZvwfdWmWxKjJAuqEraQDRf+cE/JgwmrHQMguSrmgIHZ3xkeoQ5FYnW5NHIPpHudB3BbjL1Zn3vnA==",
|
"integrity": "sha512-6ec/z9zjAkFH3rD1RYqbrA/Lj+jux6bumWCte4yRy3leyelTdqtmOd2Ph+86IXQQzsIArEMBwmraAbNQ0J3UAA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
|
@ -2408,15 +2408,23 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/styled-components": {
|
"@types/styled-components": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.2.tgz",
|
||||||
"integrity": "sha512-fIjKvDU1LJExBZWEQilHqzfpOK4KUwBsj5zC79lxa94ekz8oDQSBNcayMACBImxIuevF+NbBGL9O/2CQ67Zhig==",
|
"integrity": "sha512-HNocYLfrsnNNm8NTS/W53OERSjRA8dx5Bn6wBd2rXXwt4Z3s+oqvY6/PbVt3e6sgtzI63GX//WiWiRhWur08qQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/hoist-non-react-statics": "*",
|
"@types/hoist-non-react-statics": "*",
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"@types/react-native": "*",
|
"@types/react-native": "*",
|
||||||
"csstype": "^2.2.0"
|
"csstype": "^3.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"csstype": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/tapable": {
|
"@types/tapable": {
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import styled from 'styled-components'
|
import styled from '../styled-components';
|
||||||
|
|
||||||
export const Flex = styled.div`
|
type FlexProps = {
|
||||||
|
justifyContent?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Flex = styled.div.attrs((props: FlexProps) => ({
|
||||||
|
justifyContent: props.justifyContent,
|
||||||
|
}))<FlexProps>`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: ${(props) => props.justifyContent};
|
justify-content: ${(props) => props.justifyContent};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -9,3 +9,4 @@ export * from './mixins';
|
||||||
export * from './tabs';
|
export * from './tabs';
|
||||||
export * from './samples';
|
export * from './samples';
|
||||||
export * from './perfect-scrollbar';
|
export * from './perfect-scrollbar';
|
||||||
|
export * as Flex from './flex';
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import PerfectScrollbarType, * as PerfectScrollbarNamespace from 'perfect-scrollbar';
|
import PerfectScrollbarType, * as PerfectScrollbarNamespace from 'perfect-scrollbar';
|
||||||
import psStyles from 'perfect-scrollbar/css/perfect-scrollbar.css';
|
|
||||||
|
|
||||||
import { OptionsContext } from '../components/OptionsProvider';
|
import { OptionsContext } from '../components/OptionsProvider';
|
||||||
import styled, { createGlobalStyle } from '../styled-components';
|
import styled from '../styled-components';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* perfect scrollbar umd bundle uses exports assignment while module uses default export
|
* perfect scrollbar umd bundle uses exports assignment while module uses default export
|
||||||
|
@ -14,7 +13,7 @@ import styled, { createGlobalStyle } from '../styled-components';
|
||||||
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`${require('perfect-scrollbar/css/perfect-scrollbar.css').toString()}`;
|
||||||
|
|
||||||
const StyledScrollWrapper = styled.div`
|
const StyledScrollWrapper = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -46,7 +45,7 @@ export class PerfectScrollbar extends React.Component<PerfectScrollbarProps> {
|
||||||
this.inst.destroy();
|
this.inst.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRef = ref => {
|
handleRef = (ref) => {
|
||||||
this._container = ref;
|
this._container = ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,7 +58,7 @@ export class PerfectScrollbar extends React.Component<PerfectScrollbarProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PSStyling />
|
{/* <PSStyling /> */}
|
||||||
<StyledScrollWrapper className={`scrollbar-container ${className}`} ref={this.handleRef}>
|
<StyledScrollWrapper className={`scrollbar-container ${className}`} ref={this.handleRef}>
|
||||||
{children}
|
{children}
|
||||||
</StyledScrollWrapper>
|
</StyledScrollWrapper>
|
||||||
|
@ -73,7 +72,7 @@ export function PerfectScrollbarWrap(
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options =>
|
{(options) =>
|
||||||
!options.nativeScrollbars ? (
|
!options.nativeScrollbars ? (
|
||||||
<PerfectScrollbar {...props}>{props.children}</PerfectScrollbar>
|
<PerfectScrollbar {...props}>{props.children}</PerfectScrollbar>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -15,8 +15,7 @@ import { ApiContentWrap, BackgroundStub, RedocWrap } from './styled.elements';
|
||||||
|
|
||||||
import { SearchBox } from '../SearchBox/SearchBox';
|
import { SearchBox } from '../SearchBox/SearchBox';
|
||||||
import { StoreProvider } from '../StoreBuilder';
|
import { StoreProvider } from '../StoreBuilder';
|
||||||
|
import components from '../../markdown/components';
|
||||||
import { sections, components } from '../../markdown';
|
|
||||||
|
|
||||||
export interface RedocProps {
|
export interface RedocProps {
|
||||||
store: AppStore;
|
store: AppStore;
|
||||||
|
@ -37,9 +36,12 @@ export class Redoc extends React.Component<RedocProps> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
store: { spec, menu, options, search, marker },
|
store: { spec, menu, options, search, marker, markdownIndex },
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const store = this.props.store;
|
const store = this.props.store;
|
||||||
|
|
||||||
|
console.log('components', components);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={options.theme}>
|
<ThemeProvider theme={options.theme}>
|
||||||
<StoreProvider value={this.props.store}>
|
<StoreProvider value={this.props.store}>
|
||||||
|
@ -61,8 +63,9 @@ export class Redoc extends React.Component<RedocProps> {
|
||||||
</StickyResponsiveSidebar>
|
</StickyResponsiveSidebar>
|
||||||
<ApiContentWrap className="api-content">
|
<ApiContentWrap className="api-content">
|
||||||
<ApiInfo store={store} />
|
<ApiInfo store={store} />
|
||||||
{sections.map((MDXComponent, idx) => {
|
{markdownIndex &&
|
||||||
return <MDXComponent key={`section-${idx}`} />;
|
markdownIndex.components.map((MDXComponent, idx) => {
|
||||||
|
return <MDXComponent key={`mdxsection-${idx}`} />;
|
||||||
})}
|
})}
|
||||||
<ContentItems items={menu.items as any} />
|
<ContentItems items={menu.items as any} />
|
||||||
</ApiContentWrap>
|
</ApiContentWrap>
|
||||||
|
|
|
@ -6,11 +6,13 @@ import { ErrorBoundary } from './ErrorBoundary';
|
||||||
import { Loading } from './Loading/Loading';
|
import { Loading } from './Loading/Loading';
|
||||||
import { Redoc } from './Redoc/Redoc';
|
import { Redoc } from './Redoc/Redoc';
|
||||||
import { StoreBuilder } from './StoreBuilder';
|
import { StoreBuilder } from './StoreBuilder';
|
||||||
|
import { MarkdownIndex } from '../markdown';
|
||||||
|
|
||||||
export interface RedocStandaloneProps {
|
export interface RedocStandaloneProps {
|
||||||
spec?: object;
|
spec?: object;
|
||||||
specUrl?: string;
|
specUrl?: string;
|
||||||
options?: RedocRawOptions;
|
options?: RedocRawOptions;
|
||||||
|
markdownIndex?: MarkdownIndex;
|
||||||
onLoaded?: (e?: Error) => any;
|
onLoaded?: (e?: Error) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ export class SelectOnClick extends React.PureComponent {
|
||||||
const { children } = this.props;
|
const { children } = this.props;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={el => (this.child = el)}
|
ref={el => (this.child = (el as HTMLDivElement))}
|
||||||
onClick={this.selectElement}
|
onClick={this.selectElement}
|
||||||
onFocus={this.selectElement}
|
onFocus={this.selectElement}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
export * from './components';
|
export * from './components';
|
||||||
export { MiddlePanel, Row, RightPanel, Section } from './common-elements/';
|
export { MiddlePanel, Row, RightPanel, Section, Flex } from './common-elements/';
|
||||||
export { OpenAPIEncoding } from './types';
|
export { OpenAPIEncoding } from './types';
|
||||||
export * from './services';
|
export * from './services';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
|
||||||
export * from './styled-components';
|
export * from './styled-components';
|
||||||
export { default as styled } from './styled-components';
|
export { default as styled } from './styled-components';
|
||||||
|
export * from './markdown';
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Highlight, { defaultProps, Language } from 'prism-react-renderer';
|
import Highlight, { defaultProps, Language } from 'prism-react-renderer';
|
||||||
import styled from 'styled-components';
|
import styled from '../../../styled-components';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className: string;
|
className: string;
|
||||||
|
style: any;
|
||||||
|
componentId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const OverflowHighlighter = styled.pre`
|
const OverflowHighlighter = styled.pre`
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Highligher: React.FC<Props> = ({ children, className }) => {
|
const Highlighter: React.FC<Props> = ({ children, className }) => {
|
||||||
const language = className.replace(/language-/, '');
|
console.log('className', className);
|
||||||
|
const language = className ? className.replace(/language-/, '').trim() : 'bash';
|
||||||
|
|
||||||
if (!children) {
|
if (!children) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -34,4 +37,4 @@ const Highligher: React.FC<Props> = ({ children, className }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Highligher;
|
export default Highlighter;
|
||||||
|
|
89
src/markdown/components.tsx
Normal file
89
src/markdown/components.tsx
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
DarkRightPanel,
|
||||||
|
H1,
|
||||||
|
H2,
|
||||||
|
MiddlePanel,
|
||||||
|
PropertiesTable,
|
||||||
|
Row,
|
||||||
|
Section,
|
||||||
|
ShareLink,
|
||||||
|
} from '../common-elements';
|
||||||
|
import Highlighter from './code/Highlighter';
|
||||||
|
|
||||||
|
const H1Comp = ({ children, ...props }) => {
|
||||||
|
return (
|
||||||
|
<H1 id={props.id}>
|
||||||
|
<ShareLink to={props.id as string} />
|
||||||
|
{children}
|
||||||
|
</H1>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const H2Comp = ({ children, ...props }) => (
|
||||||
|
<H2 id={props.id}>
|
||||||
|
<ShareLink to={props.id as string} />
|
||||||
|
{children}
|
||||||
|
</H2>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Wrapper = ({ children }) => {
|
||||||
|
const getSections = (children) => {
|
||||||
|
let currentSection: any[] = [];
|
||||||
|
let currentId: number | null = null;
|
||||||
|
let currentCodeBlocks: any[] = [];
|
||||||
|
const result: any[] = [];
|
||||||
|
|
||||||
|
children.forEach((child) => {
|
||||||
|
const type = child.props.mdxType;
|
||||||
|
if (['h1', 'h2', 'h3', 'h4', 'h5'].includes(type)) {
|
||||||
|
if (currentSection.length > 0) {
|
||||||
|
result.push({
|
||||||
|
id: currentId,
|
||||||
|
middle: currentSection,
|
||||||
|
right: currentCodeBlocks,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
currentId = child.props.id;
|
||||||
|
currentSection = [];
|
||||||
|
currentCodeBlocks = [];
|
||||||
|
}
|
||||||
|
if (['code', 'pre', 'blockquote'].includes(type)) {
|
||||||
|
currentCodeBlocks.push(child);
|
||||||
|
} else {
|
||||||
|
currentSection.push(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (currentSection.length > 0) {
|
||||||
|
result.push({ id: currentId, middle: currentSection, right: currentCodeBlocks });
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sections = getSections(children);
|
||||||
|
|
||||||
|
console.log('sections', sections);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{sections.map((section) => (
|
||||||
|
<Section key={`resection-${section.id}`} id={section.id}>
|
||||||
|
<Row id={section.id}>
|
||||||
|
{section.middle && <MiddlePanel>{section.middle}</MiddlePanel>}
|
||||||
|
{section.right && <DarkRightPanel>{section.right}</DarkRightPanel>}
|
||||||
|
</Row>
|
||||||
|
</Section>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
code: Highlighter,
|
||||||
|
h1: H1Comp,
|
||||||
|
h2: H2Comp,
|
||||||
|
table: PropertiesTable,
|
||||||
|
wrapper: Wrapper,
|
||||||
|
};
|
|
@ -1,100 +1,30 @@
|
||||||
import * as React from 'react';
|
const TocPlugin = require('./plugins/export-toc');
|
||||||
import {
|
|
||||||
DarkRightPanel,
|
|
||||||
H1,
|
|
||||||
H2,
|
|
||||||
MiddlePanel,
|
|
||||||
PropertiesTable,
|
|
||||||
Row,
|
|
||||||
Section,
|
|
||||||
ShareLink,
|
|
||||||
} from '../common-elements';
|
|
||||||
import Highlighter from './code/Highlighter';
|
|
||||||
|
|
||||||
const INCLUDES = ['auth', 'welcome'];
|
type Header = {
|
||||||
|
depth: number;
|
||||||
const H1Comp = ({ children, ...props }) => {
|
id: number;
|
||||||
return (
|
text: string;
|
||||||
<H1 id={props.id}>
|
};
|
||||||
<ShareLink to={props.id as string} />
|
export type MarkdownDocument = {
|
||||||
{children}
|
component: (props: any) => JSX.Element;
|
||||||
</H1>
|
headings: Header[];
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const H2Comp = ({ children, ...props }) => (
|
export type MarkdownIndex = {
|
||||||
<H2 id={props.id}>
|
components: ((props: any) => JSX.Element)[];
|
||||||
<ShareLink to={props.id as string} />
|
headings: Header[];
|
||||||
{children}
|
|
||||||
</H2>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Wrapper = ({ children }) => {
|
|
||||||
const getSections = (children) => {
|
|
||||||
let currentSection: any[] = [];
|
|
||||||
let currentId: number | null = null;
|
|
||||||
let currentCodeBlocks: any[] = [];
|
|
||||||
const result: any[] = [];
|
|
||||||
|
|
||||||
children.forEach((child) => {
|
|
||||||
const type = child.props.mdxType;
|
|
||||||
if (['h1', 'h2', 'h3', 'h4', 'h5'].includes(type)) {
|
|
||||||
if (currentSection.length > 0) {
|
|
||||||
result.push({
|
|
||||||
id: currentId,
|
|
||||||
middle: currentSection,
|
|
||||||
right: currentCodeBlocks,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
currentId = child.props.id;
|
|
||||||
currentSection = [];
|
|
||||||
currentCodeBlocks = [];
|
|
||||||
}
|
|
||||||
if (['code', 'pre', 'blockquote'].includes(type)) {
|
|
||||||
currentCodeBlocks.push(child);
|
|
||||||
} else {
|
|
||||||
currentSection.push(child);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (currentSection.length > 0) {
|
|
||||||
result.push({ id: currentId, middle: currentSection, right: currentCodeBlocks });
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const sections = getSections(children);
|
export const loadMarkdownIndexFromComponents = (mdxComponents) => {
|
||||||
|
const components = mdxComponents.map((sect) => sect.default);
|
||||||
|
const headings = mdxComponents.reduce((acc, section) => [...acc, ...section.headings], []);
|
||||||
|
|
||||||
return (
|
return {
|
||||||
<>
|
components,
|
||||||
{sections.map((section) => (
|
headings,
|
||||||
<Section key={`section-${section.id}`} id={section.id}>
|
};
|
||||||
<Row id={section.id}>
|
|
||||||
{section.middle && <MiddlePanel>{section.middle}</MiddlePanel>}
|
|
||||||
{section.right && <DarkRightPanel>{section.right}</DarkRightPanel>}
|
|
||||||
</Row>
|
|
||||||
</Section>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const includedSections = INCLUDES.map((include) => require(`./${include}.mdx`));
|
export default {
|
||||||
|
remarkPlugins: [TocPlugin],
|
||||||
export const sections = includedSections.map((sect) => sect.default);
|
|
||||||
|
|
||||||
export const headings = includedSections.reduce(
|
|
||||||
(acc, section) => [...acc, ...section.headings],
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
export const components = {
|
|
||||||
code: Highlighter,
|
|
||||||
h1: H1Comp,
|
|
||||||
h2: H2Comp,
|
|
||||||
table: PropertiesTable,
|
|
||||||
wrapper: Wrapper,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sections;
|
|
||||||
|
|
|
@ -19,26 +19,29 @@ import {
|
||||||
} from '../utils/openapi';
|
} from '../utils/openapi';
|
||||||
|
|
||||||
import { IS_BROWSER } from '../utils';
|
import { IS_BROWSER } from '../utils';
|
||||||
|
import { MarkdownIndex } from '../markdown';
|
||||||
|
|
||||||
export interface StoreState {
|
export interface StoreState {
|
||||||
menu: {
|
menu: {
|
||||||
activeItemIdx: number;
|
activeItemIdx: number;
|
||||||
};
|
};
|
||||||
spec: {
|
spec: {
|
||||||
url?: string;
|
url?: string | null;
|
||||||
data: any;
|
data: any;
|
||||||
};
|
};
|
||||||
searchIndex: any;
|
searchIndex: any;
|
||||||
options: RedocRawOptions;
|
options: RedocRawOptions;
|
||||||
|
markdownIndex?: MarkdownIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createStore(
|
export async function createStore(
|
||||||
spec: object,
|
spec: object,
|
||||||
specUrl: string | undefined,
|
specUrl: string | undefined | null,
|
||||||
options: RedocRawOptions = {},
|
options: RedocRawOptions = {},
|
||||||
|
markdownIndex?: MarkdownIndex,
|
||||||
) {
|
) {
|
||||||
const resolvedSpec = await loadAndBundleSpec(spec || specUrl);
|
const resolvedSpec = await loadAndBundleSpec(spec || specUrl);
|
||||||
return new AppStore(resolvedSpec, specUrl, options);
|
return new AppStore(resolvedSpec, specUrl, options, true, markdownIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AppStore {
|
export class AppStore {
|
||||||
|
@ -47,8 +50,8 @@ export class AppStore {
|
||||||
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
|
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
|
||||||
*/
|
*/
|
||||||
// TODO:
|
// TODO:
|
||||||
static fromJS(state: StoreState): AppStore {
|
static fromJS(state: StoreState, markdownIndex?: MarkdownIndex): AppStore {
|
||||||
const inst = new AppStore(state.spec.data, state.spec.url, state.options, false);
|
const inst = new AppStore(state.spec.data, state.spec.url, state.options, false, markdownIndex);
|
||||||
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]);
|
||||||
if (!inst.options.disableSearch) {
|
if (!inst.options.disableSearch) {
|
||||||
|
@ -63,15 +66,17 @@ export class AppStore {
|
||||||
options: RedocNormalizedOptions;
|
options: RedocNormalizedOptions;
|
||||||
search?: SearchStore<string>;
|
search?: SearchStore<string>;
|
||||||
marker = new MarkerService();
|
marker = new MarkerService();
|
||||||
|
markdownIndex?: MarkdownIndex;
|
||||||
|
|
||||||
private scroll: ScrollService;
|
private scroll: ScrollService;
|
||||||
private disposer: Lambda | null = null;
|
private disposer: Lambda | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
spec: OpenAPISpec,
|
spec: OpenAPISpec,
|
||||||
specUrl?: string,
|
specUrl?: string | null,
|
||||||
options: RedocRawOptions = {},
|
options: RedocRawOptions = {},
|
||||||
createSearchIndex: boolean = true,
|
createSearchIndex: boolean = true,
|
||||||
|
markdownIndex?: MarkdownIndex,
|
||||||
) {
|
) {
|
||||||
this.rawOptions = options;
|
this.rawOptions = options;
|
||||||
this.options = new RedocNormalizedOptions(options, DEFAULT_OPTIONS);
|
this.options = new RedocNormalizedOptions(options, DEFAULT_OPTIONS);
|
||||||
|
@ -81,7 +86,9 @@ export class AppStore {
|
||||||
MenuStore.updateOnHistory(history.currentId, this.scroll);
|
MenuStore.updateOnHistory(history.currentId, this.scroll);
|
||||||
|
|
||||||
this.spec = new SpecStore(spec, specUrl, this.options);
|
this.spec = new SpecStore(spec, specUrl, this.options);
|
||||||
this.menu = new MenuStore(this.spec, this.scroll, history);
|
|
||||||
|
this.menu = new MenuStore(this.spec, this.scroll, history, markdownIndex);
|
||||||
|
this.markdownIndex = markdownIndex;
|
||||||
|
|
||||||
if (!this.options.disableSearch) {
|
if (!this.options.disableSearch) {
|
||||||
this.search = new SearchStore();
|
this.search = new SearchStore();
|
||||||
|
@ -122,11 +129,12 @@ export class AppStore {
|
||||||
activeItemIdx: this.menu.activeItemIdx,
|
activeItemIdx: this.menu.activeItemIdx,
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
url: this.spec.parser.specUrl,
|
url: this.spec.parser.specUrl || null,
|
||||||
data: this.spec.parser.spec,
|
data: this.spec.parser.spec,
|
||||||
},
|
},
|
||||||
searchIndex: this.search ? await this.search.toJS() : undefined,
|
searchIndex: this.search ? await this.search.toJS() : undefined,
|
||||||
options: this.rawOptions,
|
options: this.rawOptions,
|
||||||
|
markdownIndex: this.markdownIndex,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { GROUP_DEPTH } from './MenuBuilder';
|
||||||
import { SpecStore } from './models';
|
import { SpecStore } from './models';
|
||||||
import { ScrollService } from './ScrollService';
|
import { ScrollService } from './ScrollService';
|
||||||
import { ExtraContent } from './models/ExtraContent';
|
import { ExtraContent } from './models/ExtraContent';
|
||||||
import { headings } from '../markdown';
|
import { MarkdownIndex } from '../markdown';
|
||||||
|
|
||||||
export type MenuItemGroupType = 'group' | 'tag' | 'section' | 'extra';
|
export type MenuItemGroupType = 'group' | 'tag' | 'section' | 'extra';
|
||||||
export type MenuItemType = MenuItemGroupType | 'operation';
|
export type MenuItemType = MenuItemGroupType | 'operation';
|
||||||
|
@ -75,15 +75,21 @@ export class MenuStore {
|
||||||
* @param spec [SpecStore](#SpecStore) which contains page content structure
|
* @param spec [SpecStore](#SpecStore) which contains page content structure
|
||||||
* @param scroll scroll service instance used by this menu
|
* @param scroll scroll service instance used by this menu
|
||||||
*/
|
*/
|
||||||
constructor(spec: SpecStore, public scroll: ScrollService, public history: HistoryService) {
|
constructor(
|
||||||
const extraItems = headings.map(
|
spec: SpecStore,
|
||||||
|
public scroll: ScrollService,
|
||||||
|
public history: HistoryService,
|
||||||
|
markdownIndex?: MarkdownIndex,
|
||||||
|
) {
|
||||||
|
const { headings: extraHeadings } = markdownIndex || {};
|
||||||
|
const extraItems = extraHeadings ? extraHeadings.map(
|
||||||
({ id, text, depth }) =>
|
({ id, text, depth }) =>
|
||||||
new ExtraContent({
|
new ExtraContent({
|
||||||
id,
|
id,
|
||||||
name: text,
|
name: text,
|
||||||
depth,
|
depth,
|
||||||
}),
|
})
|
||||||
);
|
) : [];
|
||||||
this.items = [...extraItems, ...spec.contentItems];
|
this.items = [...extraItems, ...spec.contentItems];
|
||||||
|
|
||||||
this.flatItems = flattenByProp(this.items || [], 'items');
|
this.flatItems = flattenByProp(this.items || [], 'items');
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { resolve as urlResolve } from 'url';
|
||||||
|
|
||||||
import { OpenAPIRef, OpenAPISchema, OpenAPISpec, Referenced } from '../types';
|
import { OpenAPIRef, OpenAPISchema, OpenAPISpec, Referenced } from '../types';
|
||||||
|
|
||||||
import { appendToMdHeading, IS_BROWSER } from '../utils/';
|
import { appendToMdHeading } from '../utils/';
|
||||||
import { JsonPointer } from '../utils/JsonPointer';
|
import { JsonPointer } from '../utils/JsonPointer';
|
||||||
import {
|
import {
|
||||||
isNamedDefinition,
|
isNamedDefinition,
|
||||||
|
@ -42,7 +42,7 @@ class RefCounter {
|
||||||
* Loads and keeps spec. Provides raw spec operations
|
* Loads and keeps spec. Provides raw spec operations
|
||||||
*/
|
*/
|
||||||
export class OpenAPIParser {
|
export class OpenAPIParser {
|
||||||
specUrl?: string;
|
specUrl?: string | null;
|
||||||
spec: OpenAPISpec;
|
spec: OpenAPISpec;
|
||||||
mergeRefs: Set<string>;
|
mergeRefs: Set<string>;
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ export class OpenAPIParser {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
spec: OpenAPISpec,
|
spec: OpenAPISpec,
|
||||||
specUrl?: string,
|
specUrl?: string | null,
|
||||||
private options: RedocNormalizedOptions = new RedocNormalizedOptions({}),
|
private options: RedocNormalizedOptions = new RedocNormalizedOptions({}),
|
||||||
) {
|
) {
|
||||||
this.validate(spec);
|
this.validate(spec);
|
||||||
|
@ -60,7 +60,7 @@ export class OpenAPIParser {
|
||||||
|
|
||||||
this.mergeRefs = new Set();
|
this.mergeRefs = new Set();
|
||||||
|
|
||||||
const href = IS_BROWSER ? window.location.href : '';
|
const href = '';
|
||||||
if (typeof specUrl === 'string') {
|
if (typeof specUrl === 'string') {
|
||||||
this.specUrl = urlResolve(href, specUrl);
|
this.specUrl = urlResolve(href, specUrl);
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,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;
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,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;
|
||||||
}>;
|
}>;
|
||||||
|
@ -305,7 +305,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];
|
||||||
}
|
}
|
||||||
|
@ -331,7 +331,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],
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,7 +20,7 @@ export class SpecStore {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
spec: OpenAPISpec,
|
spec: OpenAPISpec,
|
||||||
specUrl: string | undefined,
|
specUrl: string | undefined | null,
|
||||||
private options: RedocNormalizedOptions,
|
private options: RedocNormalizedOptions,
|
||||||
) {
|
) {
|
||||||
this.parser = new OpenAPIParser(spec, specUrl, options);
|
this.parser = new OpenAPIParser(spec, specUrl, options);
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
OpenAPIServer,
|
OpenAPIServer,
|
||||||
Referenced,
|
Referenced,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { IS_BROWSER } from './dom';
|
|
||||||
import { isNumeric, removeQueryString, resolveUrl } from './helpers';
|
import { isNumeric, removeQueryString, resolveUrl } from './helpers';
|
||||||
|
|
||||||
function isWildcardStatusCode(statusCode: string | number): statusCode is string {
|
function isWildcardStatusCode(statusCode: string | number): statusCode is string {
|
||||||
|
@ -140,10 +139,10 @@ export function isFormUrlEncoded(contentType: string): boolean {
|
||||||
|
|
||||||
function delimitedEncodeField(fieldVal: any, fieldName: string, delimiter: string): string {
|
function delimitedEncodeField(fieldVal: any, fieldName: string, delimiter: string): string {
|
||||||
if (Array.isArray(fieldVal)) {
|
if (Array.isArray(fieldVal)) {
|
||||||
return fieldVal.map(v => v.toString()).join(delimiter);
|
return fieldVal.map((v) => v.toString()).join(delimiter);
|
||||||
} else if (typeof fieldVal === 'object') {
|
} else if (typeof fieldVal === 'object') {
|
||||||
return Object.keys(fieldVal)
|
return Object.keys(fieldVal)
|
||||||
.map(k => `${k}${delimiter}${fieldVal[k]}`)
|
.map((k) => `${k}${delimiter}${fieldVal[k]}`)
|
||||||
.join(delimiter);
|
.join(delimiter);
|
||||||
} else {
|
} else {
|
||||||
return fieldName + '=' + fieldVal.toString();
|
return fieldName + '=' + fieldVal.toString();
|
||||||
|
@ -156,7 +155,7 @@ function deepObjectEncodeField(fieldVal: any, fieldName: string): string {
|
||||||
return '';
|
return '';
|
||||||
} else if (typeof fieldVal === 'object') {
|
} else if (typeof fieldVal === 'object') {
|
||||||
return Object.keys(fieldVal)
|
return Object.keys(fieldVal)
|
||||||
.map(k => `${fieldName}[${k}]=${fieldVal[k]}`)
|
.map((k) => `${fieldName}[${k}]=${fieldVal[k]}`)
|
||||||
.join('&');
|
.join('&');
|
||||||
} else {
|
} else {
|
||||||
console.warn('deepObject style cannot be used with non-object value:' + fieldVal.toString());
|
console.warn('deepObject style cannot be used with non-object value:' + fieldVal.toString());
|
||||||
|
@ -188,7 +187,7 @@ export function urlFormEncodePayload(
|
||||||
throw new Error('Payload must have fields: ' + payload.toString());
|
throw new Error('Payload must have fields: ' + payload.toString());
|
||||||
} else {
|
} else {
|
||||||
return Object.keys(payload)
|
return Object.keys(payload)
|
||||||
.map(fieldName => {
|
.map((fieldName) => {
|
||||||
const fieldVal = payload[fieldName];
|
const fieldVal = payload[fieldName];
|
||||||
const { style = 'form', explode = true } = encoding[fieldName] || {};
|
const { style = 'form', explode = true } = encoding[fieldName] || {};
|
||||||
switch (style) {
|
switch (style) {
|
||||||
|
@ -450,7 +449,7 @@ export function sortByRequired(fields: FieldModel[], order: string[] = []) {
|
||||||
const orderedFields: FieldModel[] = [];
|
const orderedFields: FieldModel[] = [];
|
||||||
const unorderedFields: FieldModel[] = [];
|
const unorderedFields: FieldModel[] = [];
|
||||||
|
|
||||||
fields.forEach(field => {
|
fields.forEach((field) => {
|
||||||
if (field.required) {
|
if (field.required) {
|
||||||
order.includes(field.name) ? orderedFields.push(field) : unorderedFields.push(field);
|
order.includes(field.name) ? orderedFields.push(field) : unorderedFields.push(field);
|
||||||
} else {
|
} else {
|
||||||
|
@ -478,13 +477,13 @@ export function mergeParams(
|
||||||
operationParams: Array<Referenced<OpenAPIParameter>> = [],
|
operationParams: Array<Referenced<OpenAPIParameter>> = [],
|
||||||
): Array<Referenced<OpenAPIParameter>> {
|
): Array<Referenced<OpenAPIParameter>> {
|
||||||
const operationParamNames = {};
|
const operationParamNames = {};
|
||||||
operationParams.forEach(param => {
|
operationParams.forEach((param) => {
|
||||||
param = parser.shalowDeref(param);
|
param = parser.shalowDeref(param);
|
||||||
operationParamNames[param.name + '_' + param.in] = true;
|
operationParamNames[param.name + '_' + param.in] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// filter out path params overridden by operation ones with the same name
|
// filter out path params overridden by operation ones with the same name
|
||||||
pathParams = pathParams.filter(param => {
|
pathParams = pathParams.filter((param) => {
|
||||||
param = parser.shalowDeref(param);
|
param = parser.shalowDeref(param);
|
||||||
return !operationParamNames[param.name + '_' + param.in];
|
return !operationParamNames[param.name + '_' + param.in];
|
||||||
});
|
});
|
||||||
|
@ -496,7 +495,7 @@ export function mergeSimilarMediaTypes(
|
||||||
types: Record<string, OpenAPIMediaType>,
|
types: Record<string, OpenAPIMediaType>,
|
||||||
): Record<string, OpenAPIMediaType> {
|
): Record<string, OpenAPIMediaType> {
|
||||||
const mergedTypes = {};
|
const mergedTypes = {};
|
||||||
Object.keys(types).forEach(name => {
|
Object.keys(types).forEach((name) => {
|
||||||
const mime = types[name];
|
const mime = types[name];
|
||||||
// ignore content type parameters (e.g. charset) and merge
|
// ignore content type parameters (e.g. charset) and merge
|
||||||
const normalizedMimeName = name.split(';')[0].trim();
|
const normalizedMimeName = name.split(';')[0].trim();
|
||||||
|
@ -518,18 +517,14 @@ export function expandDefaultServerVariables(url: string, variables: object = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeServers(
|
export function normalizeServers(
|
||||||
specUrl: string | undefined,
|
specUrl: string | undefined | null,
|
||||||
servers: OpenAPIServer[],
|
servers: OpenAPIServer[],
|
||||||
): OpenAPIServer[] {
|
): OpenAPIServer[] {
|
||||||
const getHref = () => {
|
const getHref = () => {
|
||||||
if (!IS_BROWSER) {
|
|
||||||
return '';
|
return '';
|
||||||
}
|
|
||||||
const href = window.location.href;
|
|
||||||
return href.endsWith('.html') ? dirname(href) : href;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const baseUrl = specUrl === undefined ? removeQueryString(getHref()) : dirname(specUrl);
|
const baseUrl = !specUrl ? removeQueryString(getHref()) : dirname(specUrl);
|
||||||
|
|
||||||
if (servers.length === 0) {
|
if (servers.length === 0) {
|
||||||
// Behaviour defined in OpenAPI spec: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#openapi-object
|
// Behaviour defined in OpenAPI spec: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#openapi-object
|
||||||
|
@ -544,7 +539,7 @@ export function normalizeServers(
|
||||||
return resolveUrl(baseUrl, url);
|
return resolveUrl(baseUrl, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
return servers.map(server => {
|
return servers.map((server) => {
|
||||||
return {
|
return {
|
||||||
...server,
|
...server,
|
||||||
url: normalizeUrl(server.url),
|
url: normalizeUrl(server.url),
|
||||||
|
@ -562,7 +557,7 @@ export function setSecuritySchemePrefix(prefix: string) {
|
||||||
SECURITY_SCHEMES_SECTION_PREFIX = prefix;
|
SECURITY_SCHEMES_SECTION_PREFIX = prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const shortenHTTPVerb = verb =>
|
export const shortenHTTPVerb = (verb) =>
|
||||||
({
|
({
|
||||||
delete: 'del',
|
delete: 'del',
|
||||||
options: 'opts',
|
options: 'opts',
|
||||||
|
@ -593,7 +588,7 @@ export function extractExtensions(
|
||||||
showExtensions: string[] | true,
|
showExtensions: string[] | true,
|
||||||
): Record<string, any> {
|
): Record<string, any> {
|
||||||
return Object.keys(obj)
|
return Object.keys(obj)
|
||||||
.filter(key => {
|
.filter((key) => {
|
||||||
if (showExtensions === true) {
|
if (showExtensions === true) {
|
||||||
return key.startsWith('x-') && !isRedocExtension(key);
|
return key.startsWith('x-') && !isRedocExtension(key);
|
||||||
}
|
}
|
||||||
|
@ -608,6 +603,6 @@ export function extractExtensions(
|
||||||
export function pluralizeType(displayType: string): string {
|
export function pluralizeType(displayType: string): string {
|
||||||
return displayType
|
return displayType
|
||||||
.split(' or ')
|
.split(' or ')
|
||||||
.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 ');
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@ import * as webpack from 'webpack';
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
|
const remarkSlugs = require('remark-slug');
|
||||||
|
const rehypeHtml = require('rehype-stringify');
|
||||||
|
const exportToc = require('./src/markdown/plugins/export-toc');
|
||||||
|
|
||||||
const nodeExternals = require('webpack-node-externals')({
|
const nodeExternals = require('webpack-node-externals')({
|
||||||
// bundle in modules that need transpiling + non-js (e.g. css)
|
// bundle in modules that need transpiling + non-js (e.g. css)
|
||||||
allowlist: [
|
allowlist: [
|
||||||
|
@ -27,6 +31,28 @@ try {
|
||||||
console.error('Skipping REDOC_REVISION');
|
console.error('Skipping REDOC_REVISION');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const babelLoader = (mode, transformJsx = false) => ({
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
generatorOpts: {
|
||||||
|
decoratorsBeforeExport: true,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
['@babel/plugin-syntax-typescript', { isTSX: true }],
|
||||||
|
['@babel/plugin-syntax-decorators', { legacy: true }],
|
||||||
|
'@babel/plugin-syntax-jsx',
|
||||||
|
...(transformJsx ? ['@babel/plugin-transform-react-jsx'] : []),
|
||||||
|
[
|
||||||
|
'babel-plugin-styled-components',
|
||||||
|
{
|
||||||
|
minify: true,
|
||||||
|
displayName: mode !== 'production',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const BANNER = `ReDoc - OpenAPI/Swagger-generated API Reference Documentation
|
const BANNER = `ReDoc - OpenAPI/Swagger-generated API Reference Documentation
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
Version: ${VERSION}
|
Version: ${VERSION}
|
||||||
|
@ -76,6 +102,19 @@ export default (env: { standalone?: boolean } = {}, { mode }) => ({
|
||||||
|
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.mdx?$/,
|
||||||
|
use: [
|
||||||
|
babelLoader(mode, true),
|
||||||
|
{
|
||||||
|
loader: '@mdx-js/loader',
|
||||||
|
options: {
|
||||||
|
remarkPlugins: [remarkSlugs, exportToc],
|
||||||
|
rehypePlugins: [rehypeHtml],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.tsx?$/,
|
test: /\.tsx?$/,
|
||||||
use: [
|
use: [
|
||||||
|
@ -88,26 +127,7 @@ export default (env: { standalone?: boolean } = {}, { mode }) => ({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
babelLoader(mode),
|
||||||
loader: 'babel-loader',
|
|
||||||
options: {
|
|
||||||
generatorOpts: {
|
|
||||||
decoratorsBeforeExport: true,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
['@babel/plugin-syntax-typescript', { isTSX: true }],
|
|
||||||
['@babel/plugin-syntax-decorators', { legacy: true }],
|
|
||||||
'@babel/plugin-syntax-jsx',
|
|
||||||
[
|
|
||||||
'babel-plugin-styled-components',
|
|
||||||
{
|
|
||||||
minify: true,
|
|
||||||
displayName: mode !== 'production',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
exclude: [/node_modules/],
|
exclude: [/node_modules/],
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user