Skip to content

feat: new jsdoc comments formatter #797

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions src/transform/schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { GlobalContext } from "../types";
import { comment, nodeType, tsArrayOf, tsIntersectionOf, tsPartial, tsReadonly, tsTupleOf, tsUnionOf } from "../utils";
import {
prepareComment,
nodeType,
tsArrayOf,
tsIntersectionOf,
tsPartial,
tsReadonly,
tsTupleOf,
tsUnionOf,
} from "../utils";

interface TransformSchemaObjOptions extends GlobalContext {
required: Set<string>;
Expand All @@ -18,11 +27,9 @@ export function transformSchemaObjMap(obj: Record<string, any>, options: Transfo
for (const k of Object.keys(obj)) {
const v = obj[k];

// 1. JSDoc comment (goes above property)
let schemaComment = "";
if (v.deprecated) schemaComment += `@deprecated `;
if (v.description) schemaComment += v.description;
if (schemaComment) output += comment(schemaComment);
// 1. Add comment in jsdoc notation
const comment = prepareComment(v);
if (comment) output += comment;

// 2. name (with “?” if optional property)
const readonly = tsReadonly(options.immutableTypes);
Expand Down
37 changes: 37 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,42 @@
import { OpenAPI2, OpenAPI3, ReferenceObject } from "./types";

type CommentObject = {
title?: string; // not jsdoc
format?: string; // not jsdoc
deprecated?: boolean; // jsdoc without value
description?: string; // jsdoc with value
default?: string; // jsdoc with value
example?: string; // jsdoc with value
};

/**
* Preparing comments from fields
* @see {comment} for output examples
* @returns void if not comments or jsdoc format comment string
*/
export function prepareComment(v: CommentObject): string | void {
const commentsArray: Array<string> = [];

// * Not JSDOC tags: [title, format]
if (v.title) commentsArray.push(`${v.title} `);
if (v.format) commentsArray.push(`Format: ${v.format} `);

// * JSDOC tags without value
// 'Deprecated' without value
if (v.deprecated) commentsArray.push(`@deprecated `);

// * JSDOC tags with value
const supportedJsDocTags: Array<keyof CommentObject> = ["description", "default", "example"];
for (let index = 0; index < supportedJsDocTags.length; index++) {
const field = supportedJsDocTags[index];
if (v[field]) commentsArray.push(`@${field} ${v[field]} `);
}

if (!commentsArray.length) return;

return comment(commentsArray.join("\n"));
}

export function comment(text: string): string {
const commentText = text.trim().replace(/\*\//g, "*\\/");

Expand Down
150 changes: 150 additions & 0 deletions tests/v3/expected/jsdoc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/**
* This file was auto-generated by openapi-typescript.
* Do not make direct changes to the file.
*/

export interface paths {
"/contacts": {
get: operations["getContacts"];
};
"/contacts/{userUid}": {
get: operations["getContactInfo"];
};
"/contacts/{userUid}/icon": {
get: operations["getContactIcon"];
};
"/contacts/me": {
get: operations["getMyInfo"];
};
"/contacts/me/icon": {
get: operations["getMyIcon"];
delete: operations["deleteMyIcon"];
};
}

export interface components {
schemas: {
/** Parent of most important objects */
BaseEntity: {
/**
* Format: nanoid
* @deprecated
* @description Test description with deprecated
* @example njbusD52k6YoRG346tPgD
*/
uid?: string;
/**
* Format: date-time
* @description It's date example
* @example 1999-03-31 15:00:00.000
*/
created_at?: string;
/**
* Format: date-time
* @example 2020-07-10 10:10:00.000
*/
updated_at?: string;
deleted?: boolean;
};
/** Image for preview */
Image: {
/** @example https://shantichat.com/data/V1StGXR8_Z5jdHi6B-myT/white-rabbit.png */
url: string;
/** @example 128 */
width: unknown;
/** @example 128 */
height: unknown;
/** @example LEHV6nWB2yk8pyo0adR*.7kCMdnj */
blurhash?: string;
};
/** User object */
User: components["schemas"]["BaseEntity"] & {
/** @example Thomas A. Anderson */
name?: string;
/**
* @default test
* @example The One
*/
description?: string;
icon?: components["schemas"]["Image"];
/** @example America/Chicago */
timezone?: string;
/**
* Format: date-time
* @example 2020-07-10 15:00:00.000
*/
last_online_at?: string;
};
};
}

export interface operations {
getContacts: {
responses: {
/** OK */
200: {
content: {
"application/json": components["schemas"]["User"][];
};
};
};
};
getContactInfo: {
parameters: {
path: {
userUid: string;
};
};
responses: {
/** OK */
200: {
content: {
"application/json": components["schemas"]["User"];
};
};
};
};
getContactIcon: {
parameters: {
path: {
userUid: string;
};
};
responses: {
/** OK */
200: {
content: {
"application/json": components["schemas"]["Image"];
};
};
};
};
getMyInfo: {
responses: {
/** OK */
200: {
content: {
"application/json": components["schemas"]["User"];
};
};
};
};
getMyIcon: {
responses: {
/** OK */
200: {
content: {
"application/json": components["schemas"]["Image"];
};
};
};
};
deleteMyIcon: {
responses: {
/** OK */
200: unknown;
};
};
}

export interface external {}
Loading