Skip to content

Commit

Permalink
Add support for multiple yamls, uses openapi to bundle yamls (#148)
Browse files Browse the repository at this point in the history
  • Loading branch information
rohit-gohri authored Mar 13, 2022
1 parent 937e73a commit ad66f92
Show file tree
Hide file tree
Showing 8 changed files with 1,325 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/new-poems-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'docusaurus-plugin-redoc': minor
---

Add support for bundling multiple yamls that reference each other
4 changes: 2 additions & 2 deletions packages/docusaurus-plugin-redoc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
"dependencies": {
"@docusaurus/types": "^2.0.0-beta.17",
"@docusaurus/utils": "^2.0.0-beta.17",
"@redocly/openapi-core": "^1.0.0-beta.87",
"joi": "^17.5.0",
"redoc": "^2.0.0-rc.64",
"yaml": "^1.10.2"
"redoc": "^2.0.0-rc.64"
},
"engines": {
"node": ">=14"
Expand Down
101 changes: 75 additions & 26 deletions packages/docusaurus-plugin-redoc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,25 @@ import type {
OptionValidationContext,
} from '@docusaurus/types';
import { normalizeUrl } from '@docusaurus/utils';
import YAML from 'yaml';
import { loadAndBundleSpec } from 'redoc';
import {
formatProblems,
getTotals,
Config,
bundle,
loadConfig,
stringifyYaml,
} from '@redocly/openapi-core';

import {
PluginOptionSchema,
PluginOptions,
PluginOptionsWithDefault,
DEFAULT_OPTIONS,
} from './options';
import { ParsedSpec, SpecProps, ApiDocProps } from './types/common';
import { SpecProps, ApiDocProps } from './types/common';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const version = require('../package.json').version;

export { PluginOptions };

Expand All @@ -25,50 +34,69 @@ export default function redocPlugin(
): Plugin<Record<string, unknown>> {
const { baseUrl } = context.siteConfig;
const options: PluginOptionsWithDefault = { ...DEFAULT_OPTIONS, ...opts };
const { debug, spec, url: downloadUrl } = options;
const { debug, spec, url: downloadUrl, config } = options;

let url = downloadUrl;
const isSpecFile = fs.existsSync(spec);
const fileName = path.join(
'redocusaurus',
`${options.id || 'api-spec'}.yaml`,
);

if (debug) {
console.error('[REDOCUSAURUS_PLUGIN] Opts Input:', opts);
console.error('[REDOCUSAURUS_PLUGIN] Options:', options);
}
return {
name: 'docusaurus-plugin-redoc',
async loadContent() {
let parsedSpec: ParsedSpec | null = null;
// If local file
if (fs.existsSync(spec)) {
if (!isSpecFile) {
// If spec is a remote url then add it as download url also as a default
url = url || spec;
if (debug) {
console.log('[REDOCUSAURUS_PLUGIN] reading file: ', spec);
console.log('[REDOCUSAURUS_PLUGIN] bundling spec from url', spec);
}
return loadAndBundleSpec(spec!);
}

// If local file
if (debug) {
console.log('[REDOCUSAURUS_PLUGIN] reading file: ', spec);
}

const file = fs.readFileSync(spec).toString();
let redoclyConfig: Config;

if (spec.endsWith('.yaml') || spec.endsWith('.yml')) {
parsedSpec = YAML.parse(file);
} else parsedSpec = JSON.parse(file);
if (config) {
if (typeof config === 'string') {
redoclyConfig = await loadConfig(config);
} else {
redoclyConfig = new Config(config);
}
} else {
// If spec is a remote url then add it as download url
url = spec;
redoclyConfig = await loadConfig();
}

if (debug) {
console.log('[REDOCUSAURUS_PLUGIN] bundling spec');
const { bundle: bundledSpec, problems } = await bundle({
ref: spec,
config: redoclyConfig,
});

if (problems?.length) {
console.error('[REDOCUSAURUS_PLUGIN] errors while bundling spec', spec);

formatProblems(problems, {
totals: getTotals(problems),
version,
});
}
const content = await loadAndBundleSpec(parsedSpec || spec!);

if (debug) {
console.log('[REDOCUSAURUS_PLUGIN] Content loaded');
console.log('[REDOCUSAURUS_PLUGIN] File Bundled');
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return content as any;
},
getPathsToWatch() {
if (!spec) {
return [];
}
const contentPath = path.resolve(context.siteDir, spec);
return [contentPath];
// If download url is not provided then use bundled yaml as a static file (see `postBuild`)
url = url || fileName;
return bundledSpec.parsed;
},
async contentLoaded({ content, actions }) {
const { createData, addRoute, setGlobalData } = actions;
Expand Down Expand Up @@ -114,6 +142,27 @@ export default function redocPlugin(
addRoute(routeOptions);
}
},
async postBuild(props) {
if (!isSpecFile || downloadUrl) {
return;
}
// Create a static file from bundled spec
const staticFile = path.join(context.outDir, fileName);
fs.mkdirSync(path.dirname(staticFile));
console.error(
'[REDOCUSAURUS_PLUGIN] creating static bundle copy for download',
staticFile,
);
// create bundled url
const bundledYaml = stringifyYaml(props.content);
fs.writeFileSync(staticFile, bundledYaml);
},
getPathsToWatch() {
if (!isSpecFile) {
return [];
}
return [path.resolve(spec)];
},
};
}

Expand Down
7 changes: 7 additions & 0 deletions packages/docusaurus-plugin-redoc/src/options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Joi from 'joi';
import { RawConfig } from '@redocly/openapi-core';

type LayoutProps = {
title?: string;
Expand All @@ -21,6 +22,11 @@ export interface PluginOptions {
route?: string;
layout?: LayoutProps;
debug?: boolean;
/**
* Redocly config to bundle file
* @see https://redocly.com/docs/cli/configuration/configuration-file/
*/
config?: string | Partial<RawConfig>;
}

export interface PluginOptionsWithDefault extends PluginOptions {
Expand All @@ -39,4 +45,5 @@ export const PluginOptionSchema = Joi.object<PluginOptions>({
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(),
});
10 changes: 10 additions & 0 deletions website/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ const redocusaurus = [
url: `/openapi-page.yaml`,
route: '/examples/using-spec-yaml/',
},
{
id: 'using-multi-file-yaml',
// Local File
spec: 'examples/multi-yaml/index.yaml',
route: '/examples/using-multi-file-yaml/',
},
{
id: 'using-spec-url',
// Remote File
Expand Down Expand Up @@ -119,6 +125,10 @@ const config = {
label: 'Custom Layout',
to: '/examples/custom-layout/',
},
{
label: 'Using Multiple YAMLs',
to: '/examples/using-multi-file-yaml/',
},
{
label: 'Using Spec URL',
to: '/examples/using-spec-url/',
Expand Down
Loading

0 comments on commit ad66f92

Please sign in to comment.