chore: export hydrate from standalone build

This commit is contained in:
Roman Hotsiy 2018-03-07 17:14:00 +02:00
parent 8757fa510a
commit 0658b8ea67
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
9 changed files with 48 additions and 23 deletions

2
custom.d.ts vendored
View File

@ -13,7 +13,7 @@ declare module '*.css' {
export default content; export default content;
} }
declare var __DEV__: boolean; declare var __REDOC_DEV__: boolean;
declare var __REDOC_VERSION__: string; declare var __REDOC_VERSION__: string;
declare var __REDOC_REVISION__: string; declare var __REDOC_REVISION__: string;

View File

@ -18,7 +18,7 @@ const server = http.createServer(async (request, response) => {
fs.createReadStream('bundles/redoc.standalone.js', 'utf8').pipe(response); fs.createReadStream('bundles/redoc.standalone.js', 'utf8').pipe(response);
} else if (request.url === '/') { } else if (request.url === '/') {
const spec = yaml.load(readFileSync(resolve(__dirname, '../openapi.yaml'))); const spec = yaml.load(readFileSync(resolve(__dirname, '../openapi.yaml')));
let store = await createStore(spec, '', { nativeScrollbars: true }); let store = await createStore(spec, 'path/to/spec.yaml');
const sheet = new ServerStyleSheet(); const sheet = new ServerStyleSheet();
@ -37,26 +37,21 @@ const server = http.createServer(async (request, response) => {
margin: 0; margin: 0;
} }
</style> </style>
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
<script src="redoc.standalone.js"></script> <script src="redoc.standalone.js"></script>
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
${css} ${css}
</head> </head>
<body> <body>
<div id="redoc">${html}</div> <script>
<script>
const state = ${JSON.stringify(await store.toJS())};
const store = Redoc.AppStore.fromJS(state);
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
console.time('ReDoc hydrate'); const state = ${JSON.stringify(await store.toJS())};
ReactDOM.hydrate(React.createElement(Redoc.Redoc, {store: store}), document.getElementById('redoc')); Redoc.hydrate(state, document.getElementById('redoc'));
console.timeEnd('ReDoc hydrate');
}); });
</script> </script>
<div id="redoc">${html}</div>
</body> </body>
</html>`; </html>`;
response.writeHead(200); response.writeHead(200, { 'Content-Length': res.length });
response.write(res); response.write(res);
response.end(); response.end();
} else { } else {

View File

@ -8,13 +8,13 @@ import { resolve } from 'path';
describe('SSR', () => { describe('SSR', () => {
it('should render in SSR mode', async () => { it('should render in SSR mode', async () => {
(global as any).__DEV__ = true; (global as any).__REDOC_DEV__ = true;
const spec = yaml.load(readFileSync(resolve(__dirname, '../../demo/openapi.yaml'))); const spec = yaml.load(readFileSync(resolve(__dirname, '../../demo/openapi.yaml')));
const store = await createStore(spec, ''); const store = await createStore(spec, '');
expect(() => { expect(() => {
renderToString(<Redoc store={store} />); renderToString(<Redoc store={store} />);
}).not.toThrow(); }).not.toThrow();
delete (global as any).__DEV__; delete (global as any).__REDOC_DEV__;
}); });
}); });

View File

@ -9,7 +9,7 @@ import { RedocNormalizedOptions, RedocRawOptions } from './RedocNormalizedOption
import { ScrollService } from './ScrollService'; import { ScrollService } from './ScrollService';
import { SearchStore } from './SearchStore'; import { SearchStore } from './SearchStore';
interface StoreData { export interface StoreState {
menu: { menu: {
activeItemIdx: number; activeItemIdx: number;
}; };
@ -36,7 +36,7 @@ export class AppStore {
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION** * **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
*/ */
// TODO: // TODO:
static fromJS(state: StoreData): AppStore { static fromJS(state: StoreState): 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);
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]);
@ -114,7 +114,7 @@ export class AppStore {
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION** * **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
*/ */
// TODO: // TODO:
async toJS(): Promise<StoreData> { async toJS(): Promise<StoreState> {
return { return {
menu: { menu: {
activeItemIdx: this.menu.activeItemIdx, activeItemIdx: this.menu.activeItemIdx,

View File

@ -122,7 +122,7 @@ export class OpenAPIParser {
* resets visited enpoints. should be run after * resets visited enpoints. should be run after
*/ */
resetVisited() { resetVisited() {
if (__DEV__) { if (__REDOC_DEV__) {
// check in dev mode // check in dev mode
for (const k in this._refCounter._counter) { for (const k in this._refCounter._counter) {
if (this._refCounter._counter[k] > 0) { if (this._refCounter._counter[k] > 0) {

View File

@ -1,10 +1,12 @@
import * as React from 'react'; import * as React from 'react';
import { render } from 'react-dom'; import { render, hydrate as hydrateComponent } from 'react-dom';
import { RedocStandalone } from './components/RedocStandalone'; import { RedocStandalone, Redoc } from './components/';
import { AppStore, StoreState } from './services/AppStore';
import { querySelector } from './utils/dom'; import { querySelector } from './utils/dom';
import { debugTime, debugTimeEnd } from './utils/debug';
export { Redoc, AppStore } from './index'; export { Redoc, AppStore } from '.';
export const version = __REDOC_VERSION__; export const version = __REDOC_VERSION__;
export const revision = __REDOC_REVISION__; export const revision = __REDOC_REVISION__;
@ -35,6 +37,7 @@ export function init(
specOrSpecUrl: string | any, specOrSpecUrl: string | any,
options: any = {}, options: any = {},
element: Element | null = querySelector('redoc'), element: Element | null = querySelector('redoc'),
callback?: () => void,
) { ) {
if (element === null) { if (element === null) {
throw new Error('"element" argument is not provided and <redoc> tag is not found on the page'); throw new Error('"element" argument is not provided and <redoc> tag is not found on the page');
@ -60,9 +63,24 @@ export function init(
['Loading...'], ['Loading...'],
), ),
element, element,
callback,
); );
} }
export function hydrate(
state: StoreState,
element: Element | null = querySelector('redoc'),
callback?: () => void,
) {
debugTime('Redoc create store');
const store = AppStore.fromJS(state);
debugTimeEnd('Redoc create store');
debugTime('Redoc hydrate');
hydrateComponent(<Redoc store={store} />, element, callback);
debugTimeEnd('Redoc hydrate');
}
/** /**
* autoinit ReDoc if <redoc> tag is found on the page with "spec-url" attr * autoinit ReDoc if <redoc> tag is found on the page with "spec-url" attr
*/ */

11
src/utils/debug.ts Normal file
View File

@ -0,0 +1,11 @@
export function debugTime(label: string) {
if (__REDOC_DEV__) {
console.time(label);
}
}
export function debugTimeEnd(label: string) {
if (__REDOC_DEV__) {
console.timeEnd(label);
}
}

View File

@ -7,3 +7,4 @@ export * from './highlight';
export * from './loadAndBundleSpec'; export * from './loadAndBundleSpec';
export * from './dom'; export * from './dom';
export * from './decorators'; export * from './decorators';
export * from './debug';

View File

@ -108,7 +108,7 @@ export default env => {
'process.env.NODE_ENV': env.prod ? '"production"' : '"development"', 'process.env.NODE_ENV': env.prod ? '"production"' : '"development"',
__REDOC_VERSION__: VERSION, __REDOC_VERSION__: VERSION,
__REDOC_REVISION__: REVISION, __REDOC_REVISION__: REVISION,
__DEV__: env.prod ? 'false' : 'true', __REDOC_DEV__: env.prod ? 'false' : 'true',
}), }),
new webpack.NamedModulesPlugin(), new webpack.NamedModulesPlugin(),
], ],