Skip to content

Commit

Permalink
Auto load folder to generate plugin config (#342)
Browse files Browse the repository at this point in the history
* separate normal options and preset options

* add support for auto-loading folder

* update website config to use folder

* docs(changeset): Add support for auto-loading folder
  • Loading branch information
rohit-gohri authored Mar 8, 2024
1 parent 4dc0db0 commit 646c5e1
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 69 deletions.
6 changes: 6 additions & 0 deletions .changeset/small-pugs-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'docusaurus-plugin-redoc': minor
'redocusaurus': minor
---

Add support for auto-loading folder
9 changes: 3 additions & 6 deletions packages/docusaurus-plugin-redoc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
PluginOptionSchema,
PluginOptions,
PluginOptionsWithDefault,
PluginDirectUsageOptions,
DEFAULT_OPTIONS,
} from './options';
import type { SpecProps, ApiDocProps } from './types/common';
Expand All @@ -30,7 +31,7 @@ import { loadRedoclyConfig } from './loadRedoclyConfig';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const version = require('../package.json').version;

export { PluginOptions, loadRedoclyConfig };
export { PluginOptions, PluginDirectUsageOptions, loadRedoclyConfig };

export default function redocPlugin(
context: LoadContext,
Expand All @@ -41,7 +42,7 @@ export default function redocPlugin(
}> {
const { baseUrl } = context.siteConfig;
const options: PluginOptionsWithDefault = { ...DEFAULT_OPTIONS, ...opts };
const { debug, spec, url: downloadUrl, config } = options;
const { debug, spec, url: downloadUrl, config, themeId } = options;

let url = downloadUrl;
const isSpecFile = fs.existsSync(spec);
Expand All @@ -56,7 +57,6 @@ export default function redocPlugin(
console.error('[REDOCUSAURUS_PLUGIN] Options:', options);
}

const { themeId } = options;
return {
name: 'docusaurus-plugin-redoc',
async loadContent() {
Expand Down Expand Up @@ -185,9 +185,6 @@ export default function redocPlugin(
fs.writeFileSync(staticFile, bundledYaml);
},
getPathsToWatch() {
if (!isSpecFile) {
return [];
}
return filesToWatch;
},
};
Expand Down
34 changes: 22 additions & 12 deletions packages/docusaurus-plugin-redoc/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,47 @@ type LayoutProps = {
};
};

export interface PluginOptions {
id?: string;
spec: string;
url?: string;
route?: string;
layout?: LayoutProps;
/**
* Can pass only if directly using plugin.
* `redocusaurus` auto populates them
*/
export interface PluginDirectUsageOptions {
debug?: boolean;
themeId?: string;
/**
* Redocly config to bundle file
* @see https://redocly.com/docs/cli/configuration/configuration-file/
*/
config?: string | RawConfig;
layout?: LayoutProps;
}

export interface PluginOptions extends PluginDirectUsageOptions {
id?: string;
spec: string;
url?: string;
route?: string;
}

export interface PluginOptionsWithDefault extends PluginOptions {
debug: boolean;
}

export const DEFAULT_OPTIONS: Omit<PluginOptionsWithDefault, 'spec'> = {
export const DEFAULT_OPTIONS = {
layout: {},
debug: false,
};
} satisfies Omit<PluginOptions, 'spec'>;

export const PluginOptionSchema = Joi.object<PluginOptions>({
// Direct Usage without redocusaurus preset
id: Joi.string(),
spec: Joi.string(),
url: Joi.string().uri({ allowRelative: true }).optional(),
layout: Joi.any().default(DEFAULT_OPTIONS.layout),
debug: Joi.boolean().default(DEFAULT_OPTIONS.debug),
route: Joi.string().uri({ relativeOnly: true }).optional(),
config: Joi.any().optional(),
themeId: Joi.string().optional(),

// Basic
spec: Joi.string(),
url: Joi.string().uri({ allowRelative: true }).optional(),
route: Joi.string().uri({ relativeOnly: true }).optional(),
layout: Joi.any().default(DEFAULT_OPTIONS.layout),
});
79 changes: 55 additions & 24 deletions packages/redocusaurus/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,76 @@
import path from 'path';
import type { LoadContext } from '@docusaurus/types';
import { Globby, createSlugger } from '@docusaurus/utils';
import type { PluginOptions } from 'docusaurus-plugin-redoc';
import type { ThemeOptions } from 'docusaurus-theme-redoc';
import type { PresetEntry, PresetOptions, SpecOptions } from './types';

export interface PresetOptions {
id?: string;
debug?: boolean;
/**
* Path to the Redocly config file `redocly.yaml`
*/
config?: string;
specs: PluginOptions[];
theme?: ThemeOptions;
}

export type PresetEntry = ['redocusaurus', PresetOptions];
export type { PresetEntry, PresetOptions };

export default function preset(
export default async function preset(
context: LoadContext,
opts: PresetOptions = {
specs: [],
theme: {},
},
) {
let specsArray: PluginOptions[] = [];
const { debug = false, specs, theme = {}, config } = opts;
const { debug = false, openapi, specs, theme = {}, config } = opts;
if (debug) {
console.error('[REDOCUSAURUS] Options:', opts);
}

if (Array.isArray(specs)) {
specsArray = specs;
} else if (specs) {
specsArray = [specs];
const id = opts.id ? `-${opts.id}` : '';
const themeId = `theme-redoc${id}`;
if (debug) {
console.error('[REDOCUSAURUS] ID Suffix:', id);
}

if (debug) {
console.error('[REDOCUSAURUS] Specs:', specsArray);
const specsArray: SpecOptions[] = [];

if (specs) {
if (debug) {
console.error('[REDOCUSAURUS] Specs Files:', specs);
}
specsArray.push(...(Array.isArray(specs) ? specs : [specs]));
}
const id = opts.id ? `-${opts.id}` : '';
const themeId = `theme-redoc${id}`;
if (!specs || openapi) {
// Load folder if no specs provided or folder specifically provided
const folder = openapi?.folder || 'openapi';
const resolvedFolder = path.resolve(folder);
if (debug) {
console.error('[REDOCUSAURUS] Loading Folder:', {
folder,
resolvedFolder,
});
}
const specFiles = await Globby([
`${folder}/**/*.openapi.{yaml,json}`,
`${folder}/**/openapi.{yaml,json}`,
]);
if (debug) {
console.error('[REDOCUSAURUS] Found openapi files:', specFiles);
}
const slugger = createSlugger();
specsArray.push(
...specFiles.map((specFile): SpecOptions => {
const spec = path.resolve(specFile);
const fileRoute = path
.relative(resolvedFolder, spec)
.replace(/(\/index)?\.openapi\.(yaml|json)$/, '')
.replace(/\/*$/, '');

const docRoute = `${openapi?.routeBasePath ?? ''}/${fileRoute}`;
return {
id: slugger.slug(fileRoute),
spec: spec,
route: docRoute,
};
}),
);
}

if (debug) {
console.error('[REDOCUSAURUS] All specs:', specsArray);
}
const resolvedPreset: {
themes: readonly (readonly [string, ThemeOptions])[];
plugins: readonly (readonly [string, PluginOptions])[];
Expand Down
44 changes: 44 additions & 0 deletions packages/redocusaurus/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type {
PluginOptions,
PluginDirectUsageOptions,
} from 'docusaurus-plugin-redoc';
import type { ThemeOptions } from 'docusaurus-theme-redoc';

interface FolderOptions {
/**
* Default is `openapi`. This is similar to how
* Will load all YAML or JSON files in the folder
* that are named `openapi` or end with `.openapi`, like:
* - `index.openapi.yaml`
* - `swagger.openapi.yaml`
* - `swagger.openapi.json`
*/
folder?: string;
/**
* The path at which the files will be rendered,
* if the file is called `index.openapi.yaml` it will be rendered at base path itself
* Otherwise the name of the file path from the base folder will be used as the url path
*/
routeBasePath?: string;
}

export type SpecOptions = Omit<PluginOptions, keyof PluginDirectUsageOptions>;

export type PresetOptions = {
id?: string;
debug?: boolean;
/**
* Path to the Redocly config file `redocly.yaml`
* @see https://redocly.com/docs/cli/configuration/configuration-file/
*/
config?: string;
specs?: SpecOptions[];
/**
* The folder is not going to be hot-reloaded
* but any changes to specs loaded at start will trigger live-reload
*/
openapi?: FolderOptions;
theme?: Omit<ThemeOptions, 'id'>;
};

export type PresetEntry = ['redocusaurus', PresetOptions];
29 changes: 4 additions & 25 deletions website/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,38 +38,17 @@ const config: Config = {
{
debug: Boolean(process.env.DEBUG || process.env.CI),
config: path.join(__dirname, 'redocly.yaml'),
openapi: {
folder: 'openapi',
routeBasePath: '/examples',
},
specs: [
{
id: 'using-single-yaml',
spec: 'openapi/single-file/openapi.yaml',
route: '/examples/using-single-yaml/',
},
{
id: 'using-multi-file-yaml',
spec: 'openapi/multi-file/openapi.yaml',
route: '/examples/using-multi-file-yaml/',
},
{
id: 'using-swagger-json',
spec: 'openapi/swagger/swagger.json',
route: '/examples/using-swagger-json/',
},
{
id: 'using-remote-url',
// Remote File
spec: 'https://redocly.github.io/redoc/openapi.yaml',
route: '/examples/using-remote-url/',
},
{
id: 'using-custom-page',
spec: 'openapi/single-file/openapi.yaml',
// NOTE: no `route` passed, instead data used in custom React Component ('custom-page/index.jsx')
},
{
id: 'using-custom-layout',
spec: 'openapi/single-file/openapi.yaml',
// NOTE: no `route` passed, instead data used in custom React Component ('custom-layout/index.jsx')
},
],
theme: {
/**
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion website/src/pages/examples/custom-layout/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Redoc from '@theme/Redoc';
import useSpecData from '@theme/useSpecData';

function CustomPage() {
const specData = useSpecData('using-custom-layout');
const specData = useSpecData('using-single-yaml');
return (
<Layout
title="Custom Layout Docs"
Expand Down
2 changes: 1 addition & 1 deletion website/src/pages/examples/custom-page/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ApiDoc from '@theme/ApiDoc';
import useSpecData from '@theme/useSpecData';

function CustomPage() {
const specData = useSpecData('using-custom-page');
const specData = useSpecData('using-single-yaml');

return (
<ApiDoc
Expand Down

0 comments on commit 646c5e1

Please sign in to comment.