mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-08 14:14:56 +03:00
Merge remote-tracking branch 'source/master' into develop
This commit is contained in:
commit
965778d3b0
35
cli/index.ts
35
cli/index.ts
|
@ -13,7 +13,7 @@ import * as zlib from 'zlib';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { createStore, loadAndBundleSpec, Redoc } from 'redoc';
|
import { createStore, loadAndBundleSpec, Redoc } from 'redoc';
|
||||||
|
|
||||||
import {watch} from 'chokidar';
|
import { watch } from 'chokidar';
|
||||||
import { createReadStream, existsSync, readFileSync, ReadStream, writeFileSync } from 'fs';
|
import { createReadStream, existsSync, readFileSync, ReadStream, writeFileSync } from 'fs';
|
||||||
import * as mkdirp from 'mkdirp';
|
import * as mkdirp from 'mkdirp';
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ interface Options {
|
||||||
cdn?: boolean;
|
cdn?: boolean;
|
||||||
output?: string;
|
output?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
port?: number;
|
||||||
templateFileName?: string;
|
templateFileName?: string;
|
||||||
templateOptions?: any;
|
templateOptions?: any;
|
||||||
redocOptions?: any;
|
redocOptions?: any;
|
||||||
|
@ -62,16 +63,16 @@ YargsParser.command(
|
||||||
return yargs;
|
return yargs;
|
||||||
},
|
},
|
||||||
async argv => {
|
async argv => {
|
||||||
const config = {
|
const config: Options = {
|
||||||
ssr: argv.ssr,
|
ssr: argv.ssr as boolean,
|
||||||
watch: argv.watch,
|
watch: argv.watch as boolean,
|
||||||
templateFileName: argv.template,
|
templateFileName: argv.template as string,
|
||||||
templateOptions: argv.templateOptions || {},
|
templateOptions: argv.templateOptions || {},
|
||||||
redocOptions: argv.options || {},
|
redocOptions: argv.options || {},
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await serve(argv.port, argv.spec, config);
|
await serve(argv.port as number, argv.spec as string, config);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e);
|
handleError(e);
|
||||||
}
|
}
|
||||||
|
@ -108,12 +109,12 @@ YargsParser.command(
|
||||||
return yargs;
|
return yargs;
|
||||||
},
|
},
|
||||||
async argv => {
|
async argv => {
|
||||||
const config = {
|
const config: Options = {
|
||||||
ssr: true,
|
ssr: true,
|
||||||
output: argv.o,
|
output: argv.o as string,
|
||||||
cdn: argv.cdn,
|
cdn: argv.cdn as boolean,
|
||||||
title: argv.title,
|
title: argv.title as string,
|
||||||
templateFileName: argv.template,
|
templateFileName: argv.template as string,
|
||||||
templateOptions: argv.templateOptions || {},
|
templateOptions: argv.templateOptions || {},
|
||||||
redocOptions: argv.options || {},
|
redocOptions: argv.options || {},
|
||||||
};
|
};
|
||||||
|
@ -132,7 +133,8 @@ YargsParser.command(
|
||||||
type: 'string',
|
type: 'string',
|
||||||
})
|
})
|
||||||
.options('templateOptions', {
|
.options('templateOptions', {
|
||||||
describe: 'Additional options that you want pass to template. Use dot notation, e.g. templateOptions.metaDescription',
|
describe:
|
||||||
|
'Additional options that you want pass to template. Use dot notation, e.g. templateOptions.metaDescription',
|
||||||
})
|
})
|
||||||
.options('options', {
|
.options('options', {
|
||||||
describe: 'ReDoc options, use dot notation, e.g. options.nativeScrollbars',
|
describe: 'ReDoc options, use dot notation, e.g. options.nativeScrollbars',
|
||||||
|
@ -190,7 +192,8 @@ async function serve(port: number, pathToSpec: string, options: Options = {}) {
|
||||||
log('Updated successfully');
|
log('Updated successfully');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error while updating: ', e.message);
|
console.error('Error while updating: ', e.message);
|
||||||
}})
|
}
|
||||||
|
})
|
||||||
.on('error', error => console.error(`Watcher error: ${error}`))
|
.on('error', error => console.error(`Watcher error: ${error}`))
|
||||||
.on('ready', () => log(`👀 Watching ${pathToSpecDirectory} for changes...`));
|
.on('ready', () => log(`👀 Watching ${pathToSpecDirectory} for changes...`));
|
||||||
}
|
}
|
||||||
|
@ -247,13 +250,13 @@ async function getPageHTML(
|
||||||
ssr
|
ssr
|
||||||
? 'hydrate(__redoc_state, container);'
|
? 'hydrate(__redoc_state, container);'
|
||||||
: `init("spec.json", ${JSON.stringify(redocOptions)}, container)`
|
: `init("spec.json", ${JSON.stringify(redocOptions)}, container)`
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>`,
|
</script>`,
|
||||||
redocHead: ssr
|
redocHead: ssr
|
||||||
? (cdn
|
? (cdn
|
||||||
? '<script src="https://unpkg.com/redoc@next/bundles/redoc.standalone.js"></script>'
|
? '<script src="https://unpkg.com/redoc@next/bundles/redoc.standalone.js"></script>'
|
||||||
: `<script>${redocStandaloneSrc}</script>`) + css
|
: `<script>${redocStandaloneSrc}</script>`) + css
|
||||||
: '<script src="redoc.standalone.js"></script>',
|
: '<script src="redoc.standalone.js"></script>',
|
||||||
title,
|
title,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
Serve remote spec by URL:
|
Serve remote spec by URL:
|
||||||
|
|
||||||
docker run -it --rm -p 80:80 \
|
docker run -it --rm -p 80:80 \
|
||||||
-e SPEC_URL='http://localhost:8000/swagger.yaml' redoc
|
-e SPEC_URL='http://localhost:8000/swagger.yaml' redocly/redoc
|
||||||
|
|
||||||
Serve local file:
|
Serve local file:
|
||||||
|
|
||||||
docker run -it --rm -p 80:80 \
|
docker run -it --rm -p 80:80 \
|
||||||
-v $(PWD)/demo/swagger.yaml:/usr/share/nginx/html/swagger.yaml \
|
-v $(pwd)/demo/swagger.yaml:/usr/share/nginx/html/swagger.yaml \
|
||||||
-e SPEC_URL=swagger.yaml redoc
|
-e SPEC_URL=swagger.yaml redocly/redoc
|
||||||
|
|
||||||
## Runtime configuration options
|
## Runtime configuration options
|
||||||
|
|
||||||
|
@ -23,4 +23,4 @@ Serve local file:
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
docker build -t redoc .
|
docker build -t redocly/redoc .
|
||||||
|
|
2
custom.d.ts
vendored
2
custom.d.ts
vendored
|
@ -18,6 +18,8 @@ declare module '*.css' {
|
||||||
declare var __REDOC_VERSION__: string;
|
declare var __REDOC_VERSION__: string;
|
||||||
declare var __REDOC_REVISION__: string;
|
declare var __REDOC_REVISION__: string;
|
||||||
|
|
||||||
|
declare var reactHotLoaderGlobal: any;
|
||||||
|
|
||||||
interface Element {
|
interface Element {
|
||||||
scrollIntoViewIfNeeded(centerIfNeeded?: boolean): void;
|
scrollIntoViewIfNeeded(centerIfNeeded?: boolean): void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,8 +57,8 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
env.playground
|
env.playground
|
||||||
? 'playground/hmr-playground.tsx'
|
? 'playground/hmr-playground.tsx'
|
||||||
: env.bench
|
: env.bench
|
||||||
? '../benchmark/index.tsx'
|
? '../benchmark/index.tsx'
|
||||||
: 'index.tsx',
|
: 'index.tsx',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
output: {
|
output: {
|
||||||
|
@ -77,6 +77,12 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.tsx', '.js', '.json'],
|
extensions: ['.ts', '.tsx', '.js', '.json'],
|
||||||
|
alias:
|
||||||
|
mode !== 'production'
|
||||||
|
? {
|
||||||
|
'react-dom': '@hot-loader/react-dom',
|
||||||
|
}
|
||||||
|
: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
node: {
|
node: {
|
||||||
|
@ -105,7 +111,6 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
minimize: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -136,8 +141,8 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
template: env.playground
|
template: env.playground
|
||||||
? 'demo/playground/index.html'
|
? 'demo/playground/index.html'
|
||||||
: env.bench
|
: env.bench
|
||||||
? 'benchmark/index.html'
|
? 'benchmark/index.html'
|
||||||
: 'demo/index.html',
|
: 'demo/index.html',
|
||||||
}),
|
}),
|
||||||
new ForkTsCheckerWebpackPlugin(),
|
new ForkTsCheckerWebpackPlugin(),
|
||||||
ignore(/js-yaml\/dumper\.js$/),
|
ignore(/js-yaml\/dumper\.js$/),
|
||||||
|
|
131
package.json
131
package.json
|
@ -52,74 +52,75 @@
|
||||||
"docker:build": "docker build -f config/docker/Dockerfile -t redoc ."
|
"docker:build": "docker build -f config/docker/Dockerfile -t redoc ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.1.6",
|
"@babel/core": "7.3.4",
|
||||||
"@babel/plugin-syntax-decorators": "7.1.0",
|
"@babel/plugin-syntax-decorators": "7.2.0",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||||
"@babel/plugin-syntax-jsx": "7.0.0",
|
"@babel/plugin-syntax-jsx": "7.2.0",
|
||||||
"@babel/plugin-syntax-typescript": "7.1.5",
|
"@babel/plugin-syntax-typescript": "7.3.3",
|
||||||
"@cypress/webpack-preprocessor": "4.0.2",
|
"@cypress/webpack-preprocessor": "4.0.3",
|
||||||
|
"@hot-loader/react-dom": "^16.8.4",
|
||||||
"@types/chai": "4.1.7",
|
"@types/chai": "4.1.7",
|
||||||
"@types/dompurify": "^0.0.32",
|
"@types/dompurify": "^0.0.32",
|
||||||
"@types/enzyme": "^3.1.15",
|
"@types/enzyme": "^3.9.0",
|
||||||
"@types/enzyme-to-json": "^1.5.2",
|
"@types/enzyme-to-json": "^1.5.3",
|
||||||
"@types/jest": "^23.3.9",
|
"@types/jest": "^24.0.11",
|
||||||
"@types/json-pointer": "^1.0.30",
|
"@types/json-pointer": "^1.0.30",
|
||||||
"@types/lodash": "^4.14.118",
|
"@types/lodash": "^4.14.122",
|
||||||
"@types/lunr": "^2.1.6",
|
"@types/lunr": "^2.3.2",
|
||||||
"@types/mark.js": "^8.11.1",
|
"@types/mark.js": "^8.11.3",
|
||||||
"@types/marked": "^0.6.0",
|
"@types/marked": "^0.6.3",
|
||||||
"@types/prismjs": "^1.6.4",
|
"@types/prismjs": "^1.9.1",
|
||||||
"@types/prop-types": "^15.5.6",
|
"@types/prop-types": "^15.7.0",
|
||||||
"@types/react": "^16.7.7",
|
"@types/react": "^16.8.7",
|
||||||
"@types/react-dom": "^16.0.10",
|
"@types/react-dom": "^16.8.2",
|
||||||
"@types/react-hot-loader": "^4.1.0",
|
"@types/react-hot-loader": "^4.1.0",
|
||||||
"@types/react-tabs": "^2.3.0",
|
"@types/react-tabs": "^2.3.1",
|
||||||
"@types/styled-components": "^4.1.1",
|
"@types/styled-components": "^4.1.12",
|
||||||
"@types/tapable": "1.0.4",
|
"@types/tapable": "1.0.4",
|
||||||
"@types/webpack": "^4.4.19",
|
"@types/webpack": "^4.4.25",
|
||||||
"@types/webpack-env": "^1.13.0",
|
"@types/webpack-env": "^1.13.9",
|
||||||
"@types/yargs": "^12.0.1",
|
"@types/yargs": "^12.0.9",
|
||||||
"babel-loader": "8.0.4",
|
"babel-loader": "8.0.5",
|
||||||
"babel-plugin-styled-components": "^1.9.0",
|
"babel-plugin-styled-components": "^1.10.0",
|
||||||
"beautify-benchmark": "^0.2.4",
|
"beautify-benchmark": "^0.2.4",
|
||||||
"bundlesize": "^0.17.0",
|
"bundlesize": "^0.17.1",
|
||||||
"conventional-changelog-cli": "^2.0.11",
|
"conventional-changelog-cli": "^2.0.12",
|
||||||
"copy-webpack-plugin": "^4.6.0",
|
"copy-webpack-plugin": "^5.0.0",
|
||||||
"core-js": "^2.5.7",
|
"core-js": "^2.6.5",
|
||||||
"coveralls": "^3.0.2",
|
"coveralls": "^3.0.3",
|
||||||
"css-loader": "^1.0.1",
|
"css-loader": "^2.1.1",
|
||||||
"cypress": "~3.1.2",
|
"cypress": "~3.1.5",
|
||||||
"deploy-to-gh-pages": "^1.3.6",
|
"deploy-to-gh-pages": "^1.3.6",
|
||||||
"enzyme": "^3.7.0",
|
"enzyme": "^3.9.0",
|
||||||
"enzyme-adapter-react-16": "^1.7.0",
|
"enzyme-adapter-react-16": "^1.10.0",
|
||||||
"enzyme-to-json": "^3.3.4",
|
"enzyme-to-json": "^3.3.5",
|
||||||
"fork-ts-checker-webpack-plugin": "0.5.0",
|
"fork-ts-checker-webpack-plugin": "1.0.0",
|
||||||
"html-webpack-plugin": "^3.1.0",
|
"html-webpack-plugin": "^3.1.0",
|
||||||
"jest": "^23.6.0",
|
"jest": "^24.3.1",
|
||||||
"license-checker": "^24.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"mobx": "^4.3.1",
|
"mobx": "^4.3.1",
|
||||||
"prettier": "^1.15.2",
|
"prettier": "^1.16.4",
|
||||||
"prettier-eslint": "^8.8.2",
|
"prettier-eslint": "^8.8.2",
|
||||||
"puppeteer": "^1.10.0",
|
"puppeteer": "^1.13.0",
|
||||||
"raf": "^3.4.1",
|
"raf": "^3.4.1",
|
||||||
"react": "^16.6.3",
|
"react": "^16.8.4",
|
||||||
"react-dom": "^16.6.3",
|
"react-dom": "^16.8.4",
|
||||||
"rimraf": "^2.6.2",
|
"rimraf": "^2.6.3",
|
||||||
"shelljs": "^0.8.3",
|
"shelljs": "^0.8.3",
|
||||||
"source-map-loader": "^0.2.4",
|
"source-map-loader": "^0.2.4",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"styled-components": "^4.1.1",
|
"styled-components": "^4.1.3",
|
||||||
"swagger2openapi": "^3.2.14",
|
"swagger2openapi": "^5.2.3",
|
||||||
"ts-jest": "23.10.5",
|
"ts-jest": "24.0.0",
|
||||||
"ts-loader": "5.3.1",
|
"ts-loader": "5.3.3",
|
||||||
"ts-node": "^7.0.1",
|
"ts-node": "^8.0.3",
|
||||||
"tslint": "^5.11.0",
|
"tslint": "^5.13.1",
|
||||||
"tslint-react": "^3.4.0",
|
"tslint-react": "^3.4.0",
|
||||||
"typescript": "^3.1.6",
|
"typescript": "^3.3.3333",
|
||||||
"webpack": "^4.26.1",
|
"webpack": "^4.29.6",
|
||||||
"webpack-cli": "^3.1.2",
|
"webpack-cli": "^3.2.3",
|
||||||
"webpack-dev-server": "^3.1.10",
|
"webpack-dev-server": "^3.2.1",
|
||||||
"webpack-node-externals": "^1.6.0",
|
"webpack-node-externals": "^1.6.0",
|
||||||
"workerize-loader": "^1.0.4",
|
"workerize-loader": "^1.0.4",
|
||||||
"yaml-js": "^0.2.3"
|
"yaml-js": "^0.2.3"
|
||||||
|
@ -133,24 +134,24 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"decko": "^1.2.0",
|
"decko": "^1.2.0",
|
||||||
"dompurify": "^1.0.7",
|
"dompurify": "^1.0.10",
|
||||||
"eventemitter3": "^3.0.0",
|
"eventemitter3": "^3.0.0",
|
||||||
"json-pointer": "^0.6.0",
|
"json-pointer": "^0.6.0",
|
||||||
"json-schema-ref-parser": "^6.0.1",
|
"json-schema-ref-parser": "^6.1.0",
|
||||||
"lunr": "^2.3.2",
|
"lunr": "^2.3.6",
|
||||||
"mark.js": "^8.11.1",
|
"mark.js": "^8.11.1",
|
||||||
"marked": "^0.6.0",
|
"marked": "^0.6.1",
|
||||||
"memoize-one": "^4.0.0",
|
"memoize-one": "^5.0.0",
|
||||||
"mobx-react": "^5.2.5",
|
"mobx-react": "^5.4.3",
|
||||||
"openapi-sampler": "1.0.0-beta.14",
|
"openapi-sampler": "1.0.0-beta.14",
|
||||||
"perfect-scrollbar": "^1.4.0",
|
"perfect-scrollbar": "^1.4.0",
|
||||||
"polished": "^2.0.2",
|
"polished": "^3.0.3",
|
||||||
"prismjs": "^1.15.0",
|
"prismjs": "^1.15.0",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.7.2",
|
||||||
"react-dropdown": "^1.6.2",
|
"react-dropdown": "^1.6.4",
|
||||||
"react-hot-loader": "^4.3.5",
|
"react-hot-loader": "^4.8.0",
|
||||||
"react-tabs": "^2.0.0",
|
"react-tabs": "^3.0.0",
|
||||||
"slugify": "^1.3.1",
|
"slugify": "^1.3.4",
|
||||||
"stickyfill": "^1.1.1",
|
"stickyfill": "^1.1.1",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
|
@ -161,7 +162,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"jest": {
|
"jest": {
|
||||||
"setupTestFrameworkScriptFile": "<rootDir>/src/setupTests.ts",
|
"setupFilesAfterEnv": ["<rootDir>/src/setupTests.ts"],
|
||||||
"preset": "ts-jest",
|
"preset": "ts-jest",
|
||||||
"collectCoverageFrom": [
|
"collectCoverageFrom": [
|
||||||
"src/**/*.{ts,tsx}"
|
"src/**/*.{ts,tsx}"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import styled from '../styled-components';
|
import styled from '../styled-components';
|
||||||
|
import { PrismDiv } from './PrismDiv';
|
||||||
|
|
||||||
export const SampleControls = styled.div`
|
export const SampleControls = styled.div`
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
|
@ -21,3 +22,12 @@ export const SampleControlsWrap = styled.div`
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const StyledPre = styled(PrismDiv.withComponent('pre'))`
|
||||||
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
|
font-size: ${props => props.theme.typography.code.fontSize};
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
||||||
|
`;
|
||||||
|
|
52
src/components/PayloadSamples/Example.tsx
Normal file
52
src/components/PayloadSamples/Example.tsx
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { StyledPre } from '../../common-elements/samples';
|
||||||
|
import { ExampleModel } from '../../services/models';
|
||||||
|
import { isJsonLike, langFromMime } from '../../utils';
|
||||||
|
import { JsonViewer } from '../JsonViewer/JsonViewer';
|
||||||
|
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
|
||||||
|
import { ExampleValue } from './ExampleValue';
|
||||||
|
import { useExternalExample } from './exernalExampleHook';
|
||||||
|
|
||||||
|
export interface ExampleProps {
|
||||||
|
example: ExampleModel;
|
||||||
|
mimeType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Example({ example, mimeType }: ExampleProps) {
|
||||||
|
if (example.value === undefined && example.externalValueUrl) {
|
||||||
|
return <ExternalExample example={example} mimeType={mimeType} />;
|
||||||
|
} else {
|
||||||
|
return <ExampleValue value={example.value} mimeType={mimeType} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExternalExample({ example, mimeType }: ExampleProps) {
|
||||||
|
let value = useExternalExample(example, mimeType);
|
||||||
|
|
||||||
|
if (value === undefined) {
|
||||||
|
return <span>Loading...</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Error) {
|
||||||
|
console.log(value);
|
||||||
|
return (
|
||||||
|
<StyledPre>
|
||||||
|
Error loading external example: <br />
|
||||||
|
<a className={'token string'} href={example.externalValueUrl} target="_blank">
|
||||||
|
{example.externalValueUrl}
|
||||||
|
</a>
|
||||||
|
</StyledPre>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isJsonLike(mimeType)) {
|
||||||
|
return <JsonViewer data={value} />;
|
||||||
|
} else {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
// just in case example was cached as json but used as non-json
|
||||||
|
value = JSON.stringify(value, null, 2);
|
||||||
|
}
|
||||||
|
return <SourceCodeWithCopy lang={langFromMime(mimeType)} source={value} />;
|
||||||
|
}
|
||||||
|
}
|
18
src/components/PayloadSamples/ExampleValue.tsx
Normal file
18
src/components/PayloadSamples/ExampleValue.tsx
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { isJsonLike, langFromMime } from '../../utils/openapi';
|
||||||
|
import { JsonViewer } from '../JsonViewer/JsonViewer';
|
||||||
|
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
|
||||||
|
|
||||||
|
export interface ExampleValueProps {
|
||||||
|
value: any;
|
||||||
|
mimeType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExampleValue({ value, mimeType }: ExampleValueProps) {
|
||||||
|
if (isJsonLike(mimeType)) {
|
||||||
|
return <JsonViewer data={value} />;
|
||||||
|
} else {
|
||||||
|
return <SourceCodeWithCopy lang={langFromMime(mimeType)} source={value} />;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,11 +2,9 @@ import * as React from 'react';
|
||||||
|
|
||||||
import { SmallTabs, Tab, TabList, TabPanel } from '../../common-elements';
|
import { SmallTabs, Tab, TabList, TabPanel } from '../../common-elements';
|
||||||
import { MediaTypeModel } from '../../services/models';
|
import { MediaTypeModel } from '../../services/models';
|
||||||
import { JsonViewer } from '../JsonViewer/JsonViewer';
|
|
||||||
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
|
|
||||||
import { NoSampleLabel } from './styled.elements';
|
|
||||||
|
|
||||||
import { isJsonLike, langFromMime } from '../../utils';
|
import { Example } from './Example';
|
||||||
|
import { NoSampleLabel } from './styled.elements';
|
||||||
|
|
||||||
export interface PayloadSamplesProps {
|
export interface PayloadSamplesProps {
|
||||||
mediaType: MediaTypeModel;
|
mediaType: MediaTypeModel;
|
||||||
|
@ -18,13 +16,6 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps> {
|
||||||
const mimeType = this.props.mediaType.name;
|
const mimeType = this.props.mediaType.name;
|
||||||
|
|
||||||
const noSample = <NoSampleLabel>No sample</NoSampleLabel>;
|
const noSample = <NoSampleLabel>No sample</NoSampleLabel>;
|
||||||
const sampleView = isJsonLike(mimeType)
|
|
||||||
? sample => <JsonViewer data={sample} />
|
|
||||||
: sample =>
|
|
||||||
(sample !== undefined && (
|
|
||||||
<SourceCodeWithCopy lang={langFromMime(mimeType)} source={sample} />
|
|
||||||
)) ||
|
|
||||||
noSample;
|
|
||||||
|
|
||||||
const examplesNames = Object.keys(examples);
|
const examplesNames = Object.keys(examples);
|
||||||
if (examplesNames.length === 0) {
|
if (examplesNames.length === 0) {
|
||||||
|
@ -39,13 +30,19 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps> {
|
||||||
))}
|
))}
|
||||||
</TabList>
|
</TabList>
|
||||||
{examplesNames.map(name => (
|
{examplesNames.map(name => (
|
||||||
<TabPanel key={name}>{sampleView(examples[name].value)}</TabPanel>
|
<TabPanel key={name}>
|
||||||
|
<Example example={examples[name]} mimeType={mimeType} />
|
||||||
|
</TabPanel>
|
||||||
))}
|
))}
|
||||||
</SmallTabs>
|
</SmallTabs>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const name = examplesNames[0];
|
const name = examplesNames[0];
|
||||||
return <div>{sampleView(examples[name].value)}</div>;
|
return (
|
||||||
|
<div>
|
||||||
|
<Example example={examples[name]} mimeType={mimeType} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
src/components/PayloadSamples/exernalExampleHook.ts
Normal file
34
src/components/PayloadSamples/exernalExampleHook.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { ExampleModel } from '../../services/models/Example';
|
||||||
|
|
||||||
|
export function useExternalExample(example: ExampleModel, mimeType: string) {
|
||||||
|
const [, setIsLoading] = useState(true); // to trigger component reload
|
||||||
|
|
||||||
|
const value = useRef<any>(undefined);
|
||||||
|
const prevRef = useRef<ExampleModel | undefined>(undefined);
|
||||||
|
|
||||||
|
if (prevRef.current !== example) {
|
||||||
|
value.current = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRef.current = example;
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
const load = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
value.current = await example.getExternalValue(mimeType);
|
||||||
|
} catch (e) {
|
||||||
|
value.current = e;
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
load();
|
||||||
|
},
|
||||||
|
[example, mimeType],
|
||||||
|
);
|
||||||
|
|
||||||
|
return value.current;
|
||||||
|
}
|
|
@ -1,19 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { highlight } from '../../utils';
|
import { highlight } from '../../utils';
|
||||||
|
|
||||||
import { SampleControls, SampleControlsWrap } from '../../common-elements';
|
import { SampleControls, SampleControlsWrap, StyledPre } from '../../common-elements';
|
||||||
import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper';
|
import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper';
|
||||||
import { PrismDiv } from '../../common-elements/PrismDiv';
|
|
||||||
import styled from '../../styled-components';
|
|
||||||
|
|
||||||
const StyledPre = styled(PrismDiv.withComponent('pre'))`
|
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
|
||||||
font-size: ${props => props.theme.typography.code.fontSize};
|
|
||||||
overflow-x: auto;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export interface SourceCodeProps {
|
export interface SourceCodeProps {
|
||||||
source: string;
|
source: string;
|
||||||
|
|
|
@ -5,7 +5,9 @@ import defaultTheme from '../theme';
|
||||||
export default class TestThemeProvider extends React.Component {
|
export default class TestThemeProvider extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={defaultTheme}>{React.Children.only(this.props.children)}</ThemeProvider>
|
<ThemeProvider theme={defaultTheme}>
|
||||||
|
{React.Children.only(this.props.children as any)}
|
||||||
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,55 @@
|
||||||
|
import { resolve as urlResolve } from 'url';
|
||||||
|
|
||||||
import { OpenAPIExample, Referenced } from '../../types';
|
import { OpenAPIExample, Referenced } from '../../types';
|
||||||
|
import { isJsonLike } from '../../utils/openapi';
|
||||||
import { OpenAPIParser } from '../OpenAPIParser';
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
|
|
||||||
|
const externalExamplesCache: { [url: string]: Promise<any> } = {};
|
||||||
|
|
||||||
export class ExampleModel {
|
export class ExampleModel {
|
||||||
value: any;
|
value: any;
|
||||||
summary?: string;
|
summary?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
externalValue?: string;
|
externalValueUrl?: string;
|
||||||
|
|
||||||
constructor(parser: OpenAPIParser, infoOrRef: Referenced<OpenAPIExample>) {
|
constructor(parser: OpenAPIParser, infoOrRef: Referenced<OpenAPIExample>) {
|
||||||
Object.assign(this, parser.deref(infoOrRef));
|
const example = parser.deref(infoOrRef);
|
||||||
|
this.value = example.value;
|
||||||
|
this.summary = example.summary;
|
||||||
|
this.description = example.description;
|
||||||
|
if (example.externalValue) {
|
||||||
|
this.externalValueUrl = urlResolve(parser.specUrl || '', example.externalValue);
|
||||||
|
}
|
||||||
parser.exitRef(infoOrRef);
|
parser.exitRef(infoOrRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getExternalValue(mimeType: string): Promise<any> {
|
||||||
|
if (!this.externalValueUrl) {
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (externalExamplesCache[this.externalValueUrl]) {
|
||||||
|
return externalExamplesCache[this.externalValueUrl];
|
||||||
|
}
|
||||||
|
|
||||||
|
externalExamplesCache[this.externalValueUrl] = fetch(this.externalValueUrl).then(res => {
|
||||||
|
return res.text().then(txt => {
|
||||||
|
if (!res.ok) {
|
||||||
|
return Promise.reject(new Error(txt));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isJsonLike(mimeType)) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(txt);
|
||||||
|
} catch (e) {
|
||||||
|
return txt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return txt;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return externalExamplesCache[this.externalValueUrl];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as Sampler from 'openapi-sampler';
|
import * as Sampler from 'openapi-sampler';
|
||||||
|
|
||||||
import { OpenAPIExample, OpenAPIMediaType } from '../../types';
|
import { OpenAPIMediaType } from '../../types';
|
||||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
import { SchemaModel } from './Schema';
|
import { SchemaModel } from './Schema';
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
import { ExampleModel } from './Example';
|
import { ExampleModel } from './Example';
|
||||||
|
|
||||||
export class MediaTypeModel {
|
export class MediaTypeModel {
|
||||||
examples?: { [name: string]: OpenAPIExample };
|
examples?: { [name: string]: ExampleModel };
|
||||||
schema?: SchemaModel;
|
schema?: SchemaModel;
|
||||||
name: string;
|
name: string;
|
||||||
isRequestType: boolean;
|
isRequestType: boolean;
|
||||||
|
@ -33,7 +33,7 @@ export class MediaTypeModel {
|
||||||
this.examples = mapValues(info.examples, example => new ExampleModel(parser, example));
|
this.examples = mapValues(info.examples, example => new ExampleModel(parser, example));
|
||||||
} else if (info.example !== undefined) {
|
} else if (info.example !== undefined) {
|
||||||
this.examples = {
|
this.examples = {
|
||||||
default: new ExampleModel(parser, { value: info.example }),
|
default: new ExampleModel(parser, { value: parser.shalowDeref(info.example) }),
|
||||||
};
|
};
|
||||||
} else if (isJsonLike(name)) {
|
} else if (isJsonLike(name)) {
|
||||||
this.generateExample(parser, info);
|
this.generateExample(parser, info);
|
||||||
|
@ -49,28 +49,20 @@ export class MediaTypeModel {
|
||||||
if (this.schema && this.schema.oneOf) {
|
if (this.schema && this.schema.oneOf) {
|
||||||
this.examples = {};
|
this.examples = {};
|
||||||
for (const subSchema of this.schema.oneOf) {
|
for (const subSchema of this.schema.oneOf) {
|
||||||
const sample = Sampler.sample(
|
const sample = Sampler.sample(subSchema.rawSchema, samplerOptions, parser.spec);
|
||||||
subSchema.rawSchema,
|
|
||||||
samplerOptions,
|
|
||||||
parser.spec,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.schema.discriminatorProp && typeof sample === 'object' && sample) {
|
if (this.schema.discriminatorProp && typeof sample === 'object' && sample) {
|
||||||
sample[this.schema.discriminatorProp] = subSchema.title;
|
sample[this.schema.discriminatorProp] = subSchema.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.examples[subSchema.title] = {
|
this.examples[subSchema.title] = new ExampleModel(parser, {
|
||||||
value: sample,
|
value: sample,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
} else if (this.schema) {
|
} else if (this.schema) {
|
||||||
this.examples = {
|
this.examples = {
|
||||||
default: new ExampleModel(parser, {
|
default: new ExampleModel(parser, {
|
||||||
value: Sampler.sample(
|
value: Sampler.sample(info.schema, samplerOptions, parser.spec),
|
||||||
info.schema,
|
|
||||||
samplerOptions,
|
|
||||||
parser.spec,
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
11
typings/styled-patch.d.ts
vendored
11
typings/styled-patch.d.ts
vendored
|
@ -18,4 +18,15 @@ declare module 'styled-components' {
|
||||||
...interpolations: SimpleInterpolation[]
|
...interpolations: SimpleInterpolation[]
|
||||||
): Keyframes;
|
): Keyframes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BaseThemedCssFunction<T extends object> {
|
||||||
|
<P extends object>(
|
||||||
|
first:
|
||||||
|
| TemplateStringsArray
|
||||||
|
| CSSObject
|
||||||
|
| InterpolationFunction<ThemedStyledProps<P, T>>
|
||||||
|
| string[],
|
||||||
|
...interpolations: Array<Interpolation<ThemedStyledProps<P, T>>>
|
||||||
|
): FlattenInterpolation<ThemedStyledProps<P, T>>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,6 @@ export default (env: { standalone?: boolean } = {}, { mode }) => ({
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
sourceMap: false,
|
sourceMap: false,
|
||||||
minimize: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user