Skip to content

Commit

Permalink
update tsp-openapi3 automatic doc newlines logic (#3839)
Browse files Browse the repository at this point in the history
Fixes #3835 

`tsp-openapi3` will no longer automatically attempt to wrap
documentation strings based on maximum column width.

It will still honor any line breaks that exist in the original
documentation when generating doc strings.

---------

Co-authored-by: Christopher Radek <Christopher.Radek@microsoft.com>
  • Loading branch information
chrisradek and Christopher Radek committed Jul 15, 2024
1 parent 0d85077 commit 65a0288
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@typespec/openapi3"
---

Updates tsp-openapi3 doc line wrapping to only automatically create newlines when they are present in the original documentation.
47 changes: 10 additions & 37 deletions packages/openapi3/src/cli/actions/convert/utils/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,30 @@ export function generateDocs(doc: string | string[]): string {
return ``;
}

const wrapped = lineWrap(doc);
const split = splitNewlines(doc);

for (let i = 0; i < wrapped.length; i++) {
if (wrapped[i].includes("@") || wrapped[i].includes("*/")) {
if (wrapped.length === 1) {
return `@doc("${wrapped[0].replace(/\\/g, "\\\\").replace(/"/g, '\\"')}")`;
for (let i = 0; i < split.length; i++) {
if (split[i].includes("@") || split[i].includes("*/")) {
if (split.length === 1) {
return `@doc("${split[0].replace(/\\/g, "\\\\").replace(/"/g, '\\"')}")`;
}
return `@doc("""\n${wrapped.join("\n").replace(/\\/g, "\\\\").replace(/"/g, '\\"')}\n""")`;
return `@doc("""\n${split.join("\n").replace(/\\/g, "\\\\").replace(/"/g, '\\"')}\n""")`;
}
}

return `/**\n* ${wrapped.join("\n* ")}\n*/`;
return `/**\n* ${split.join("\n* ")}\n*/`;
}

export function generateDocsContent(doc: string | string[]): string {
if (isEmptyDoc(doc)) {
return ``;
}

const wrapped = lineWrap(doc);
return wrapped.length === 1 ? `${wrapped[0]}` : `""\n${wrapped.join("\n")}\n""`;
}

function lineWrap(doc: string | string[]): string[] {
const maxLength = 80;

function splitNewlines(doc: string | string[]): string[] {
let docString = Array.isArray(doc) ? doc.join("\n") : doc;
docString = docString.replace(/\r\n/g, "\n");
docString = docString.replace(/\r/g, "\n");

if (docString.length <= maxLength && !docString.includes("\n")) {
if (!docString.includes("\n")) {
return [docString];
}

const oriLines = docString.split("\n");
const lines: string[] = [];
for (const oriLine of oriLines) {
const words = oriLine.split(" ");
let line = ``;
for (const word of words) {
if (word.length + 1 > maxLength - line.length) {
lines.push(line.substring(0, line.length - 1));
line = `${word} `;
} else {
line = `${line}${word} `;
}
}
lines.push(`${line.substring(0, line.length - 1)}`);
}

return lines;
return docString.split("\n");
}

function isEmptyDoc(doc?: string | string[]): doc is undefined {
Expand Down
84 changes: 84 additions & 0 deletions packages/openapi3/test/tsp-openapi3/docs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { strictEqual } from "assert";
import { it } from "vitest";
import { generateDocs } from "../../src/cli/actions/convert/utils/docs.js";

it("returns empty string for empty docs", () => {
strictEqual(generateDocs(""), "");
strictEqual(generateDocs([]), "");
});

it("returns single line doc", () => {
strictEqual(
generateDocs("Hello, World!"),
`
/**
* Hello, World!
*/
`.trim()
);
});

it("returns multi-line doc", () => {
strictEqual(
generateDocs(["Hello,", "World!"]),
`
/**
* Hello,
* World!
*/`.trim()
);
});

it("returns multi-line docs when they contain newline characters", () => {
strictEqual(
generateDocs("Hello,\nWorld!"),
`
/**
* Hello,
* World!
*/`.trim()
);
});

it("does not automatically apply line-wrapping for very long lines", () => {
const longLine = "This is a long line".repeat(20); // 380 characters
strictEqual(
generateDocs(longLine),
`
/**
* ${longLine}
*/`.trim()
);
});

it("handles different newline breaks", () => {
const scenarios = ["Hello,\nWorld!", "Hello,\rWorld!", "Hello,\r\nWorld!"];

for (const scenario of scenarios) {
strictEqual(
generateDocs(scenario),
`
/**
* Hello,
* World!
*/`.trim()
);
}
});

it("uses doc generator for @ and */", () => {
strictEqual(generateDocs("Hello, @World!"), `@doc("Hello, @World!")`);

strictEqual(generateDocs("Hello, */World!"), `@doc("Hello, */World!")`);
});

it("supports multi-line with decorator", () => {
strictEqual(
generateDocs(["Hello,", "@World!"]),
`
@doc("""
Hello,
@World!
""")`.trim()
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,14 @@ using Http;
using OpenAPI;

/**
* This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You
* can find out more about
* Swagger at [http://swagger.io](http://swagger.io). In the third iteration of
* the pet store, we've switched to the design first approach!
* You can now help us improve the API whether it's by making changes to the
* definition itself or to the code.
* That way, with time, we can improve the API in general, and expose some of the
* new features in OAS3.
* This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
* Swagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!
* You can now help us improve the API whether it's by making changes to the definition itself or to the code.
* That way, with time, we can improve the API in general, and expose some of the new features in OAS3.
*
* Some useful links:
* - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)
* - [The source API definition for the Pet
* Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
* - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
*/
@service({
title: "Swagger Petstore - OpenAPI 3.0",
Expand Down Expand Up @@ -548,8 +543,7 @@ op findPetsByStatus(
): findPetsByStatus200ApplicationXmlResponse | findPetsByStatus200ApplicationJsonResponse | findPetsByStatus400Response;

/**
* Multiple tags can be provided with comma separated strings. Use tag1, tag2,
* tag3 for testing.
* Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
*/
@tag("pet")
@route("/pet/findByTags")
Expand Down Expand Up @@ -653,8 +647,7 @@ op placeOrder(
): placeOrder200ApplicationJsonResponse | placeOrder405Response;

/**
* For valid response try integer IDs with value < 1000. Anything above 1000 or
* nonintegers will generate API errors
* For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
*/
@tag("store")
@extension("x-swagger-router-controller", "OrderController")
Expand All @@ -668,8 +661,7 @@ op deleteOrder(
): deleteOrder400Response | deleteOrder404Response;

/**
* For valid response try integer IDs with value <= 5 or > 10. Other values will
* generate exceptions.
* For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.
*/
@tag("store")
@extension("x-swagger-router-controller", "OrderController")
Expand Down

0 comments on commit 65a0288

Please sign in to comment.