Skip to content

Commit

Permalink
feat(includers/openapi): rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
moki committed Dec 26, 2022
1 parent b03fbee commit 509dbf9
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 117 deletions.
2 changes: 1 addition & 1 deletion src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export type YfmTocIncluders = YfmTocIncluder[];

export type YfmTocIncluder = YfmTocIncluderName | YfmTocIncluderObject;

export const includersNames = ['sourcedocs'] as const;
export const includersNames = ['sourcedocs', 'openapi'] as const;

export type YfmTocIncluderName = typeof includersNames[number];

Expand Down
2 changes: 1 addition & 1 deletion src/services/includers/batteries/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * as sourcedocs from './sourcedocs';
// export * as openapi from './openapi';
export * as openapi from './openapi';
194 changes: 81 additions & 113 deletions src/services/includers/batteries/openapi/index.ts
Original file line number Diff line number Diff line change
@@ -1,100 +1,71 @@
/* eslint-disable no-shadow */
import {join} from 'path';
import {resolve, join, dirname} from 'path';
import {mkdir, writeFile} from 'fs/promises';

import parser from '@apidevtools/swagger-parser';
import {dump} from 'js-yaml';

import parsers from './parsers';
import generators from './generators';

import {
YfmToc,
IncluderFnParams,
IncluderFnOutput,
IncluderFnOutputElement,
} from '../../../../models';
import {IncluderFunctionParams, YfmToc} from '../../../../models';
import {Endpoint, Info} from './types';

async function generateContent(params: IncluderFnParams): IncluderFnOutput {
const {
include: {path},
root,
} = params;
const name = 'openapi';

const includePath = fixpath(path);
class OpenApiIncluderError extends Error {
path: string;

let data;
constructor(message: string, path: string) {
super(message);

try {
data = await parser.validate(join(root, path), {validate: {spec: true}});
} catch (e) {
throw Error('includer openapi: failed to parse specification');
this.name = 'OpenApiIncluderError';
this.path = path;
}
}

const results = [];
async function includerFunction(params: IncluderFunctionParams) {
const {readBasePath, writeBasePath, tocPath, passedParams: {input, leadingPage}} = params;

const info: Info = parsers.info(data);
const spec = parsers.paths(data, parsers.tags(data));
const tocDirPath = dirname(tocPath);

const main: string = generators.main(info, spec);
const contentPath = resolve(process.cwd(), readBasePath, input);

results.push({
path: join(root, includePath, 'index.md'),
content: main,
});

spec.tags.forEach((tag, id) => {
const path = join(root, includePath, id, 'index.md');
const content = generators.section(tag);

results.push({path, content});
const leadingPageName = leadingPage?.name ?? 'Overview';

const {endpoints} = tag;
if (!endpoints) { return; }

endpoints.forEach((endpoint) => {
results.push(handleEndpointIncluder(endpoint, join(root, includePath, id)));
});
});
let data;

for (const endpoint of spec.endpoints) {
results.push(handleEndpointIncluder(endpoint, join(root, includePath)));
try {
data = await parser.validate(contentPath, {validate: {spec: true}});
} catch (err) {
if (err instanceof Error) {
throw new OpenApiIncluderError(err.toString(), tocPath);
}
}

return results;
}

async function generateTocs(params: IncluderFnParams): IncluderFnOutput {
const {
name,
include: {path},
root,
} = params;

let data;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const writePath = join(writeBasePath, tocDirPath, params.item.include!.path);

try {
data = await parser.validate(join(root, path), {validate: {spec: true}});

} catch (e) {
throw Error('includer openapi: failed to parse specification');
await mkdir(writePath, {recursive: true});
await generateToc(data, writePath, leadingPageName);
await generateContent(data, writePath);
} catch (err) {
if (err instanceof Error) {
throw new OpenApiIncluderError(err.toString(), tocPath);
}
}
}

const includePath = fixpath(path);

const result = {
path: join(root, includePath, 'toc.yaml'),
content: {
name,
href: 'index.yaml',
items: [
async function generateToc(data: any, writePath: string, leadingPageName: string): Promise<any> {
const toc = {
name,
items: [
{
hidden: true,
name: 'index',
name: leadingPageName,
href: 'index.md',
} as unknown as YfmToc,
],
} as YfmToc,
};
],
} as YfmToc;

const {tags, endpoints} = parsers.paths(data, parsers.tags(data));

Expand All @@ -104,8 +75,7 @@ async function generateTocs(params: IncluderFnParams): IncluderFnOutput {
const section: YfmToc = {
name,
items: [{
hidden: true,
name: 'index',
name: leadingPageName,
href: join(id, 'index.md'),
} as unknown as YfmToc],
} as YfmToc;
Expand All @@ -116,49 +86,55 @@ async function generateTocs(params: IncluderFnParams): IncluderFnOutput {
section.items.push(handleEndpointRender(endpoint, id));
});

result.content.items.push(section);
toc.items.push(section);
});

for (const endpoint of endpoints) {
result.content.items.push(handleEndpointRender(endpoint));
toc.items.push(handleEndpointRender(endpoint));
}

return [result];
await mkdir(dirname(writePath), {recursive: true});
await writeFile(join(writePath, 'toc.yaml'), dump(toc));
}

async function generateLeadingPages(
params: IncluderFnParams,
): IncluderFnOutput {
const {
name,
include: {path},
root,
} = params;

const includePath = fixpath(path);

return [
{
path: join(root, includePath, 'index.yaml'),
content: {
title: name,
links: [
{title: 'index', href: 'index.md'},
],
},
},
];
}
async function generateContent(data: any, writePath: string): Promise<void> {
const results = [];

async function generatePath(params: IncluderFnParams): Promise<string> {
return join(fixpath(params.include.path), 'toc.yaml');
}
const info: Info = parsers.info(data);
const spec = parsers.paths(data, parsers.tags(data));

function fixpath(path: string) {
return path.replace(/\.[^.]+$/gmu, '');
const main: string = generators.main(info, spec);

results.push({
path: join(writePath, 'index.md'),
content: main,
});

spec.tags.forEach((tag, id) => {
const path = join(writePath, id, 'index.md');
const content = generators.section(tag);

results.push({path, content});

const {endpoints} = tag;
if (!endpoints) { return; }

endpoints.forEach((endpoint) => {
results.push(handleEndpointIncluder(endpoint, join(writePath, id)));
});
});

for (const endpoint of spec.endpoints) {
results.push(handleEndpointIncluder(endpoint, join(writePath)));
}

for (const {path, content} of results) {
await mkdir(dirname(path), {recursive: true});
await writeFile(path, content);
}
}

function handleEndpointIncluder(endpoint: Endpoint, pathPrefix: string): IncluderFnOutputElement {
function handleEndpointIncluder(endpoint: Endpoint, pathPrefix: string) {
const path = join(pathPrefix, mdPath(endpoint));
const content = generators.endpoint(endpoint);

Expand All @@ -184,12 +160,4 @@ export function mdPath(e: Endpoint): string {
return `${e.id}.md`;
}

const name = 'openapi';

export {
name,
generateTocs,
generateContent,
generateLeadingPages,
generatePath,
};
export {name, includerFunction};
4 changes: 2 additions & 2 deletions src/services/includers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {isObject} from 'lodash';
import {ArgvService} from '../index';
import {IncludeMode} from '../../constants';
import {logger} from '../../utils/logger';
import {sourcedocs} from './batteries';
import {sourcedocs, openapi} from './batteries';

import type {
Includer,
Expand Down Expand Up @@ -51,7 +51,7 @@ class IncludersError extends Error {
function init(custom: Includer[] = []) {
if (includersMap) { return; }

includersMap = {sourcedocs};
includersMap = {sourcedocs, openapi};

for (const includer of custom) {
includersMap[includer.name] = includer;
Expand Down

0 comments on commit 509dbf9

Please sign in to comment.