@redocly/portal-plugin-async-api
Version:
Async API plugin for Redocly products
107 lines (91 loc) • 3.62 kB
text/typescript
import * as asyncApi from '@asyncapi/parser';
import { AvroSchemaParser } from '@asyncapi/avro-schema-parser';
import path from 'node:path';
import type { AsyncAPIDocumentInterface } from '@asyncapi/parser';
import type { PageStaticData } from '@redocly/realm/dist/shared/types';
import type { Feature } from '@redocly/realm/dist/server/entitlements/entitlements.types';
import type {
GetStaticDataFn,
PageRouteDetails,
ExternalPlugin,
} from '@redocly/realm/dist/server/plugins/types';
import type { AsyncApiDocsSettings } from './config';
import { asyncApiDocsConfigSchema } from './config.js';
const ASYNCAPI_TEMPLATE_ID = 'asyncapi-docs';
const ASYNCAPI_SHARED_DATA_PREFIX = 'asyncapi-docs-';
export default function asyncAPIDocsPlugin(): ExternalPlugin {
const parser = new asyncApi.Parser();
parser.registerSchemaParser(AvroSchemaParser());
return {
id: 'asyncapi',
requiredEntitlements: ['asyncapi' as Feature],
redoclyConfigSchema: asyncApiDocsConfigSchema as any,
loaders: {
asyncapi: async (relativePath, { fs, cache, logger }) => {
const absolutePath = path.join(fs.cwd, relativePath);
const { data: yaml } = await cache.load<any>(relativePath, 'yaml');
if (!yaml.asyncapi) return undefined;
const { document, diagnostics } = await asyncApi.fromFile(parser, absolutePath).parse();
diagnostics
.filter((d) => d.severity === 0)
.forEach(({ message, path }) =>
logger.error(
`Cannot parse AsyncAPI schema: ${message} in '${relativePath}:${path.join('.')}'`,
),
);
return document;
},
},
processContent: async (actions, context) => {
const asyncAPITemplateId = actions.createTemplate(
ASYNCAPI_TEMPLATE_ID,
'@redocly/portal-plugin-async-api/template.js',
);
const config = await context.getConfig();
const globalSettings =
(config as { asyncapi: AsyncApiDocsSettings } | undefined)?.asyncapi || {};
for (const record of await context.fs.scan(/(\.ya?ml|\.json)$/)) {
if (await context.isPathIgnored(record.relativePath)) continue;
const { relativePath } = record;
const sharedDataId = `${ASYNCAPI_SHARED_DATA_PREFIX}${relativePath}`;
try {
const { data: document } = await context.cache.load<AsyncAPIDocumentInterface>(
record.realRelativePath,
'asyncapi',
);
if (!document) continue;
await actions.createSharedData(sharedDataId, document?.json() ?? {});
actions.addRoute({
fsPath: relativePath,
templateId: asyncAPITemplateId,
getStaticData: buildGetStaticDataFn(globalSettings, context.withPathPrefix),
metadata: {
type: 'asyncapi',
title: document?.json()?.info?.title ?? 'AsyncAPI',
description: document?.json()?.info?.description ?? '',
...(document?.json()?.info?.['x-metadata'] ?? {}),
},
sharedData: [{ key: 'AsyncApiDefinition', id: sharedDataId }],
});
} catch (e) {
console.error(e);
}
}
},
};
function buildGetStaticDataFn(
settings: AsyncApiDocsSettings,
withPathPrefix: (url: string) => string,
): GetStaticDataFn<PageRouteDetails, PageStaticData> {
return async function (route, _context) {
return {
props: {
settings: {
...settings,
baseUrlPath: withPathPrefix(route.slug),
},
},
};
};
}
}