Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve types, doc, allow spec on ApiSchema, fix showExample #339

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,8 @@
"printWidth": 80,
"tabWidth": 2
},
"packageManager": "[email protected]"
"packageManager": "[email protected]",
"volta": {
"node": "18.12.0"
}
rohit-gohri marked this conversation as resolved.
Show resolved Hide resolved
}
18 changes: 11 additions & 7 deletions packages/docusaurus-plugin-redoc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
PluginDirectUsageOptions,
DEFAULT_OPTIONS,
} from './options';
import type { SpecProps, ApiDocProps } from './types/common';
import type { SpecDataResult, ApiDocProps } from './types/common';
import { loadSpecWithConfig } from './loadSpec';
import { loadRedoclyConfig } from './loadRedoclyConfig';

Expand All @@ -33,6 +33,10 @@ const version = require('../package.json').version;

export { PluginOptions, PluginDirectUsageOptions, loadRedoclyConfig };

function getIsExternalUrl(url = '') {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this still doesn't handle relative urls

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really want to handle relative URL (inside the doc) as external API ?

I don't get the usecase.

Either you use RedocStandalone if the schema is outside of the doc, either you use the real Redoc and it handles it by loading it

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It supports it currently, so gotta keep it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... I didn't think it was intented.

It's more a miss-usage of the API

return ['http://', 'https://'].some((protocol) => url.startsWith(protocol));
}

export default function redocPlugin(
context: LoadContext,
opts: PluginOptions,
Expand All @@ -45,12 +49,13 @@ export default function redocPlugin(
const { debug, spec, url: downloadUrl, config, themeId } = options;

let url = downloadUrl;
const isSpecFile = fs.existsSync(spec);
const isExternalUrl = getIsExternalUrl(url);

const fileName = path.join(
'redocusaurus',
`${options.id || 'api-spec'}.yaml`,
);
let filesToWatch: string[] = isSpecFile ? [path.resolve(spec)] : [];
let filesToWatch: string[] = !isExternalUrl ? [path.resolve(spec)] : [];

if (debug) {
console.error('[REDOCUSAURUS_PLUGIN] Opts Input:', opts);
Expand All @@ -67,7 +72,7 @@ export default function redocPlugin(

let bundledSpec: Document, problems: NormalizedProblem[];

if (!isSpecFile) {
if (isExternalUrl) {
// If spec is a remote url then add it as download url also as a default
url = url || spec;
if (debug) {
Expand Down Expand Up @@ -123,10 +128,9 @@ export default function redocPlugin(
throw new Error(`[Redocusaurus] Spec could not be parsed: ${spec}`);
}

const data: SpecProps = {
const data: SpecDataResult = {
url,
themeId,
isSpecFile,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
spec: content.converted as any,
};
Expand Down Expand Up @@ -165,7 +169,7 @@ export default function redocPlugin(
}
},
async postBuild({ content }) {
if (!isSpecFile || downloadUrl) {
if (isExternalUrl || downloadUrl) {
return;
}
// Create a static file from bundled spec
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useMemo } from 'react';
import Redoc from '@theme/Redoc';
import useSpecData from '@theme/useSpecData';
import { MdxProps as Props } from '../../types/common';
import { MdxProps } from '../../types/common';
import '../ApiSchema/styles.css';

const ApiDocMdx: React.FC<Props> = ({ id }: Props): JSX.Element => {
const ApiDocMdx: React.FC<MdxProps> = ({ id }: MdxProps): JSX.Element => {
const specProps = useSpecData(id);
const optionsOverrides = useMemo(() => {
return {
Expand Down
28 changes: 17 additions & 11 deletions packages/docusaurus-theme-redoc/src/theme/ApiSchema/ApiSchema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ import clsx from 'clsx';
import { ThemeProvider } from 'styled-components';
import '../../global';
import { SchemaDefinition } from 'redoc';
import { useSpec } from '../../utils/useSpec';
import { useSpecData } from '../useSpecData';
import { ApiSchemaProps as Props } from '../../types/common';
import useSpec from '@theme/useSpec';
import '../Redoc/styles.css';
import './styles.css';

const ApiSchema: React.FC<Props> = ({
id,
example,
const ApiSchema: React.FC<ApiSchemaProps> = ({
serut marked this conversation as resolved.
Show resolved Hide resolved
showExample,
rohit-gohri marked this conversation as resolved.
Show resolved Hide resolved
pointer,
id,
spec,
optionsOverrides,
...rest
}: Props): JSX.Element => {
const specProps = useSpecData(id);
const { store } = useSpec(specProps);
}: ApiSchemaProps): JSX.Element => {
const { store } = useSpec(
{
id,
spec,
},
optionsOverrides,
);

useEffect(() => {
/**
Expand All @@ -31,13 +36,14 @@ const ApiSchema: React.FC<Props> = ({
className={clsx([
'redocusaurus',
'redocusaurus-schema',
example ? null : 'hide-example',
showExample ? null : 'hide-example',
])}
>
<SchemaDefinition
parser={store.spec.parser}
options={store.options}
schemaRef={pointer}
showExample={showExample}
{...rest}
/>
</div>
Expand All @@ -46,7 +52,7 @@ const ApiSchema: React.FC<Props> = ({
};

ApiSchema.defaultProps = {
example: false,
showExample: false,
};

export default ApiSchema;
23 changes: 10 additions & 13 deletions packages/docusaurus-theme-redoc/src/theme/Redoc/Redoc.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
import React from 'react';
import clsx from 'clsx';
import '../../global';
import { RedocStandalone, RedocRawOptions } from 'redoc';
import { SpecProps } from '../../types/common';
import { useSpecOptions } from '../../utils/useSpecOptions';
import { RedocStandalone } from 'redoc';
import useSpecOptions from '@theme/useSpecOptions';
import './styles.css';
import ServerRedoc from './ServerRedoc';

function getIsExternalUrl(url = '') {
return ['http://', 'https://'].some((protocol) => url.startsWith(protocol));
}

/*!
* Redocusaurus
* https://redocusaurus.vercel.app/
* (c) 2024 Rohit Gohri
* Released under the MIT License
*/
function Redoc(
props: Partial<SpecProps> & {
className?: string;
optionsOverrides?: RedocRawOptions;
},
): JSX.Element {
const { className, optionsOverrides, spec, url, themeId, isSpecFile } = props;
function Redoc(props: RedocProps): JSX.Element {
const { className, optionsOverrides, url, themeId } = props;
const { options } = useSpecOptions(themeId, optionsOverrides);
const isDevMode = process.env.NODE_ENV === 'development';

if ((isDevMode && isSpecFile === false) || !spec) {
if (getIsExternalUrl(url)) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not what we want, external urls should still be parsed at build time for server side rendering

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, maybe I miss the usecase... What is the difference between running in client mode and server side rendering mode for props in this component ?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difference is if the html is generated at build time (and hence works for SEO and works without javascript) or on client side

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but in terms of props, what's the difference ?

return (
<div className={clsx(['redocusaurus', className])}>
<RedocStandalone specUrl={url} options={options} />
</div>
);
}

return <ServerRedoc {...props} spec={spec} />;
return <ServerRedoc {...props} />;
}

export default Redoc;
25 changes: 12 additions & 13 deletions packages/docusaurus-theme-redoc/src/theme/Redoc/ServerRedoc.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from 'react';
import clsx from 'clsx';
import '../../global';
import { Redoc as RedocComponent, RedocRawOptions } from 'redoc';
import { SpecProps } from '../../types/common';
import { useSpec } from '../../utils/useSpec';
import { Redoc as RedocComponent } from 'redoc';
import useSpec from '@theme/useSpec';
import { ServerStyles } from './Styles';
import './styles.css';

Expand All @@ -13,22 +12,22 @@ import './styles.css';
* (c) 2024 Rohit Gohri
* Released under the MIT License
*/
function ServerRedoc(
props: SpecProps & {
className?: string;
optionsOverrides?: RedocRawOptions;
},
): JSX.Element {
const { className, optionsOverrides, ...specProps } = props;
const { store, darkThemeOptions, lightThemeOptions, hasLogo } = useSpec(
specProps,
function ServerRedoc(props: RedocProps): JSX.Element {
const { className, optionsOverrides, url, id, themeId } = props;
const { store, spec, darkThemeOptions, lightThemeOptions, hasLogo } = useSpec(
{
spec: props.spec,
themeId,
id,
},
optionsOverrides,
);

return (
<>
<ServerStyles
specProps={specProps}
spec={spec}
url={url}
lightThemeOptions={lightThemeOptions}
darkThemeOptions={darkThemeOptions}
/>
Expand Down
23 changes: 14 additions & 9 deletions packages/docusaurus-theme-redoc/src/theme/Redoc/ServerStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import '../../global';
import useBaseUrl from '@docusaurus/useBaseUrl';
import { AppStore, Redoc, RedocRawOptions } from 'redoc';
import type { OpenAPISpec } from 'redoc/typings/types';
// eslint-disable-next-line import/no-extraneous-dependencies
import { renderToString } from 'react-dom/server';
import { ServerStyleSheet } from 'styled-components';
Expand Down Expand Up @@ -53,22 +54,26 @@ const prefixCssSelectors = function (rules: string, className: string) {
const LIGHT_MODE_PREFIX = "html:not([data-theme='dark'])";
const DARK_MODE_PREFIX = "html([data-theme='dark'])";

export type ServerStylesProps = {
spec: OpenAPISpec;
url?: string;
lightThemeOptions: RedocRawOptions;
darkThemeOptions: RedocRawOptions;
};

export function ServerStyles({
specProps,
spec,
url,
lightThemeOptions,
darkThemeOptions,
}: {
specProps: SpecProps;
lightThemeOptions: RedocRawOptions;
darkThemeOptions: RedocRawOptions;
}) {
const fullUrl = useBaseUrl(specProps.url, { absolute: true });
}: ServerStylesProps) {
const fullUrl = useBaseUrl(url, { absolute: true });
const css = {
light: '',
dark: '',
};
const lightSheet = new ServerStyleSheet();
const lightStore = new AppStore(specProps.spec, fullUrl, lightThemeOptions);
const lightStore = new AppStore(spec, fullUrl, lightThemeOptions);
renderToString(
lightSheet.collectStyles(React.createElement(Redoc, { store: lightStore })),
);
Expand All @@ -78,7 +83,7 @@ export function ServerStyles({
css.light = prefixCssSelectors(lightCss, LIGHT_MODE_PREFIX);

const darkSheet = new ServerStyleSheet();
const darkStore = new AppStore(specProps.spec, fullUrl, darkThemeOptions);
const darkStore = new AppStore(spec, fullUrl, darkThemeOptions);
renderToString(
darkSheet.collectStyles(React.createElement(Redoc, { store: darkStore })),
);
Expand Down
8 changes: 2 additions & 6 deletions packages/docusaurus-theme-redoc/src/theme/Redoc/Styles.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import React from 'react';
import '../../global';
import type { RedocRawOptions } from 'redoc';
import type { ServerStylesProps } from './ServerStyles';

/**
* Don't hydrate/replace server styles
* @see https://github.com/facebook/react/issues/10923#issuecomment-338715787
*/
export function ServerStyles(_props: {
specProps: SpecProps;
lightThemeOptions: RedocRawOptions;
darkThemeOptions: RedocRawOptions;
}) {
export function ServerStyles(_props: ServerStylesProps) {
return <div className="redocusaurus-styles"></div>;
}
3 changes: 3 additions & 0 deletions packages/docusaurus-theme-redoc/src/theme/useSpec/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { useSpec } from './useSpec';

export default useSpec;
serut marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { useMemo, useEffect } from 'react';
import useBaseUrl from '@docusaurus/useBaseUrl';
import useIsBrowser from '@docusaurus/useIsBrowser';
import { useColorMode } from '@docusaurus/theme-common';
import '../global';
import useSpecData from '@theme/useSpecData';
import useSpecOptions from '@theme/useSpecOptions';
import '../../global';
import { AppStore, RedocRawOptions } from 'redoc';
import { SpecProps } from '../types/common';
import { useSpecOptions } from './useSpecOptions';

// the current store singleton in the app's instance
let currentStore: AppStore | null = null;
Expand All @@ -17,9 +17,14 @@ let currentStore: AppStore | null = null;
* Released under the MIT License
*/
export function useSpec(
{ spec, url, themeId }: SpecProps,
specInfo: SpecProps,
optionsOverrides?: RedocRawOptions,
) {
): SpecResult {
const { spec, url, themeId } = useSpecData(
specInfo.id,
specInfo.spec,
specInfo.themeId,
);
const specOptions = useSpecOptions(themeId, optionsOverrides);
const fullUrl = useBaseUrl(url, { absolute: true });
const isBrowser = useIsBrowser();
Expand All @@ -36,6 +41,7 @@ export function useSpec(
// @ts-expect-error extra prop
hasLogo: !!spec.info?.['x-logo'],
store: currentStore,
spec,
};
}, [isBrowser, spec, fullUrl, specOptions]);

Expand Down
18 changes: 0 additions & 18 deletions packages/docusaurus-theme-redoc/src/theme/useSpecData.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { useSpecData } from './useSpecData';

export default useSpecData;
Loading
Loading