@cjser/es-joy__jsdoccomment 0.96.0-cjser.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.md +883 -0
- package/LICENSE-MIT.txt +20 -0
- package/README.md +250 -0
- package/dist/index.d.ts +421 -0
- package/dist-cjser/index.cjs +1224 -0
- package/package.json +144 -0
- package/src/commentHandler.js +48 -0
- package/src/commentParserToESTree.js +525 -0
- package/src/estreeToString.js +234 -0
- package/src/index.js +59 -0
- package/src/inlineTagEscapes.js +73 -0
- package/src/jsdoccomment.js +705 -0
- package/src/parseComment.js +190 -0
- package/src/parseInlineTags.js +114 -0
- package/src/toCamelCase.js +13 -0
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
/* eslint-disable jsdoc/reject-any-type -- Todo */
|
|
2
|
+
/**
|
|
3
|
+
* Obtained originally from {@link https://github.com/eslint/eslint/blob/master/lib/util/source-code.js#L313}.
|
|
4
|
+
*
|
|
5
|
+
* @license MIT
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {import('eslint').AST.Token | import('estree').Comment | {
|
|
10
|
+
* type: import('eslint').AST.TokenType|"Line"|"Block"|"Shebang",
|
|
11
|
+
* range: [number, number],
|
|
12
|
+
* value: string
|
|
13
|
+
* }} Token
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {import('eslint').Rule.Node|
|
|
18
|
+
* import('@typescript-eslint/types').TSESTree.Node} ESLintOrTSNode
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {number} int
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks if the given token is a comment token or not.
|
|
27
|
+
*
|
|
28
|
+
* @param {Token} token - The token to check.
|
|
29
|
+
* @returns {boolean} `true` if the token is a comment token.
|
|
30
|
+
*/
|
|
31
|
+
const isCommentToken = (token) => {
|
|
32
|
+
return ['Line', 'Block', 'Shebang'].includes(token.type);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {(
|
|
37
|
+
* ESLintOrTSNode|
|
|
38
|
+
* import('estree').Comment|
|
|
39
|
+
* import('eslint').Rule.Node & {declaration?: any, decorators?: any[]}
|
|
40
|
+
* )} DecoratedNode
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* @param {DecoratedNode} node
|
|
44
|
+
* @returns {import('@typescript-eslint/types').TSESTree.Decorator|undefined}
|
|
45
|
+
*/
|
|
46
|
+
const getDecorator = (node) => {
|
|
47
|
+
// @ts-expect-error -- Loose checking for decorator presence across node kinds
|
|
48
|
+
return node?.declaration?.decorators?.[0] ||
|
|
49
|
+
// @ts-expect-error -- Loose checking
|
|
50
|
+
node?.decorators?.[0] ||
|
|
51
|
+
// @ts-expect-error -- Loose checking
|
|
52
|
+
node?.parent?.decorators?.[0];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check to see if it is a ES6 export declaration.
|
|
57
|
+
*
|
|
58
|
+
* @param {ESLintOrTSNode} astNode An AST node.
|
|
59
|
+
* @returns {boolean} whether the given node represents an export declaration.
|
|
60
|
+
* @private
|
|
61
|
+
*/
|
|
62
|
+
const looksLikeExport = function (astNode) {
|
|
63
|
+
return [
|
|
64
|
+
'ExportDefaultDeclaration',
|
|
65
|
+
'ExportNamedDeclaration',
|
|
66
|
+
'ExportAllDeclaration',
|
|
67
|
+
'ExportSpecifier'
|
|
68
|
+
].includes(astNode.type);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {ESLintOrTSNode} astNode
|
|
73
|
+
* @returns {ESLintOrTSNode}
|
|
74
|
+
*/
|
|
75
|
+
const getTSFunctionComment = function (astNode) {
|
|
76
|
+
const {parent} = astNode;
|
|
77
|
+
/* v8 ignore next 3 */
|
|
78
|
+
if (!parent) {
|
|
79
|
+
return astNode;
|
|
80
|
+
}
|
|
81
|
+
const grandparent = parent.parent;
|
|
82
|
+
/* v8 ignore next 3 */
|
|
83
|
+
if (!grandparent) {
|
|
84
|
+
return astNode;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (/** @type {ESLintOrTSNode} */ (parent).type !== 'TSTypeAnnotation') {
|
|
88
|
+
if (
|
|
89
|
+
parent.type === 'TSTypeAliasDeclaration' &&
|
|
90
|
+
grandparent.type === 'ExportNamedDeclaration'
|
|
91
|
+
) {
|
|
92
|
+
return grandparent;
|
|
93
|
+
/* v8 ignore start */
|
|
94
|
+
}
|
|
95
|
+
return astNode;
|
|
96
|
+
/* v8 ignore stop */
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const greatGrandparent = grandparent.parent;
|
|
100
|
+
const greatGreatGrandparent = greatGrandparent && greatGrandparent.parent;
|
|
101
|
+
|
|
102
|
+
switch (/** @type {ESLintOrTSNode} */ (grandparent).type) {
|
|
103
|
+
// @ts-expect-error -- For `ClassProperty`.
|
|
104
|
+
case 'PropertyDefinition': case 'ClassProperty':
|
|
105
|
+
case 'TSDeclareFunction':
|
|
106
|
+
case 'TSMethodSignature':
|
|
107
|
+
case 'TSPropertySignature':
|
|
108
|
+
return grandparent;
|
|
109
|
+
case 'ArrowFunctionExpression':
|
|
110
|
+
/* v8 ignore next 3 */
|
|
111
|
+
if (!greatGrandparent) {
|
|
112
|
+
return astNode;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (
|
|
116
|
+
greatGrandparent.type === 'VariableDeclarator'
|
|
117
|
+
|
|
118
|
+
// && greatGreatGrandparent.parent.type === 'VariableDeclaration'
|
|
119
|
+
) {
|
|
120
|
+
/* v8 ignore next 3 */
|
|
121
|
+
if (!greatGreatGrandparent || !greatGreatGrandparent.parent) {
|
|
122
|
+
return astNode;
|
|
123
|
+
}
|
|
124
|
+
return greatGreatGrandparent.parent;
|
|
125
|
+
/* v8 ignore start */
|
|
126
|
+
}
|
|
127
|
+
return astNode;
|
|
128
|
+
/* v8 ignore stop */
|
|
129
|
+
case 'FunctionExpression':
|
|
130
|
+
/* v8 ignore next 3 */
|
|
131
|
+
if (!greatGreatGrandparent) {
|
|
132
|
+
return astNode;
|
|
133
|
+
}
|
|
134
|
+
if (greatGrandparent.type === 'MethodDefinition') {
|
|
135
|
+
return greatGrandparent;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Fallthrough
|
|
139
|
+
default:
|
|
140
|
+
/* v8 ignore next 3 */
|
|
141
|
+
if (grandparent.type !== 'Identifier') {
|
|
142
|
+
return astNode;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* v8 ignore next 3 */
|
|
147
|
+
if (!greatGreatGrandparent) {
|
|
148
|
+
return astNode;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
switch (greatGrandparent.type) {
|
|
152
|
+
case 'ArrowFunctionExpression':
|
|
153
|
+
if (
|
|
154
|
+
greatGreatGrandparent.type === 'VariableDeclarator' &&
|
|
155
|
+
greatGreatGrandparent.parent.type === 'VariableDeclaration'
|
|
156
|
+
) {
|
|
157
|
+
return greatGreatGrandparent.parent;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return astNode;
|
|
161
|
+
case 'FunctionDeclaration':
|
|
162
|
+
return greatGrandparent;
|
|
163
|
+
case 'VariableDeclarator':
|
|
164
|
+
/* v8 ignore next */
|
|
165
|
+
if (greatGreatGrandparent.type === 'VariableDeclaration') {
|
|
166
|
+
return greatGreatGrandparent;
|
|
167
|
+
}
|
|
168
|
+
/* v8 ignore next 2 */
|
|
169
|
+
// Fallthrough
|
|
170
|
+
default:
|
|
171
|
+
/* v8 ignore next 3 */
|
|
172
|
+
return astNode;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const invokedExpression = new Set(
|
|
177
|
+
['CallExpression', 'OptionalCallExpression', 'NewExpression']
|
|
178
|
+
);
|
|
179
|
+
const allowableCommentNode = new Set([
|
|
180
|
+
'AssignmentPattern',
|
|
181
|
+
'VariableDeclaration',
|
|
182
|
+
'ExpressionStatement',
|
|
183
|
+
'MethodDefinition',
|
|
184
|
+
'Property',
|
|
185
|
+
'ObjectProperty',
|
|
186
|
+
'ClassProperty',
|
|
187
|
+
'PropertyDefinition',
|
|
188
|
+
'ExportDefaultDeclaration',
|
|
189
|
+
'ReturnStatement'
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @typedef {{
|
|
194
|
+
* maxLines: int,
|
|
195
|
+
* minLines: int,
|
|
196
|
+
* skipInvokedExpressionsForCommentFinding?: boolean,
|
|
197
|
+
* [name: string]: any
|
|
198
|
+
* }} Settings
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Reduces the provided node to the appropriate node for evaluating
|
|
203
|
+
* JSDoc comment status.
|
|
204
|
+
*
|
|
205
|
+
* @param {ESLintOrTSNode} node An AST node.
|
|
206
|
+
* @param {import('eslint').SourceCode} sourceCode The ESLint SourceCode.
|
|
207
|
+
* @param {Settings} [settings]
|
|
208
|
+
* @returns {ESLintOrTSNode} The AST node that
|
|
209
|
+
* can be evaluated for appropriate JSDoc comments.
|
|
210
|
+
*/
|
|
211
|
+
const getReducedASTNode = function (node, sourceCode, settings) {
|
|
212
|
+
let {parent} = node;
|
|
213
|
+
switch (/** @type {ESLintOrTSNode} */ (node).type) {
|
|
214
|
+
case 'TSFunctionType':
|
|
215
|
+
return getTSFunctionComment(node);
|
|
216
|
+
case 'TSInterfaceDeclaration':
|
|
217
|
+
case 'TSTypeAliasDeclaration':
|
|
218
|
+
case 'TSEnumDeclaration':
|
|
219
|
+
case 'ClassDeclaration':
|
|
220
|
+
case 'FunctionDeclaration':
|
|
221
|
+
/* v8 ignore next 3 */
|
|
222
|
+
if (!parent) {
|
|
223
|
+
return node;
|
|
224
|
+
}
|
|
225
|
+
return looksLikeExport(parent) ? parent : node;
|
|
226
|
+
|
|
227
|
+
case 'TSDeclareFunction':
|
|
228
|
+
case 'ClassExpression':
|
|
229
|
+
case 'ObjectExpression':
|
|
230
|
+
case 'ArrowFunctionExpression':
|
|
231
|
+
case 'TSEmptyBodyFunctionExpression':
|
|
232
|
+
case 'FunctionExpression':
|
|
233
|
+
/* v8 ignore next 3 */
|
|
234
|
+
if (!parent) {
|
|
235
|
+
return node;
|
|
236
|
+
}
|
|
237
|
+
if (
|
|
238
|
+
!invokedExpression.has(parent.type) ||
|
|
239
|
+
settings?.skipInvokedExpressionsForCommentFinding
|
|
240
|
+
) {
|
|
241
|
+
/**
|
|
242
|
+
* @type {ESLintOrTSNode|Token|null}
|
|
243
|
+
*/
|
|
244
|
+
let token = node;
|
|
245
|
+
do {
|
|
246
|
+
token = sourceCode.getTokenBefore(
|
|
247
|
+
/** @type {import('eslint').Rule.Node|import('eslint').AST.Token} */ (
|
|
248
|
+
token
|
|
249
|
+
),
|
|
250
|
+
{includeComments: true}
|
|
251
|
+
);
|
|
252
|
+
} while (token && token.type === 'Punctuator' && token.value === '(');
|
|
253
|
+
if (token && token.type === 'Block') {
|
|
254
|
+
return node;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (sourceCode.getCommentsBefore(
|
|
258
|
+
/** @type {import('eslint').Rule.Node} */
|
|
259
|
+
(node)
|
|
260
|
+
).length) {
|
|
261
|
+
return node;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
while (
|
|
265
|
+
!sourceCode.getCommentsBefore(
|
|
266
|
+
/** @type {import('eslint').Rule.Node} */
|
|
267
|
+
(parent)
|
|
268
|
+
).length &&
|
|
269
|
+
!(/Function/v).test(parent.type) &&
|
|
270
|
+
!allowableCommentNode.has(parent.type)
|
|
271
|
+
) {
|
|
272
|
+
({parent} = parent);
|
|
273
|
+
|
|
274
|
+
if (!parent) {
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (parent && parent.type !== 'FunctionDeclaration' &&
|
|
279
|
+
parent.type !== 'Program'
|
|
280
|
+
) {
|
|
281
|
+
if (parent.parent && parent.parent.type === 'ExportNamedDeclaration') {
|
|
282
|
+
return parent.parent;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return parent;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return node;
|
|
290
|
+
|
|
291
|
+
default:
|
|
292
|
+
return node;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Checks for the presence of a JSDoc comment for the given node and returns it.
|
|
298
|
+
*
|
|
299
|
+
* @param {ESLintOrTSNode} astNode The AST node to get
|
|
300
|
+
* the comment for.
|
|
301
|
+
* @param {import('eslint').SourceCode} sourceCode
|
|
302
|
+
* @param {{maxLines: int, minLines: int, [name: string]: any}} settings
|
|
303
|
+
* @param {{nonJSDoc?: boolean}} [opts]
|
|
304
|
+
* @returns {Token|null} The Block comment token containing the JSDoc comment
|
|
305
|
+
* for the given node or null if not found.
|
|
306
|
+
*/
|
|
307
|
+
const findJSDocComment = (astNode, sourceCode, settings, opts = {}) => {
|
|
308
|
+
const {nonJSDoc} = opts;
|
|
309
|
+
const {minLines, maxLines} = settings;
|
|
310
|
+
|
|
311
|
+
/** @type {ESLintOrTSNode|import('estree').Comment} */
|
|
312
|
+
let currentNode = astNode;
|
|
313
|
+
let tokenBefore = null;
|
|
314
|
+
let parenthesisToken = null;
|
|
315
|
+
|
|
316
|
+
while (currentNode) {
|
|
317
|
+
const decorator = getDecorator(currentNode);
|
|
318
|
+
if (decorator) {
|
|
319
|
+
const dec = /** @type {unknown} */ (decorator);
|
|
320
|
+
currentNode = /** @type {import('eslint').Rule.Node} */ (dec);
|
|
321
|
+
}
|
|
322
|
+
tokenBefore = sourceCode.getTokenBefore(
|
|
323
|
+
/** @type {import('eslint').Rule.Node} */
|
|
324
|
+
(currentNode),
|
|
325
|
+
{includeComments: true}
|
|
326
|
+
);
|
|
327
|
+
if (
|
|
328
|
+
tokenBefore && tokenBefore.type === 'Punctuator' &&
|
|
329
|
+
tokenBefore.value === '('
|
|
330
|
+
) {
|
|
331
|
+
parenthesisToken = tokenBefore;
|
|
332
|
+
[tokenBefore] = sourceCode.getTokensBefore(
|
|
333
|
+
/** @type {import('eslint').Rule.Node} */
|
|
334
|
+
(currentNode),
|
|
335
|
+
{
|
|
336
|
+
count: 2,
|
|
337
|
+
includeComments: true
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
if (!tokenBefore || !isCommentToken(tokenBefore)) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
if (!nonJSDoc && tokenBefore.type === 'Line') {
|
|
345
|
+
currentNode = tokenBefore;
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* v8 ignore next 3 */
|
|
352
|
+
if (!tokenBefore || !currentNode.loc || !tokenBefore.loc) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (
|
|
357
|
+
(
|
|
358
|
+
(nonJSDoc && (tokenBefore.type !== 'Block' ||
|
|
359
|
+
!(/^\*\s/v).test(tokenBefore.value))) ||
|
|
360
|
+
(!nonJSDoc && tokenBefore.type === 'Block' &&
|
|
361
|
+
(/^\*\s/v).test(tokenBefore.value))
|
|
362
|
+
) &&
|
|
363
|
+
currentNode.loc.start.line - (
|
|
364
|
+
/** @type {import('eslint').AST.Token} */
|
|
365
|
+
(parenthesisToken ?? tokenBefore)
|
|
366
|
+
).loc.end.line >= minLines &&
|
|
367
|
+
currentNode.loc.start.line - (
|
|
368
|
+
/** @type {import('eslint').AST.Token} */
|
|
369
|
+
(parenthesisToken ?? tokenBefore)
|
|
370
|
+
).loc.end.line <= maxLines
|
|
371
|
+
) {
|
|
372
|
+
return tokenBefore;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return null;
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const overloadMethodNode = new Set([
|
|
379
|
+
'MethodDefinition',
|
|
380
|
+
'TSAbstractMethodDefinition'
|
|
381
|
+
]);
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* @param {ESLintOrTSNode|null|undefined} node
|
|
385
|
+
* @returns {ESLintOrTSNode[]|undefined}
|
|
386
|
+
*/
|
|
387
|
+
const getOverloadStatementSiblings = (node) => {
|
|
388
|
+
if (
|
|
389
|
+
node &&
|
|
390
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
391
|
+
// eslint-disable-next-line unicorn/prefer-includes-over-repeated-comparisons -- TS
|
|
392
|
+
(node.type === 'BlockStatement' ||
|
|
393
|
+
node.type === 'Program' ||
|
|
394
|
+
node.type === 'StaticBlock' ||
|
|
395
|
+
node.type === 'TSModuleBlock')
|
|
396
|
+
) {
|
|
397
|
+
return /** @type {ESLintOrTSNode[]} */ (node.body);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return undefined;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @param {ESLintOrTSNode} node
|
|
405
|
+
* @returns {{
|
|
406
|
+
* bodyless: boolean,
|
|
407
|
+
* kind: string,
|
|
408
|
+
* name: string,
|
|
409
|
+
* static: boolean
|
|
410
|
+
* }|null}
|
|
411
|
+
*/
|
|
412
|
+
const getMethodOverloadInfo = (node) => {
|
|
413
|
+
/* v8 ignore next 3 -- Defensive */
|
|
414
|
+
if (!overloadMethodNode.has(node.type)) {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* @type {{
|
|
420
|
+
* computed?: boolean,
|
|
421
|
+
* key?: {name?: string},
|
|
422
|
+
* kind?: string,
|
|
423
|
+
* static?: boolean,
|
|
424
|
+
* value?: {type?: string}
|
|
425
|
+
* }}
|
|
426
|
+
*/
|
|
427
|
+
// @ts-expect-error -- Loose method-shape check after node.type guard.
|
|
428
|
+
const method = node;
|
|
429
|
+
if (method.computed ||
|
|
430
|
+
!method.kind ||
|
|
431
|
+
!['method', 'constructor'].includes(/** @type {string} */ (method.kind)) ||
|
|
432
|
+
!method.key?.name
|
|
433
|
+
) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return {
|
|
438
|
+
bodyless: node.type === 'TSAbstractMethodDefinition' ||
|
|
439
|
+
method.value?.type === 'TSEmptyBodyFunctionExpression',
|
|
440
|
+
kind: method.kind,
|
|
441
|
+
name: method.key.name,
|
|
442
|
+
static: Boolean(method.static)
|
|
443
|
+
};
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* @param {ESLintOrTSNode} node
|
|
448
|
+
* @param {ESLintOrTSNode} prevSibling
|
|
449
|
+
* @returns {boolean}
|
|
450
|
+
*/
|
|
451
|
+
const isMatchingMethodOverloadSibling = (node, prevSibling) => {
|
|
452
|
+
const current = getMethodOverloadInfo(node);
|
|
453
|
+
const previous = getMethodOverloadInfo(prevSibling);
|
|
454
|
+
|
|
455
|
+
return Boolean(
|
|
456
|
+
current &&
|
|
457
|
+
previous?.bodyless &&
|
|
458
|
+
previous.name === current.name &&
|
|
459
|
+
previous.kind === current.kind &&
|
|
460
|
+
previous.static === current.static
|
|
461
|
+
);
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* @param {ESLintOrTSNode} node
|
|
466
|
+
* @returns {string|undefined}
|
|
467
|
+
*/
|
|
468
|
+
const getCurrentOverloadName = (node) => {
|
|
469
|
+
if (node.type === 'TSDeclareFunction' ||
|
|
470
|
+
node.type === 'FunctionDeclaration') {
|
|
471
|
+
return /** @type {{id?: {name?: string}}} */ (node).id?.name;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
475
|
+
const {declaration} =
|
|
476
|
+
/** @type {{declaration?: {type?: string, id?: {name?: string}}}} */ (
|
|
477
|
+
node
|
|
478
|
+
);
|
|
479
|
+
if (declaration?.type === 'FunctionDeclaration' ||
|
|
480
|
+
declaration?.type === 'TSDeclareFunction') {
|
|
481
|
+
return declaration.id?.name;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (overloadMethodNode.has(node.type)) {
|
|
486
|
+
const method =
|
|
487
|
+
/** @type {{computed?: boolean, key?: {name?: string}}} */ (node);
|
|
488
|
+
if (!method.computed) {
|
|
489
|
+
return method.key?.name;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return undefined;
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* @param {ESLintOrTSNode} node
|
|
498
|
+
* @returns {string|undefined}
|
|
499
|
+
*/
|
|
500
|
+
const getPreviousOverloadName = (node) => {
|
|
501
|
+
if (node.type === 'TSDeclareFunction') {
|
|
502
|
+
return /** @type {{id?: {name?: string}}} */ (node).id?.name;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
506
|
+
const {declaration} =
|
|
507
|
+
/** @type {{declaration?: {type?: string, id?: {name?: string}}}} */ (
|
|
508
|
+
node
|
|
509
|
+
);
|
|
510
|
+
if (declaration?.type === 'TSDeclareFunction') {
|
|
511
|
+
return declaration.id?.name;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (overloadMethodNode.has(node.type)) {
|
|
516
|
+
const method =
|
|
517
|
+
/** @type {{computed?: boolean, key?: {name?: string}}} */ (node);
|
|
518
|
+
if (!method.computed) {
|
|
519
|
+
return method.key?.name;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return undefined;
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* @param {ESLintOrTSNode} node
|
|
528
|
+
* @returns {ESLintOrTSNode|null}
|
|
529
|
+
*/
|
|
530
|
+
const getPreviousOverloadSibling = (node) => {
|
|
531
|
+
const {parent} = node;
|
|
532
|
+
let childNode = node;
|
|
533
|
+
/** @type {ESLintOrTSNode[]|undefined} */
|
|
534
|
+
let siblings;
|
|
535
|
+
|
|
536
|
+
if (
|
|
537
|
+
overloadMethodNode.has(node.type) &&
|
|
538
|
+
parent?.type === 'ClassBody'
|
|
539
|
+
) {
|
|
540
|
+
siblings = /** @type {ESLintOrTSNode[]} */ (parent.body);
|
|
541
|
+
} else if (parent?.type === 'ExportNamedDeclaration') {
|
|
542
|
+
childNode = parent;
|
|
543
|
+
siblings = getOverloadStatementSiblings(parent.parent);
|
|
544
|
+
} else {
|
|
545
|
+
siblings = getOverloadStatementSiblings(parent);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (!siblings) {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const idx = siblings.indexOf(childNode);
|
|
553
|
+
return idx > 0 ? siblings[idx - 1] : null;
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Retrieves the JSDoc comment for a given node.
|
|
558
|
+
*
|
|
559
|
+
* @param {import('eslint').SourceCode} sourceCode The ESLint SourceCode
|
|
560
|
+
* @param {ESLintOrTSNode} node The AST node to get
|
|
561
|
+
* the comment for.
|
|
562
|
+
* @param {Settings} settings The settings in context
|
|
563
|
+
* @param {{checkOverloads?: boolean}} [opts]
|
|
564
|
+
* @returns {Token|null} The Block comment
|
|
565
|
+
* token containing the JSDoc comment for the given node or
|
|
566
|
+
* null if not found.
|
|
567
|
+
* @public
|
|
568
|
+
*/
|
|
569
|
+
const getJSDocComment = function (sourceCode, node, settings, opts = {}) {
|
|
570
|
+
const reducedNode = getReducedASTNode(node, sourceCode, settings);
|
|
571
|
+
const comment = findJSDocComment(reducedNode, sourceCode, settings);
|
|
572
|
+
|
|
573
|
+
if (!comment && opts.checkOverloads) {
|
|
574
|
+
const functionName = getCurrentOverloadName(reducedNode);
|
|
575
|
+
const prevSibling = getPreviousOverloadSibling(reducedNode);
|
|
576
|
+
if (
|
|
577
|
+
prevSibling &&
|
|
578
|
+
functionName &&
|
|
579
|
+
getPreviousOverloadName(prevSibling) === functionName &&
|
|
580
|
+
(
|
|
581
|
+
!overloadMethodNode.has(reducedNode.type) ||
|
|
582
|
+
isMatchingMethodOverloadSibling(reducedNode, prevSibling)
|
|
583
|
+
)
|
|
584
|
+
) {
|
|
585
|
+
return getJSDocComment(sourceCode, prevSibling, settings, opts);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return comment;
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Retrieves the comment preceding a given node.
|
|
593
|
+
*
|
|
594
|
+
* @param {import('eslint').SourceCode} sourceCode The ESLint SourceCode
|
|
595
|
+
* @param {ESLintOrTSNode} node The AST node to get
|
|
596
|
+
* the comment for.
|
|
597
|
+
* @param {{maxLines: int, minLines: int, [name: string]: any}} settings The
|
|
598
|
+
* settings in context
|
|
599
|
+
* @returns {Token|null} The Block comment
|
|
600
|
+
* token containing the JSDoc comment for the given node or
|
|
601
|
+
* null if not found.
|
|
602
|
+
* @public
|
|
603
|
+
*/
|
|
604
|
+
const getNonJsdocComment = function (sourceCode, node, settings) {
|
|
605
|
+
const reducedNode = getReducedASTNode(node, sourceCode, settings);
|
|
606
|
+
|
|
607
|
+
return findJSDocComment(reducedNode, sourceCode, settings, {
|
|
608
|
+
nonJSDoc: true
|
|
609
|
+
});
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* @param {ESLintOrTSNode|import('eslint').AST.Token|
|
|
614
|
+
* import('estree').Comment
|
|
615
|
+
* } nodeA The AST node or token to compare
|
|
616
|
+
* @param {ESLintOrTSNode|import('eslint').AST.Token|
|
|
617
|
+
* import('estree').Comment} nodeB The
|
|
618
|
+
* AST node or token to compare
|
|
619
|
+
*/
|
|
620
|
+
const compareLocEndToStart = (nodeA, nodeB) => {
|
|
621
|
+
/* v8 ignore next */
|
|
622
|
+
return (nodeA.loc?.end.line ?? 0) === (nodeB.loc?.start.line ?? 0);
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Checks for the presence of a comment following the given node and
|
|
627
|
+
* returns it.
|
|
628
|
+
*
|
|
629
|
+
* This method is experimental.
|
|
630
|
+
*
|
|
631
|
+
* @param {import('eslint').SourceCode} sourceCode
|
|
632
|
+
* @param {ESLintOrTSNode} astNode The AST node to get
|
|
633
|
+
* the comment for.
|
|
634
|
+
* @returns {Token|null} The comment token containing the comment
|
|
635
|
+
* for the given node or null if not found.
|
|
636
|
+
*/
|
|
637
|
+
const getFollowingComment = function (sourceCode, astNode) {
|
|
638
|
+
/**
|
|
639
|
+
* @param {ESLintOrTSNode} node The
|
|
640
|
+
* AST node to get the comment for.
|
|
641
|
+
*/
|
|
642
|
+
const getTokensAfterIgnoringSemis = (node) => {
|
|
643
|
+
let tokenAfter = sourceCode.getTokenAfter(
|
|
644
|
+
/** @type {import('eslint').Rule.Node} */
|
|
645
|
+
(node),
|
|
646
|
+
{includeComments: true}
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
while (
|
|
650
|
+
tokenAfter && tokenAfter.type === 'Punctuator' &&
|
|
651
|
+
// tokenAfter.value === ')' // Don't apparently need to ignore
|
|
652
|
+
tokenAfter.value === ';'
|
|
653
|
+
) {
|
|
654
|
+
[tokenAfter] = sourceCode.getTokensAfter(tokenAfter, {
|
|
655
|
+
includeComments: true
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
return tokenAfter;
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* @param {ESLintOrTSNode} node The
|
|
663
|
+
* AST node to get the comment for.
|
|
664
|
+
*/
|
|
665
|
+
const tokenAfterIgnoringSemis = (node) => {
|
|
666
|
+
const tokenAfter = getTokensAfterIgnoringSemis(node);
|
|
667
|
+
return (
|
|
668
|
+
tokenAfter &&
|
|
669
|
+
isCommentToken(tokenAfter) &&
|
|
670
|
+
compareLocEndToStart(node, tokenAfter)
|
|
671
|
+
)
|
|
672
|
+
? tokenAfter
|
|
673
|
+
: null;
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
let tokenAfter = tokenAfterIgnoringSemis(astNode);
|
|
677
|
+
|
|
678
|
+
if (!tokenAfter) {
|
|
679
|
+
switch (astNode.type) {
|
|
680
|
+
case 'FunctionDeclaration':
|
|
681
|
+
tokenAfter = tokenAfterIgnoringSemis(
|
|
682
|
+
/** @type {ESLintOrTSNode} */
|
|
683
|
+
(astNode.body)
|
|
684
|
+
);
|
|
685
|
+
break;
|
|
686
|
+
case 'ExpressionStatement':
|
|
687
|
+
tokenAfter = tokenAfterIgnoringSemis(
|
|
688
|
+
/** @type {ESLintOrTSNode} */
|
|
689
|
+
(astNode.expression)
|
|
690
|
+
);
|
|
691
|
+
break;
|
|
692
|
+
|
|
693
|
+
/* v8 ignore next 3 */
|
|
694
|
+
default:
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return tokenAfter;
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
export {
|
|
703
|
+
getReducedASTNode, getJSDocComment, getNonJsdocComment,
|
|
704
|
+
getDecorator, findJSDocComment, getFollowingComment
|
|
705
|
+
};
|