Skip to content

Commit 53c8da2

Browse files
authored
feat: Add --full-description option to include full comment in schema (#2224)
1 parent b07e6a3 commit 53c8da2

File tree

10 files changed

+686
-33
lines changed

10 files changed

+686
-33
lines changed

README.md

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ Extended version of [https://github.com/xiag-ag/typescript-to-json-schema](https
88

99
Inspired by [`YousefED/typescript-json-schema`](https://github.com/YousefED/typescript-json-schema). Here's the differences list:
1010

11-
- this implementation avoids the use of `typeChecker.getTypeAtLocation()` (so probably it keeps correct type aliases)
12-
- processing AST and formatting JSON schema have been split into two independent steps
13-
- not exported types, interfaces, enums are not exposed in the `definitions` section in the JSON schema
11+
- this implementation avoids the use of `typeChecker.getTypeAtLocation()` (so probably it keeps correct type aliases)
12+
- processing AST and formatting JSON schema have been split into two independent steps
13+
- not exported types, interfaces, enums are not exposed in the `definitions` section in the JSON schema
1414

1515
## Contributors
1616

@@ -47,6 +47,7 @@ By default, the command-line generator will use the `tsconfig.json` file in the
4747
-e, --expose <expose> Type exposing (choices: "all", "none", "export", default: "export")
4848
-j, --jsDoc <extended> Read JsDoc annotations (choices: "none", "basic", "extended", default: "extended")
4949
--markdown-description Generate `markdownDescription` in addition to `description`.
50+
--full-description Include the full raw JSDoc comment as `fullDescription` in the schema.
5051
--functions <functions> How to handle functions. `fail` will throw an error. `comment` will add a comment. `hide` will treat the function like a NeverType or HiddenType.
5152
(choices: "fail", "comment", "hide", default: "comment")
5253
--minify Minify generated schema (default: false)
@@ -221,20 +222,20 @@ fs.writeFile(outputPath, schemaString, (err) => {
221222

222223
## Current state
223224

224-
- `interface` types
225-
- `enum` types
226-
- `union`, `tuple`, `type[]` types
227-
- `Date`, `RegExp`, `URL` types
228-
- `string`, `boolean`, `number` types
229-
- `"value"`, `123`, `true`, `false`, `null`, `undefined` literals
230-
- type aliases
231-
- generics
232-
- `typeof`
233-
- `keyof`
234-
- conditional types
235-
- functions
236-
- `Promise<T>` unwraps to `T`
237-
- Overrides (like `@format`)
225+
- `interface` types
226+
- `enum` types
227+
- `union`, `tuple`, `type[]` types
228+
- `Date`, `RegExp`, `URL` types
229+
- `string`, `boolean`, `number` types
230+
- `"value"`, `123`, `true`, `false`, `null`, `undefined` literals
231+
- type aliases
232+
- generics
233+
- `typeof`
234+
- `keyof`
235+
- conditional types
236+
- functions
237+
- `Promise<T>` unwraps to `T`
238+
- Overrides (like `@format`)
238239

239240
## Run locally
240241

@@ -252,7 +253,7 @@ And connect via the debugger protocol.
252253

253254
Publishing is handled by a 2-branch [pre-release process](https://intuit.github.io/auto/docs/generated/shipit#next-branch-default), configured in `publish-auto.yml`. All changes should be based off the default `next` branch, and are published automatically.
254255

255-
- PRs made into the default branch are auto-deployed to the `next` pre-release tag on NPM. The result can be installed with `npm install ts-json-schema-generator@next`
256-
- When merging into `next`, please use the `squash and merge` strategy.
257-
- To release a new stable version, open a PR from `next` into `stable` using this [compare link](https://github.com/vega/ts-json-schema-generator/compare/stable...next).
258-
- When merging from `next` into `stable`, please use the `create a merge commit` strategy.
256+
- PRs made into the default branch are auto-deployed to the `next` pre-release tag on NPM. The result can be installed with `npm install ts-json-schema-generator@next`
257+
- When merging into `next`, please use the `squash and merge` strategy.
258+
- To release a new stable version, open a PR from `next` into `stable` using this [compare link](https://github.com/vega/ts-json-schema-generator/compare/stable...next).
259+
- When merging from `next` into `stable`, please use the `create a merge commit` strategy.

factory/parser.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,12 @@ export function createParser(program: ts.Program, config: CompletedConfig, augme
7979
if (config.jsDoc === "extended") {
8080
return new AnnotatedNodeParser(
8181
nodeParser,
82-
new ExtendedAnnotationsReader(typeChecker, extraTags, config.markdownDescription),
82+
new ExtendedAnnotationsReader(
83+
typeChecker,
84+
extraTags,
85+
config.markdownDescription,
86+
config.fullDescription,
87+
),
8388
);
8489
} else if (config.jsDoc === "basic") {
8590
return new AnnotatedNodeParser(nodeParser, new BasicAnnotationsReader(extraTags));

src/AnnotationsReader/ExtendedAnnotationsReader.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import json5 from "json5";
22
import type ts from "typescript";
33
import type { Annotations } from "../Type/AnnotatedType.js";
44
import { symbolAtNode } from "../Utils/symbolAtNode.js";
5+
import { getFullDescription } from "../Utils/getFullDescription.js";
56
import { BasicAnnotationsReader } from "./BasicAnnotationsReader.js";
67

78
export class ExtendedAnnotationsReader extends BasicAnnotationsReader {
89
public constructor(
910
private typeChecker: ts.TypeChecker,
1011
extraTags?: Set<string>,
1112
private markdownDescription?: boolean,
13+
private fullDescription?: boolean,
1214
) {
1315
super(extraTags);
1416
}
@@ -44,21 +46,34 @@ export class ExtendedAnnotationsReader extends BasicAnnotationsReader {
4446
return undefined;
4547
}
4648

49+
const annotations: { description?: string; markdownDescription?: string; fullDescription?: string } = {};
50+
4751
const comments: ts.SymbolDisplayPart[] = symbol.getDocumentationComment(this.typeChecker);
48-
if (!comments || !comments.length) {
49-
return undefined;
50-
}
5152

52-
const markdownDescription = comments
53-
.map((comment) => comment.text)
54-
.join(" ")
55-
.replace(/\r/g, "")
56-
.trim();
53+
if (comments && comments.length) {
54+
const markdownDescription = comments
55+
.map((comment) => comment.text)
56+
.join(" ")
57+
.replace(/\r/g, "")
58+
.trim();
5759

58-
const description = markdownDescription.replace(/(?<=[^\n])\n(?=[^\n*-])/g, " ").trim();
60+
annotations.description = markdownDescription.replace(/(?<=[^\n])\n(?=[^\n*-])/g, " ").trim();
61+
62+
if (this.markdownDescription) {
63+
annotations.markdownDescription = markdownDescription;
64+
}
65+
}
5966

60-
return this.markdownDescription ? { description, markdownDescription } : { description };
67+
if (this.fullDescription) {
68+
const fullDescription = getFullDescription(node)?.trim();
69+
if (fullDescription) {
70+
annotations.fullDescription = fullDescription;
71+
}
72+
}
73+
74+
return Object.keys(annotations).length ? annotations : undefined;
6175
}
76+
6277
private getTypeAnnotation(node: ts.Node): Annotations | undefined {
6378
const symbol = symbolAtNode(node);
6479
if (!symbol) {

src/Config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface Config {
88
topRef?: boolean;
99
jsDoc?: "none" | "extended" | "basic";
1010
markdownDescription?: boolean;
11+
fullDescription?: boolean;
1112
sortProps?: boolean;
1213
strictTuples?: boolean;
1314
skipTypeCheck?: boolean;
@@ -27,6 +28,7 @@ export const DEFAULT_CONFIG: Omit<Required<Config>, "path" | "type" | "schemaId"
2728
topRef: true,
2829
jsDoc: "extended",
2930
markdownDescription: false,
31+
fullDescription: false,
3032
sortProps: true,
3133
strictTuples: false,
3234
skipTypeCheck: false,

src/Utils/getFullDescription.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import ts from "typescript";
2+
3+
export function getFullDescription(node: ts.Node): string | undefined {
4+
const sourceFile = node.getSourceFile();
5+
const jsDocNodes = ts.getJSDocCommentsAndTags(node);
6+
7+
if (!jsDocNodes || jsDocNodes.length === 0) {
8+
return undefined;
9+
}
10+
11+
let rawText = "";
12+
13+
for (const jsDoc of jsDocNodes) {
14+
rawText += jsDoc.getFullText(sourceFile) + "\n";
15+
}
16+
17+
rawText = rawText.trim();
18+
19+
return getTextWithoutStars(rawText).trim();
20+
}
21+
22+
function getTextWithoutStars(inputText: string) {
23+
const innerTextWithStars = inputText.replace(/^\/\*\*[^\S\n]*\n?/, "").replace(/(\r?\n)?[^\S\n]*\*\/$/, "");
24+
25+
return innerTextWithStars
26+
.split(/\n/)
27+
.map((line) => {
28+
const trimmedLine = line.trimStart();
29+
30+
if (trimmedLine[0] !== "*") {
31+
return line;
32+
}
33+
34+
const textStartPos = trimmedLine[1] === " " ? 2 : 1;
35+
36+
return trimmedLine.substring(textStartPos);
37+
})
38+
.join("\n");
39+
}

test/config.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,14 @@ function assertSchema(
6464
expect(typeof actual).toBe("object");
6565
expect(actual).toEqual(expected);
6666

67+
const keywords: string[] = [];
68+
if (config.markdownDescription) keywords.push("markdownDescription");
69+
if (config.fullDescription) keywords.push("fullDescription");
70+
6771
const validator = new Ajv({
6872
// skip full check if we are not encoding refs
6973
validateFormats: config.encodeRefs === false ? undefined : true,
70-
keywords: config.markdownDescription ? ["markdownDescription"] : undefined,
74+
keywords: keywords.length ? keywords : undefined,
7175
});
7276

7377
addFormats(validator);
@@ -341,6 +345,18 @@ describe("config", () => {
341345
markdownDescription: true,
342346
}),
343347
);
348+
it(
349+
"full-description",
350+
assertSchema("full-description", {
351+
type: "MyObject",
352+
expose: "export",
353+
topRef: false,
354+
jsDoc: "extended",
355+
sortProps: true,
356+
markdownDescription: true,
357+
fullDescription: true,
358+
}),
359+
);
344360
it(
345361
"tsconfig-support",
346362
assertSchema(

0 commit comments

Comments
 (0)