swagger-jsdoc
Version:
Generates swagger doc based on JSDoc
138 lines (118 loc) • 3.58 kB
JavaScript
import { promises as fs } from 'fs';
import { extname } from 'path';
import glob from 'glob';
/**
* Converts an array of globs to full paths
* @param {array} globs - Array of globs and/or normal paths
* @return {array} Array of fully-qualified paths
*/
export function convertGlobPaths(globs) {
return globs
.map((globString) => glob.sync(globString))
.reduce((previous, current) => previous.concat(current), []);
}
/**
* Checks if there is any properties of the input object which are an empty object
* @param {object} obj - the object to check
* @returns {boolean}
*/
export function hasEmptyProperty(obj) {
if (!obj) return;
return Object.keys(obj)
.map((key) => obj[key])
.every(
(keyObject) =>
typeof keyObject === 'object' &&
Object.keys(keyObject).every((key) => !(key in keyObject))
);
}
/**
* Extracts the YAML description from JSDoc comments with `@swagger`/`@openapi` annotation.
* @param {object} jsDocComment - Single item of JSDoc comments from doctrine.parse
* @returns {array} YAML parts
*/
export function extractYamlFromJsDoc(jsDocComment) {
const yamlParts = [];
for (const tag of jsDocComment.tags) {
if (tag.title === 'swagger' || tag.title === 'openapi') {
yamlParts.push(tag.description);
}
}
return yamlParts;
}
/**
* @param {string} filePath
* @returns {{jsdoc: array, yaml: array}} JSDoc comments and Yaml files
*/
export async function extractAnnotations(filePath, encoding = 'utf8') {
const fileContent = await fs.readFile(filePath, { encoding });
const ext = extname(filePath);
const jsDocRegex = /\/\*\*([\s\S]*?)\*\//gm;
const csDocRegex = /###([\s\S]*?)###/gm;
const yaml = [];
const jsdoc = [];
let regexResults = null;
switch (ext) {
case '.yml':
case '.yaml':
yaml.push(fileContent);
break;
case '.coffee':
regexResults = fileContent.match(csDocRegex) || [];
for (const result of regexResults) {
let part = result.split('###');
part[0] = `/**`;
part[part.length - 1] = '*/';
part = part.join('');
jsdoc.push(part);
}
break;
default: {
regexResults = fileContent.match(jsDocRegex) || [];
for (const result of regexResults) {
jsdoc.push(result);
}
}
}
return { yaml, jsdoc };
}
/**
* @param {object} tag
* @param {string} tag.name
* @param {array} tags
* @returns {boolean}
*/
export function isTagPresentInTags(tag, tags) {
const match = tags.find((targetTag) => tag.name === targetTag.name);
if (match) return true;
return false;
}
/**
* @param {object} options
* @returns {object} the original input if valid, throws otherwise
*/
export function validateOptions(options) {
if (!options) {
throw new Error(`'options' parameter is required!`);
}
if (!options.swaggerDefinition && !options.definition) {
throw new Error(
`'options.swaggerDefinition' or 'options.definition' is required!`
);
}
const def = options.swaggerDefinition || options.definition;
if (!def.info) {
throw new Error(
`Swagger definition ('options.swaggerDefinition') should contain an info object!`
);
}
if (!('title' in def.info) || !('version' in def.info)) {
throw new Error(
`Swagger definition info object ('options.swaggerDefinition.info') requires title and version properties!`
);
}
if (!options.apis || !Array.isArray(options.apis)) {
throw new Error(`'options.apis' is required and it should be an array!`);
}
return options;
}