chore: modernize build pipeline (#1635)

This commit is contained in:
Roman Hotsiy 2021-06-09 14:57:36 +03:00 committed by GitHub
parent 92387bc653
commit c09cf9dbbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 8261 additions and 13386 deletions

View File

@ -26,6 +26,7 @@
- The widest OpenAPI v2.0 features support (yes, it supports even `discriminator`) <br>
![](docs/images/discriminator-demo.gif)
- OpenAPI 3.0 support
- Basic OpenAPI 3.1 support
- Neat **interactive** documentation for nested objects <br>
![](docs/images/nested-demo.gif)
- Code samples support (via vendor extension) <br>
@ -43,7 +44,6 @@
- [x] ~~React rewrite~~
- [x] ~~docs pre-rendering (performance and SEO)~~
- [ ] ability to simple branding/styling
- [ ] built-in API Console
## Releases
**Important:** all the 2.x releases are deployed to npm and can be used via jsdeliver:
@ -58,6 +58,7 @@ Additionally, all the 1.x releases are hosted on our GitHub Pages-based CDN **(d
## Version Guidance
| ReDoc Release | OpenAPI Specification |
|:--------------|:----------------------|
| 2.0.0-alpha.54| 3.1, 3.0.x, 2.0 |
| 2.0.0-alpha.x | 3.0, 2.0 |
| 1.19.x | 2.0 |
| 1.18.x | 2.0 |

690
cli/npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -17,8 +17,8 @@
"mkdirp": "^1.0.4",
"mobx": "^6.3.2",
"node-libs-browser": "^2.2.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^16.8.4",
"react-dom": "^16.8.4",
"redoc": "2.0.0-rc.53",
"styled-components": "^5.3.0",
"tslib": "^2.2.0",

46
config/webpack-utils.ts Normal file
View File

@ -0,0 +1,46 @@
import * as webpack from 'webpack';
export function getBabelLoader({useBuiltIns, hot}: {useBuiltIns: boolean, hot?: boolean}) {
return {
loader: 'babel-loader',
options: {
babelrc: false,
sourceType: 'unambiguous',
presets: [
[
'@babel/preset-env',
{
useBuiltIns: useBuiltIns ? 'usage' : false,
corejs: 3,
exclude: ['transform-typeof-symbol'],
targets: 'defaults',
modules: false,
},
],
['@babel/preset-react', { development: false, runtime: 'classic' }],
'@babel/preset-typescript',
],
plugins: [
['@babel/plugin-proposal-decorators', { legacy: true }],
['@babel/plugin-proposal-class-properties', { loose: false }],
[
'@babel/plugin-transform-runtime',
{
corejs: false,
helpers: true,
// eslint-disable-next-line import/no-internal-modules
version: require('@babel/runtime/package.json').version,
regenerator: true,
},
],
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-nullish-coalescing-operator',
hot ? 'react-hot-loader/babel' : undefined,
].filter(Boolean)
},
};
}
export function webpackIgnore(regexp) {
return new webpack.NormalModuleReplacementPlugin(regexp, require.resolve('lodash/noop.js'));
}

View File

@ -31,7 +31,7 @@ const DropDownList = styled.ul`
list-style: none;
margin: 4px 0 0 0;
padding: 5px 0;
font-family: 'Lato';
font-family: Roboto,sans-serif;
overflow: hidden;
`;

View File

@ -16,7 +16,7 @@ const demos = [
label: 'Google Calendar',
},
{ value: 'https://api.apis.guru/v2/specs/slack.com/1.5.0/openapi.yaml', label: 'Slack' },
{ value: 'https://api.apis.guru/v2/specs/zoom.us/2.0.0/swagger.yaml', label: 'Zoom.us' },
{ value: 'https://api.apis.guru/v2/specs/zoom.us/2.0.0/openapi.yaml', label: 'Zoom.us' },
{ value: 'https://docs.graphhopper.com/openapi.json', label: 'GraphHopper' },
];
@ -154,7 +154,7 @@ const Heading = styled.nav`
display: flex;
align-items: center;
font-family: 'Lato';
font-family: Roboto, sans-serif;
`;
const Logo = styled.img`

View File

@ -1,21 +1,7 @@
import * as React from 'react';
import { render } from 'react-dom';
// tslint:disable-next-line
import { AppContainer } from 'react-hot-loader';
// import DevTools from 'mobx-react-devtools';
import { Redoc, RedocProps } from '../../src/components/Redoc/Redoc';
import { AppStore } from '../../src/services/AppStore';
import { RedocRawOptions } from '../../src/services/RedocNormalizedOptions';
import { loadAndBundleSpec } from '../../src/utils/loadAndBundleSpec';
const renderRoot = (props: RedocProps) =>
render(
<AppContainer>
<Redoc {...props} />
</AppContainer>,
document.getElementById('example'),
);
import type { RedocRawOptions } from '../../src/services/RedocNormalizedOptions';
import RedocStandalone from './hot';
const big = window.location.search.indexOf('big') > -1;
const swagger = window.location.search.indexOf('swagger') > -1;
@ -25,30 +11,6 @@ const userUrl = window.location.search.match(/url=(.*)$/);
const specUrl =
(userUrl && userUrl[1]) || (swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml');
let store;
const options: RedocRawOptions = { nativeScrollbars: false, maxDisplayedEnumValues: 3 };
async function init() {
const spec = await loadAndBundleSpec(specUrl);
store = new AppStore(spec, specUrl, options);
renderRoot({ store });
}
init();
if (module.hot) {
const reload = (reloadStore = false) => async () => {
if (reloadStore) {
// create a new Store
store.dispose();
const state = await store.toJS();
store = AppStore.fromJS(state);
}
renderRoot({ store });
};
module.hot.accept(['../../src/components/Redoc/Redoc'], reload());
module.hot.accept(['../../src/services/AppStore'], reload(true));
}
render(<RedocStandalone specUrl={specUrl} options={options} />, document.getElementById('example'));

10
demo/playground/hot.tsx Normal file
View File

@ -0,0 +1,10 @@
import * as React from 'react';
// eslint-disable-next-line import/no-internal-modules
import { hot } from 'react-hot-loader/root';
import { RedocStandalone as RedocStandaloneOrig, RedocStandaloneProps } from '../../src';
const RedocStandalone = function (props: RedocStandaloneProps) {
return <RedocStandaloneOrig {...props} />;
}
export default hot(RedocStandalone);

View File

@ -1,9 +1,9 @@
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
import * as HtmlWebpackPlugin from 'html-webpack-plugin';
import { compact } from 'lodash';
import { resolve } from 'path';
import * as webpack from 'webpack';
import { getBabelLoader, webpackIgnore } from '../config/webpack-utils';
const VERSION = JSON.stringify(require('../package.json').version);
const REVISION = JSON.stringify(
@ -14,38 +14,6 @@ function root(filename) {
return resolve(__dirname + '/' + filename);
}
const tsLoader = (env) => ({
loader: 'ts-loader',
options: {
compilerOptions: {
module: env.bench ? 'esnext' : 'es2015',
declaration: false,
},
},
});
const babelLoader = () => ({
loader: 'babel-loader',
options: {
generatorOpts: {
decoratorsBeforeExport: true,
},
plugins: compact([
['@babel/plugin-syntax-typescript', { isTSX: true }],
['@babel/plugin-syntax-decorators', { legacy: true }],
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-syntax-jsx',
]),
},
});
const babelHotLoader = {
loader: 'babel-loader',
options: {
plugins: ['react-hot-loader/babel'],
},
};
export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) => ({
entry: [
root('../src/polyfills.ts'),
@ -57,6 +25,7 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
: 'index.tsx',
),
],
target: 'web',
output: {
filename: 'redoc-demo.bundle.js',
path: root('dist'),
@ -69,22 +38,25 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
port: 9090,
disableHostCheck: true,
stats: 'minimal',
hot: true,
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
fallback: {
path: require.resolve('path-browserify'),
http: false,
fs: false,
os: false,
},
alias:
mode !== 'production'
? {
'react-dom': '@hot-loader/react-dom',
}
'react-dom': '@hot-loader/react-dom',
}
: {},
},
node: {
fs: 'empty',
},
performance: false,
externals: {
@ -101,12 +73,23 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
{ test: [/\.eot$/, /\.gif$/, /\.woff$/, /\.svg$/, /\.ttf$/], use: 'null-loader' },
{
test: /\.tsx?$/,
use: compact([
mode !== 'production' ? babelHotLoader : undefined,
tsLoader(env),
babelLoader(),
]),
exclude: [/node_modules/],
use: [getBabelLoader({useBuiltIns: true, hot: true} )],
exclude: {
and: [/node_modules/],
not: {
or: [
/swagger2openapi/,
/reftools/,
/openapi-sampler/,
/mobx/,
/oas-resolver/,
/oas-kit-common/,
/oas-schema-walker/,
/\@redocly\/openapi-core/,
/colorette/,
],
},
},
},
{
test: /\.css$/,
@ -117,29 +100,18 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
},
},
},
{
test: /node_modules\/(swagger2openapi|reftools|oas-resolver|oas-kit-common|oas-schema-walker)\/.*\.js$/,
use: {
loader: 'ts-loader',
options: {
transpileOnly: true,
instance: 'ts2js-transpiler-only',
compilerOptions: {
allowJs: true,
declaration: false,
},
},
},
},
],
},
plugins: [
new webpack.DefinePlugin({
__REDOC_VERSION__: VERSION,
__REDOC_REVISION__: REVISION,
'process.env': '{}',
'process.platform': '"browser"',
'process.stdout': 'null',
}),
new webpack.NamedModulesPlugin(),
new webpack.optimize.ModuleConcatenationPlugin(),
// new webpack.NamedModulesPlugin(),
// new webpack.optimize.ModuleConcatenationPlugin(),
new HtmlWebpackPlugin({
template: env.playground
? 'demo/playground/index.html'
@ -147,16 +119,12 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
? 'benchmark/index.html'
: 'demo/index.html',
}),
new ForkTsCheckerWebpackPlugin(),
ignore(/js-yaml\/dumper\.js$/),
ignore(/json-schema-ref-parser\/lib\/dereference\.js/),
ignore(/^\.\/SearchWorker\.worker$/),
new ForkTsCheckerWebpackPlugin({ logger: { infrastructure: 'silent', issues: 'console' } }),
webpackIgnore(/js-yaml\/dumper\.js$/),
webpackIgnore(/json-schema-ref-parser\/lib\/dereference\.js/),
webpackIgnore(/^\.\/SearchWorker\.worker$/),
new CopyWebpackPlugin({
patterns: ['demo/openapi.yaml'],
}),
],
});
function ignore(regexp) {
return new webpack.NormalModuleReplacementPlugin(regexp, require.resolve('lodash/noop.js'));
}

20334
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,10 @@
"type": "git",
"url": "git://github.com/Redocly/redoc"
},
"browserslist": [
"defaults",
"ie 11"
],
"engines": {
"node": ">=6.9",
"npm": ">=3.0.0"
@ -25,9 +29,9 @@
"main": "bundles/redoc.lib.js",
"types": "typings/index.d.ts",
"scripts": {
"start": "webpack-dev-server --mode=development --env.playground --hot --config demo/webpack.config.ts",
"start:prod": "webpack-dev-server --env.playground --mode=production --config demo/webpack.config.ts",
"start:benchmark": "webpack-dev-server --mode=production --env.bench --config demo/webpack.config.ts",
"start": "webpack serve --mode=development --env playground --hot --config demo/webpack.config.ts",
"start:prod": "webpack serve --env playground --mode=production --config demo/webpack.config.ts",
"start:benchmark": "webpack serve --mode=production --env.bench --config demo/webpack.config.ts",
"test": "npm run lint && npm run unit && npm run license-check",
"unit": "jest --coverage",
"e2e": "cypress run",
@ -36,16 +40,16 @@
"ts-check": "tsc --noEmit --skipLibCheck",
"cy:open": "cypress open",
"bundle:clean": "rimraf bundles",
"bundle:standalone": "webpack --env.standalone --mode=production",
"bundle:standalone": "webpack --env production --env standalone --mode=production",
"bundle:lib": "webpack --mode=production && npm run declarations",
"bundle": "npm run bundle:clean && npm run bundle:lib && npm run bundle:standalone",
"declarations": "tsc --emitDeclarationOnly -p tsconfig.lib.json && cp -R src/types typings/",
"stats": "webpack --env.standalone --json --profile --mode=production > stats.json",
"stats": "webpack --env production --env standalone --json --profile --mode=production > stats.json",
"prettier": "prettier --write \"cli/index.ts\" \"src/**/*.{ts,tsx}\"",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1",
"lint": "eslint 'src/**/*.{js,ts,tsx}'",
"benchmark": "node ./benchmark/benchmark.js",
"start:demo": "webpack-dev-server --hot --config demo/webpack.config.ts --mode=development",
"start:demo": "webpack serve --hot --config demo/webpack.config.ts --mode=development",
"compile:cli": "tsc custom.d.ts cli/index.ts --target es6 --module commonjs --types yargs",
"build:demo": "webpack --mode=production --config demo/webpack.config.ts",
"deploy:demo": "aws s3 sync demo/dist s3://production-redoc-demo --acl=public-read",
@ -54,10 +58,17 @@
},
"devDependencies": {
"@babel/core": "^7.14.3",
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-proposal-decorators": "^7.14.2",
"@babel/plugin-proposal-object-rest-spread": "^7.14.4",
"@babel/plugin-syntax-decorators": "^7.12.13",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-jsx": "^7.10.4",
"@babel/plugin-syntax-typescript": "^7.10.4",
"@babel/plugin-transform-runtime": "^7.14.3",
"@babel/preset-env": "^7.14.4",
"@babel/preset-react": "^7.13.13",
"@babel/preset-typescript": "^7.13.0",
"@cypress/webpack-preprocessor": "^5.9.0",
"@hot-loader/react-dom": "^17.0.1",
"@types/chai": "^4.2.18",
@ -77,18 +88,18 @@
"@types/react-tabs": "^2.3.2",
"@types/styled-components": "^5.1.1",
"@types/tapable": "^2.2.2",
"@types/webpack": "^4.41.21",
"@types/webpack-env": "^1.15.2",
"@types/webpack": "^5.28.0",
"@types/webpack-env": "^1.16.0",
"@types/yargs": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.1",
"babel-loader": "^8.1.0",
"babel-loader": "^8.2.2",
"babel-plugin-styled-components": "^1.12.0",
"beautify-benchmark": "^0.2.4",
"bundlesize": "^0.18.1",
"conventional-changelog-cli": "^2.0.34",
"copy-webpack-plugin": "^6.0.3",
"copy-webpack-plugin": "^9.0.0",
"core-js": "^3.13.1",
"coveralls": "^3.1.0",
"css-loader": "^5.2.6",
@ -99,7 +110,7 @@
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-react": "^7.24.0",
"fork-ts-checker-webpack-plugin": "^6.2.10",
"html-webpack-plugin": "^4.3.0",
"html-webpack-plugin": "^5.3.1",
"jest": "^27.0.3",
"js-yaml": "^4.1.0",
"license-checker": "^25.0.1",
@ -112,21 +123,19 @@
"react-hot-loader": "^4.13.0",
"rimraf": "^3.0.2",
"shelljs": "^0.8.4",
"source-map-loader": "^1.0.0",
"style-loader": "^2.0.0",
"styled-components": "^5.3.0",
"ts-jest": "^27.0.2",
"ts-loader": "^8.0.1",
"ts-node": "^10.0.0",
"tslib": "^2.2.0",
"typescript": "~4.1.0",
"unfetch": "^4.2.0",
"url-polyfill": "^1.1.12",
"webpack": "^4.44.0",
"webpack-cli": "^3.3.12",
"webpack": "^5.38.1",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2",
"webpack-node-externals": "^2.5.0",
"workerize-loader": "^1.3.0"
"webpack-node-externals": "^3.0.0",
"workerize-loader": "github:redocly/workerize-loader#webpack-5-dist"
},
"peerDependencies": {
"core-js": "^3.1.4",
@ -136,7 +145,8 @@
"styled-components": "^4.1.1 || ^5.1.1"
},
"dependencies": {
"@redocly/openapi-core": "^1.0.0-beta.48",
"@babel/runtime": "^7.14.0",
"@redocly/openapi-core": "^1.0.0-beta.50",
"@redocly/react-dropdown-aria": "^2.0.11",
"@types/node": "^15.6.1",
"classnames": "^2.3.1",
@ -150,21 +160,21 @@
"memoize-one": "^5.2.1",
"mobx-react": "^7.2.0",
"openapi-sampler": "^1.0.1",
"path-browserify": "^1.0.1",
"perfect-scrollbar": "^1.5.1",
"polished": "^4.1.3",
"prismjs": "^1.23.0",
"prop-types": "^15.7.2",
"react-tabs": "^3.2.2",
"slugify": "^1.5.3",
"slugify": "~1.4.7",
"stickyfill": "^1.1.1",
"swagger2openapi": "^7.0.6",
"tslib": "^2.2.0",
"url-template": "^2.0.8"
},
"bundlesize": [
{
"path": "./bundles/redoc.standalone.js",
"maxSize": "300 kB"
"maxSize": "350 kB"
}
],
"jest": {

View File

@ -1,12 +1,12 @@
import * as React from 'react';
import { StoreConsumer } from '../components/StoreBuilder';
import { StoreContext } from '../components/StoreBuilder';
import styled, { css } from '../styled-components';
import { HistoryService } from '../services';
// tslint:disable-next-line
export const linkifyMixin = className => css`
export const linkifyMixin = (className) => css`
${className} {
cursor: pointer;
margin-left: -20px;
@ -33,36 +33,41 @@ export const linkifyMixin = className => css`
}
`;
const isModifiedEvent = event =>
const isModifiedEvent = (event) =>
!!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
export class Link extends React.Component<{ to: string; className?: string; children?: any }> {
navigate = (history: HistoryService, event) => {
if (
!event.defaultPrevented && // onClick prevented default
event.button === 0 && // ignore everything but left clicks
!isModifiedEvent(event) // ignore clicks with modifier keys
) {
event.preventDefault();
history.replace(this.props.to);
}
};
export function Link(props: { to: string; className?: string; children?: any }) {
const store = React.useContext(StoreContext);
const clickHandler = React.useCallback(
(event) => {
if (!store) return;
navigate(store.menu.history, event);
},
[store],
);
render() {
return (
<StoreConsumer>
{store => (
<a
className={this.props.className}
href={store!.menu.history.linkForId(this.props.to)}
onClick={this.navigate.bind(this, store!.menu.history)}
aria-label={this.props.to}
>
{this.props.children}
</a>
)}
</StoreConsumer>
);
if (!store) return null;
return (
<a
className={props.className}
href={store!.menu.history.linkForId(props.to)}
onClick={clickHandler}
aria-label={props.to}
>
{props.children}
</a>
);
}
function navigate(history: HistoryService, event) {
if (
!event.defaultPrevented && // onClick prevented default
event.button === 0 && // ignore everything but left clicks
!isModifiedEvent(event) // ignore clicks with modifier keys
) {
event.preventDefault();
history.replace(this.props.to);
}
}

View File

@ -1,4 +1,3 @@
import * as PropTypes from 'prop-types';
import * as React from 'react';
import { RedocNormalizedOptions, RedocRawOptions } from '../services/RedocNormalizedOptions';
@ -14,47 +13,23 @@ export interface RedocStandaloneProps {
onLoaded?: (e?: Error) => any;
}
export class RedocStandalone extends React.PureComponent<RedocStandaloneProps> {
static propTypes = {
spec: (props, _, componentName) => {
if (!props.spec && !props.specUrl) {
return new Error(
`One of props 'spec' or 'specUrl' was not specified in '${componentName}'.`,
);
}
return null;
},
export const RedocStandalone = function (props: RedocStandaloneProps) {
const { spec, specUrl, options = {}, onLoaded } = props;
const hideLoading = options.hideLoading !== undefined;
specUrl: (props, _, componentName) => {
if (!props.spec && !props.specUrl) {
return new Error(
`One of props 'spec' or 'specUrl' was not specified in '${componentName}'.`,
);
}
return null;
},
options: PropTypes.any,
onLoaded: PropTypes.any,
};
const normalizedOpts = new RedocNormalizedOptions(options);
render() {
const { spec, specUrl, options = {}, onLoaded } = this.props;
const hideLoading = options.hideLoading !== undefined;
const normalizedOpts = new RedocNormalizedOptions(options);
return (
<ErrorBoundary>
<StoreBuilder spec={spec} specUrl={specUrl} options={options} onLoaded={onLoaded}>
{({ loading, store }) =>
!loading ? (
<Redoc store={store!} />
) : hideLoading ? null : (
<Loading color={normalizedOpts.theme.colors.primary.main} />
)
}
</StoreBuilder>
</ErrorBoundary>
);
}
return (
<ErrorBoundary>
<StoreBuilder spec={spec} specUrl={specUrl} options={options} onLoaded={onLoaded}>
{({ loading, store }) =>
!loading ? (
<Redoc store={store!} />
) : hideLoading ? null : (
<Loading color={normalizedOpts.theme.colors.primary.main} />
)
}
</StoreBuilder>
</ErrorBoundary>
);
}

View File

@ -1,5 +1,5 @@
import * as memoize from 'memoize-one/dist/memoize-one.cjs'; // fixme: https://github.com/alexreardon/memoize-one/issues/37
import { Component, createContext } from 'react';
import * as React from 'react';
import { createContext } from 'react';
import { AppStore } from '../services/';
import { RedocRawOptions } from '../services/RedocNormalizedOptions';
@ -14,7 +14,7 @@ export interface StoreBuilderProps {
onLoaded?: (e?: Error) => void;
children: (props: { loading: boolean; store?: AppStore }) => any;
children: (props: { loading: boolean; store: AppStore | null }) => any;
}
export interface StoreBuilderState {
@ -25,79 +25,41 @@ export interface StoreBuilderState {
prevSpecUrl?: string;
}
const { Provider, Consumer } = createContext<AppStore | undefined>(undefined);
export { Provider as StoreProvider, Consumer as StoreConsumer };
const StoreContext = createContext<AppStore | undefined>(undefined);
const { Provider, Consumer } = StoreContext;
export { Provider as StoreProvider, Consumer as StoreConsumer, StoreContext };
export class StoreBuilder extends Component<StoreBuilderProps, StoreBuilderState> {
static getDerivedStateFromProps(nextProps: StoreBuilderProps, prevState: StoreBuilderState) {
if (nextProps.specUrl !== prevState.prevSpecUrl || nextProps.spec !== prevState.prevSpec) {
return {
loading: true,
resolvedSpec: null,
prevSpec: nextProps.spec,
prevSpecUrl: nextProps.specUrl,
};
export function StoreBuilder(props: StoreBuilderProps) {
const {spec, specUrl, options, onLoaded, children } = props;
const [resolvedSpec, setResolvedSpec] = React.useState<any>(null);
React.useEffect(() => {
async function load() {
if (!spec && !specUrl) {
return undefined;
}
setResolvedSpec(null);
const resolved = await loadAndBundleSpec(spec || specUrl!);
setResolvedSpec(resolved);
}
load();
}, [spec, specUrl])
return null;
}
state: StoreBuilderState = {
loading: true,
resolvedSpec: null,
};
@memoize
makeStore(spec, specUrl, options) {
if (!spec) {
return undefined;
}
const store = React.useMemo(() => {
if (!resolvedSpec) return null;
try {
return new AppStore(spec, specUrl, options);
return new AppStore(resolvedSpec, specUrl, options);
} catch (e) {
if (this.props.onLoaded) {
this.props.onLoaded(e);
if (onLoaded) {
onLoaded(e);
}
throw e;
}
}
}, [resolvedSpec, specUrl, options]);
componentDidMount() {
this.load();
}
componentDidUpdate() {
if (this.state.resolvedSpec === null) {
this.load();
} else if (!this.state.loading && this.props.onLoaded) {
// may run multiple time
this.props.onLoaded();
}
}
async load() {
const { specUrl, spec } = this.props;
try {
const resolvedSpec = await loadAndBundleSpec(spec || specUrl!);
this.setState({ resolvedSpec, loading: false });
} catch (e) {
if (this.props.onLoaded) {
this.props.onLoaded(e);
}
this.setState({ error: e });
}
}
render() {
if (this.state.error) {
throw this.state.error;
}
const { specUrl, options } = this.props;
const { loading, resolvedSpec } = this.state;
return this.props.children({
loading,
store: this.makeStore(resolvedSpec, specUrl, options),
});
}
return children({
loading: !store,
store,
});
}

View File

@ -6,9 +6,9 @@ export {
Section,
StyledDropdown,
SimpleDropdown,
DropdownOption,
} from './common-elements/';
export { OpenAPIEncoding } from './types';
export type { DropdownOption } from './common-elements';
export type { OpenAPIEncoding } from './types';
export * from './services';
export * from './utils';

View File

@ -1,15 +1,2 @@
import 'core-js/es/promise';
import 'core-js/es/array/find';
import 'core-js/es/array/includes';
import 'core-js/es/object/assign';
import 'core-js/es/object/entries';
import 'core-js/es/object/is';
import 'core-js/es/string/ends-with';
import 'core-js/es/string/starts-with';
import 'core-js/es/map';
import 'core-js/es/symbol';
import 'unfetch/polyfill/index';
import 'url-polyfill';
import 'core-js/es/symbol';

View File

@ -1,8 +1,8 @@
import * as styledComponents from 'styled-components';
import { ResolvedThemeInterface } from './theme';
import type { ResolvedThemeInterface } from './theme';
export { ResolvedThemeInterface };
export type { ResolvedThemeInterface };
const {
default: styled,

View File

@ -1,4 +1,10 @@
import { Source, Document, bundle, Config } from '@redocly/openapi-core';
import type { Source, Document } from '@redocly/openapi-core';
// eslint-disable-next-line import/no-internal-modules
import { bundle } from '@redocly/openapi-core/lib/bundle';
// eslint-disable-next-line import/no-internal-modules
import { Config } from '@redocly/openapi-core/lib/config/config';
/* tslint:disable-next-line:no-implicit-dependencies */
import { convertObj } from 'swagger2openapi';
import { OpenAPISpec } from '../types';

View File

@ -2,6 +2,7 @@
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
import * as webpack from 'webpack';
import * as path from 'path';
import { getBabelLoader, webpackIgnore } from './config/webpack-utils';
const nodeExternals = require('webpack-node-externals')({
// bundle in modules that need transpiling + non-js (e.g. css)
@ -31,7 +32,7 @@ const BANNER = `ReDoc - OpenAPI/Swagger-generated API Reference Documentation
Version: ${VERSION}
Repo: https://github.com/Redocly/redoc`;
export default (env: { standalone?: boolean } = {}, { mode }) => ({
export default (env: { standalone?: boolean } = {}) => ({
entry: env.standalone ? ['./src/polyfills.ts', './src/standalone.tsx'] : './src/index.ts',
output: {
filename: env.standalone ? 'redoc.standalone.js' : 'redoc.lib.js',
@ -42,18 +43,20 @@ export default (env: { standalone?: boolean } = {}, { mode }) => ({
},
devtool: 'source-map',
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
},
node: {
fs: 'empty',
extensions: ['.ts', '.tsx', '.js', '.mjs', '.json'],
fallback: {
path: require.resolve('path-browserify'),
http: false,
fs: false,
os: false,
}
},
performance: false,
optimization: {
minimize: !!env.standalone,
},
// target: 'node',
externalsPresets: env.standalone ? {} : { node: true },
externals: env.standalone
? {
esprima: 'esprima',
esprima: 'null',
'node-fetch': 'null',
'node-fetch-h2': 'null',
yaml: 'null',
@ -61,7 +64,7 @@ export default (env: { standalone?: boolean } = {}, { mode }) => ({
}
: (context, request, callback) => {
// ignore node-fetch dep of swagger2openapi as it is not used
if (/esprima|node-fetch|node-fetch-h2|yaml|safe-json-stringify$/i.test(request)) {
if (/esprima|node-fetch|node-fetch-h2|\/yaml|safe-json-stringify$/i.test(request)) {
return callback(null, 'var undefined');
}
return nodeExternals(context, request, callback);
@ -70,51 +73,22 @@ export default (env: { standalone?: boolean } = {}, { mode }) => ({
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
compilerOptions: {
module: 'es2015',
declaration: 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',
[
'babel-plugin-styled-components',
{
minify: true,
displayName: mode !== 'production',
},
],
],
},
},
],
exclude: [/node_modules/],
},
{
test: /node_modules\/(swagger2openapi|reftools|oas-resolver|oas-kit-common|oas-schema-walker)\/.*\.js$/,
use: {
loader: 'ts-loader',
options: {
instance: 'ts2js-transpiler-only',
transpileOnly: true,
compilerOptions: {
allowJs: true,
declaration: false,
},
test: /\.(tsx?|[cm]?js)$/,
use: [getBabelLoader({useBuiltIns: !!env.standalone})],
exclude: {
and: [/node_modules/],
not: {
or: [
/swagger2openapi/,
/reftools/,
/openapi-sampler/,
/mobx/,
/oas-resolver/,
/oas-kit-common/,
/oas-schema-walker/,
/\@redocly\/openapi-core/,
/colorette/,
],
},
},
},
@ -127,22 +101,19 @@ export default (env: { standalone?: boolean } = {}, { mode }) => ({
},
},
},
{ enforce: 'pre', test: /\.js$/, loader: 'source-map-loader' },
],
},
plugins: [
new webpack.DefinePlugin({
__REDOC_VERSION__: VERSION,
__REDOC_REVISION__: REVISION,
'process.env': '{}',
'process.platform': '"browser"',
'process.stdout': 'null',
}),
new ForkTsCheckerWebpackPlugin({ logger: { infrastructure: 'silent', issues: 'console' } }),
new webpack.BannerPlugin(BANNER),
ignore(/js-yaml\/dumper\.js$/),
ignore(/json-schema-ref-parser\/lib\/dereference\.js/),
env.standalone ? ignore(/^\.\/SearchWorker\.worker$/) : ignore(/$non-existing^/),
],
webpackIgnore(/js-yaml\/dumper\.js$/),
env.standalone ? webpackIgnore(/^\.\/SearchWorker\.worker$/) : undefined,
].filter(Boolean),
});
function ignore(regexp) {
return new webpack.NormalModuleReplacementPlugin(regexp, require.resolve('lodash/noop.js'));
}