1 | import * as _ from 'lodash';
|
2 | import { ts, SyntaxKind } from 'ts-simple-ast';
|
3 |
|
4 | import { JSDocParameterTagExt } from '../app/nodes/jsdoc-parameter-tag.node';
|
5 |
|
6 | export class JsdocParserUtil {
|
7 | public isVariableLike(node: ts.Node): node is ts.VariableLikeDeclaration {
|
8 | if (node) {
|
9 | switch (node.kind) {
|
10 | case SyntaxKind.BindingElement:
|
11 | case SyntaxKind.EnumMember:
|
12 | case SyntaxKind.Parameter:
|
13 | case SyntaxKind.PropertyAssignment:
|
14 | case SyntaxKind.PropertyDeclaration:
|
15 | case SyntaxKind.PropertySignature:
|
16 | case SyntaxKind.ShorthandPropertyAssignment:
|
17 | case SyntaxKind.VariableDeclaration:
|
18 | return true;
|
19 | }
|
20 | }
|
21 | return false;
|
22 | }
|
23 |
|
24 | public getMainCommentOfNode(node: ts.Node): string {
|
25 | let description: string = '';
|
26 | if (node.jsDoc) {
|
27 | if (node.jsDoc.length > 0) {
|
28 | if (typeof node.jsDoc[0].comment !== 'undefined') {
|
29 | description = node.jsDoc[0].comment;
|
30 | }
|
31 | }
|
32 | }
|
33 | return description;
|
34 | }
|
35 |
|
36 | private getJSDocTags(node: ts.Node, kind: SyntaxKind): ts.JSDocTag[] {
|
37 | const docs = this.getJSDocs(node);
|
38 | if (docs) {
|
39 | const result: ts.JSDocTag[] = [];
|
40 | for (const doc of docs) {
|
41 | if (ts.isJSDocParameterTag(doc)) {
|
42 | if (doc.kind === kind) {
|
43 | result.push(doc);
|
44 | }
|
45 | } else if (ts.isJSDoc(doc)) {
|
46 | result.push(..._.filter(doc.tags, tag => tag.kind === kind));
|
47 | } else {
|
48 | throw new Error('Unexpected type');
|
49 | }
|
50 | }
|
51 | return result;
|
52 | }
|
53 | }
|
54 |
|
55 | public getJSDocs(node: ts.Node): ReadonlyArray<ts.JSDoc | ts.JSDocTag> {
|
56 |
|
57 | let cache: ReadonlyArray<ts.JSDoc | ts.JSDocTag> = (node as any).jsDocCache;
|
58 | if (!cache) {
|
59 | cache = this.getJSDocsWorker(node, []).filter(x => x);
|
60 | (node as any).jsDocCache = cache;
|
61 | }
|
62 | return cache;
|
63 | }
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 | private getJSDocsWorker(node: ts.Node, cache): ReadonlyArray<any> {
|
73 | const parent = node.parent;
|
74 | const isInitializerOfVariableDeclarationInStatement =
|
75 | this.isVariableLike(parent) &&
|
76 | parent.initializer === node &&
|
77 | ts.isVariableStatement(parent.parent.parent);
|
78 | const isVariableOfVariableDeclarationStatement =
|
79 | this.isVariableLike(node) && ts.isVariableStatement(parent.parent);
|
80 | const variableStatementNode = isInitializerOfVariableDeclarationInStatement
|
81 | ? parent.parent.parent
|
82 | : isVariableOfVariableDeclarationStatement
|
83 | ? parent.parent
|
84 | : undefined;
|
85 | if (variableStatementNode) {
|
86 | cache = this.getJSDocsWorker(variableStatementNode, cache);
|
87 | }
|
88 |
|
89 |
|
90 | const isSourceOfAssignmentExpressionStatement =
|
91 | parent &&
|
92 | parent.parent &&
|
93 | ts.isBinaryExpression(parent) &&
|
94 | parent.operatorToken.kind === SyntaxKind.EqualsToken &&
|
95 | ts.isExpressionStatement(parent.parent);
|
96 | if (isSourceOfAssignmentExpressionStatement) {
|
97 | cache = this.getJSDocsWorker(parent.parent, cache);
|
98 | }
|
99 |
|
100 | const isModuleDeclaration =
|
101 | ts.isModuleDeclaration(node) && parent && ts.isModuleDeclaration(parent);
|
102 | const isPropertyAssignmentExpression = parent && ts.isPropertyAssignment(parent);
|
103 | if (isModuleDeclaration || isPropertyAssignmentExpression) {
|
104 | cache = this.getJSDocsWorker(parent, cache);
|
105 | }
|
106 |
|
107 |
|
108 | if (ts.isParameter(node)) {
|
109 | cache = _.concat(cache, this.getJSDocParameterTags(node));
|
110 | }
|
111 |
|
112 | if (this.isVariableLike(node) && node.initializer) {
|
113 | cache = _.concat(cache, node.initializer.jsDoc);
|
114 | }
|
115 |
|
116 | cache = _.concat(cache, node.jsDoc);
|
117 |
|
118 | return cache;
|
119 | }
|
120 |
|
121 | private getJSDocParameterTags(
|
122 | param: ts.ParameterDeclaration
|
123 | ): ReadonlyArray<ts.JSDocParameterTag> {
|
124 | const func = param.parent as ts.FunctionLikeDeclaration;
|
125 | const tags = this.getJSDocTags(
|
126 | func,
|
127 | SyntaxKind.JSDocParameterTag
|
128 | ) as ts.JSDocParameterTag[];
|
129 |
|
130 | if (!param.name) {
|
131 |
|
132 | const i = func.parameters.indexOf(param);
|
133 | const paramTags = _.filter(tags, tag => ts.isJSDocParameterTag(tag));
|
134 |
|
135 | if (paramTags && 0 <= i && i < paramTags.length) {
|
136 | return [paramTags[i]];
|
137 | }
|
138 | } else if (ts.isIdentifier(param.name)) {
|
139 | const name = param.name.text;
|
140 | return _.filter(tags, tag => {
|
141 | if (ts && ts.isJSDocParameterTag(tag)) {
|
142 | let t: JSDocParameterTagExt = tag;
|
143 | if (typeof t.parameterName !== 'undefined') {
|
144 | return t.parameterName.text === name;
|
145 | } else if (typeof t.name !== 'undefined') {
|
146 | if (typeof t.name.escapedText !== 'undefined') {
|
147 | return t.name.escapedText === name;
|
148 | }
|
149 | }
|
150 | }
|
151 | });
|
152 | } else {
|
153 |
|
154 |
|
155 | return undefined;
|
156 | }
|
157 | }
|
158 | }
|