eslint-plugin-jsdoc 48.0.0 → 48.0.1

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.
Files changed (64) hide show
  1. package/package.json +1 -1
  2. package/src/WarnSettings.js +34 -0
  3. package/src/alignTransform.js +356 -0
  4. package/src/defaultTagOrder.js +168 -0
  5. package/src/exportParser.js +957 -0
  6. package/src/getDefaultTagStructureForMode.js +969 -0
  7. package/src/index.js +266 -0
  8. package/src/iterateJsdoc.js +2555 -0
  9. package/src/jsdocUtils.js +1693 -0
  10. package/src/rules/checkAccess.js +45 -0
  11. package/src/rules/checkAlignment.js +63 -0
  12. package/src/rules/checkExamples.js +594 -0
  13. package/src/rules/checkIndentation.js +75 -0
  14. package/src/rules/checkLineAlignment.js +364 -0
  15. package/src/rules/checkParamNames.js +404 -0
  16. package/src/rules/checkPropertyNames.js +152 -0
  17. package/src/rules/checkSyntax.js +30 -0
  18. package/src/rules/checkTagNames.js +314 -0
  19. package/src/rules/checkTypes.js +535 -0
  20. package/src/rules/checkValues.js +220 -0
  21. package/src/rules/emptyTags.js +88 -0
  22. package/src/rules/implementsOnClasses.js +64 -0
  23. package/src/rules/importsAsDependencies.js +131 -0
  24. package/src/rules/informativeDocs.js +182 -0
  25. package/src/rules/matchDescription.js +286 -0
  26. package/src/rules/matchName.js +147 -0
  27. package/src/rules/multilineBlocks.js +333 -0
  28. package/src/rules/noBadBlocks.js +109 -0
  29. package/src/rules/noBlankBlockDescriptions.js +69 -0
  30. package/src/rules/noBlankBlocks.js +53 -0
  31. package/src/rules/noDefaults.js +85 -0
  32. package/src/rules/noMissingSyntax.js +195 -0
  33. package/src/rules/noMultiAsterisks.js +134 -0
  34. package/src/rules/noRestrictedSyntax.js +91 -0
  35. package/src/rules/noTypes.js +73 -0
  36. package/src/rules/noUndefinedTypes.js +328 -0
  37. package/src/rules/requireAsteriskPrefix.js +189 -0
  38. package/src/rules/requireDescription.js +161 -0
  39. package/src/rules/requireDescriptionCompleteSentence.js +333 -0
  40. package/src/rules/requireExample.js +118 -0
  41. package/src/rules/requireFileOverview.js +154 -0
  42. package/src/rules/requireHyphenBeforeParamDescription.js +178 -0
  43. package/src/rules/requireJsdoc.js +629 -0
  44. package/src/rules/requireParam.js +592 -0
  45. package/src/rules/requireParamDescription.js +89 -0
  46. package/src/rules/requireParamName.js +55 -0
  47. package/src/rules/requireParamType.js +89 -0
  48. package/src/rules/requireProperty.js +48 -0
  49. package/src/rules/requirePropertyDescription.js +25 -0
  50. package/src/rules/requirePropertyName.js +25 -0
  51. package/src/rules/requirePropertyType.js +25 -0
  52. package/src/rules/requireReturns.js +238 -0
  53. package/src/rules/requireReturnsCheck.js +141 -0
  54. package/src/rules/requireReturnsDescription.js +59 -0
  55. package/src/rules/requireReturnsType.js +51 -0
  56. package/src/rules/requireThrows.js +111 -0
  57. package/src/rules/requireYields.js +216 -0
  58. package/src/rules/requireYieldsCheck.js +208 -0
  59. package/src/rules/sortTags.js +557 -0
  60. package/src/rules/tagLines.js +359 -0
  61. package/src/rules/textEscaping.js +146 -0
  62. package/src/rules/validTypes.js +368 -0
  63. package/src/tagNames.js +234 -0
  64. package/src/utils/hasReturnValue.js +549 -0
@@ -0,0 +1,629 @@
1
+ import exportParser from '../exportParser.js';
2
+ import {
3
+ getSettings,
4
+ } from '../iterateJsdoc.js';
5
+ import jsdocUtils from '../jsdocUtils.js';
6
+ import {
7
+ getDecorator,
8
+ getJSDocComment,
9
+ getReducedASTNode,
10
+ } from '@es-joy/jsdoccomment';
11
+
12
+ /**
13
+ * @typedef {{
14
+ * ancestorsOnly: boolean,
15
+ * esm: boolean,
16
+ * initModuleExports: boolean,
17
+ * initWindow: boolean
18
+ * }} RequireJsdocOpts
19
+ */
20
+
21
+ /** @type {import('json-schema').JSONSchema4} */
22
+ const OPTIONS_SCHEMA = {
23
+ additionalProperties: false,
24
+ properties: {
25
+ checkConstructors: {
26
+ default: true,
27
+ type: 'boolean',
28
+ },
29
+ checkGetters: {
30
+ anyOf: [
31
+ {
32
+ type: 'boolean',
33
+ },
34
+ {
35
+ enum: [
36
+ 'no-setter',
37
+ ],
38
+ type: 'string',
39
+ },
40
+ ],
41
+ default: true,
42
+ },
43
+ checkSetters: {
44
+ anyOf: [
45
+ {
46
+ type: 'boolean',
47
+ },
48
+ {
49
+ enum: [
50
+ 'no-getter',
51
+ ],
52
+ type: 'string',
53
+ },
54
+ ],
55
+ default: true,
56
+ },
57
+ contexts: {
58
+ items: {
59
+ anyOf: [
60
+ {
61
+ type: 'string',
62
+ },
63
+ {
64
+ additionalProperties: false,
65
+ properties: {
66
+ context: {
67
+ type: 'string',
68
+ },
69
+ inlineCommentBlock: {
70
+ type: 'boolean',
71
+ },
72
+ minLineCount: {
73
+ type: 'integer',
74
+ },
75
+ },
76
+ type: 'object',
77
+ },
78
+ ],
79
+ },
80
+ type: 'array',
81
+ },
82
+ enableFixer: {
83
+ default: true,
84
+ type: 'boolean',
85
+ },
86
+ exemptEmptyConstructors: {
87
+ default: false,
88
+ type: 'boolean',
89
+ },
90
+ exemptEmptyFunctions: {
91
+ default: false,
92
+ type: 'boolean',
93
+ },
94
+ fixerMessage: {
95
+ default: '',
96
+ type: 'string',
97
+ },
98
+ minLineCount: {
99
+ type: 'integer',
100
+ },
101
+ publicOnly: {
102
+ oneOf: [
103
+ {
104
+ default: false,
105
+ type: 'boolean',
106
+ },
107
+ {
108
+ additionalProperties: false,
109
+ default: {},
110
+ properties: {
111
+ ancestorsOnly: {
112
+ type: 'boolean',
113
+ },
114
+ cjs: {
115
+ type: 'boolean',
116
+ },
117
+ esm: {
118
+ type: 'boolean',
119
+ },
120
+ window: {
121
+ type: 'boolean',
122
+ },
123
+ },
124
+ type: 'object',
125
+ },
126
+ ],
127
+ },
128
+ require: {
129
+ additionalProperties: false,
130
+ default: {},
131
+ properties: {
132
+ ArrowFunctionExpression: {
133
+ default: false,
134
+ type: 'boolean',
135
+ },
136
+ ClassDeclaration: {
137
+ default: false,
138
+ type: 'boolean',
139
+ },
140
+ ClassExpression: {
141
+ default: false,
142
+ type: 'boolean',
143
+ },
144
+ FunctionDeclaration: {
145
+ default: true,
146
+ type: 'boolean',
147
+ },
148
+ FunctionExpression: {
149
+ default: false,
150
+ type: 'boolean',
151
+ },
152
+ MethodDefinition: {
153
+ default: false,
154
+ type: 'boolean',
155
+ },
156
+ },
157
+ type: 'object',
158
+ },
159
+ },
160
+ type: 'object',
161
+ };
162
+
163
+ /**
164
+ * @param {import('eslint').Rule.RuleContext} context
165
+ * @param {import('json-schema').JSONSchema4Object} baseObject
166
+ * @param {string} option
167
+ * @param {string} key
168
+ * @returns {boolean|undefined}
169
+ */
170
+ const getOption = (context, baseObject, option, key) => {
171
+ if (context.options[0] && option in context.options[0] &&
172
+ // Todo: boolean shouldn't be returning property, but
173
+ // tests currently require
174
+ (typeof context.options[0][option] === 'boolean' ||
175
+ key in context.options[0][option])
176
+ ) {
177
+ return context.options[0][option][key];
178
+ }
179
+
180
+ return /** @type {{[key: string]: {default?: boolean|undefined}}} */ (
181
+ baseObject.properties
182
+ )[key].default;
183
+ };
184
+
185
+ /**
186
+ * @param {import('eslint').Rule.RuleContext} context
187
+ * @param {import('../iterateJsdoc.js').Settings} settings
188
+ * @returns {{
189
+ * contexts: (string|{
190
+ * context: string,
191
+ * inlineCommentBlock: boolean,
192
+ * minLineCount: import('../iterateJsdoc.js').Integer
193
+ * })[],
194
+ * enableFixer: boolean,
195
+ * exemptEmptyConstructors: boolean,
196
+ * exemptEmptyFunctions: boolean,
197
+ * fixerMessage: string,
198
+ * minLineCount: undefined|import('../iterateJsdoc.js').Integer,
199
+ * publicOnly: boolean|{[key: string]: boolean|undefined}
200
+ * require: {[key: string]: boolean|undefined}
201
+ * }}
202
+ */
203
+ const getOptions = (context, settings) => {
204
+ const {
205
+ publicOnly,
206
+ contexts = settings.contexts || [],
207
+ exemptEmptyConstructors = true,
208
+ exemptEmptyFunctions = false,
209
+ enableFixer = true,
210
+ fixerMessage = '',
211
+ minLineCount = undefined,
212
+ } = context.options[0] || {};
213
+
214
+ return {
215
+ contexts,
216
+ enableFixer,
217
+ exemptEmptyConstructors,
218
+ exemptEmptyFunctions,
219
+ fixerMessage,
220
+ minLineCount,
221
+ publicOnly: ((baseObj) => {
222
+ if (!publicOnly) {
223
+ return false;
224
+ }
225
+
226
+ /** @type {{[key: string]: boolean|undefined}} */
227
+ const properties = {};
228
+ for (const prop of Object.keys(
229
+ /** @type {import('json-schema').JSONSchema4Object} */ (
230
+ /** @type {import('json-schema').JSONSchema4Object} */ (
231
+ baseObj
232
+ ).properties),
233
+ )) {
234
+ const opt = getOption(
235
+ context,
236
+ /** @type {import('json-schema').JSONSchema4Object} */ (baseObj),
237
+ 'publicOnly',
238
+ prop,
239
+ );
240
+
241
+ properties[prop] = opt;
242
+ }
243
+
244
+ return properties;
245
+ })(
246
+ /** @type {import('json-schema').JSONSchema4Object} */
247
+ (
248
+ /** @type {import('json-schema').JSONSchema4Object} */
249
+ (
250
+ /** @type {import('json-schema').JSONSchema4Object} */
251
+ (
252
+ OPTIONS_SCHEMA.properties
253
+ ).publicOnly
254
+ ).oneOf
255
+ )[1],
256
+ ),
257
+ require: ((baseObj) => {
258
+ /** @type {{[key: string]: boolean|undefined}} */
259
+ const properties = {};
260
+ for (const prop of Object.keys(
261
+ /** @type {import('json-schema').JSONSchema4Object} */ (
262
+ /** @type {import('json-schema').JSONSchema4Object} */ (
263
+ baseObj
264
+ ).properties),
265
+ )) {
266
+ const opt = getOption(
267
+ context,
268
+ /** @type {import('json-schema').JSONSchema4Object} */
269
+ (baseObj),
270
+ 'require',
271
+ prop,
272
+ );
273
+ properties[prop] = opt;
274
+ }
275
+
276
+ return properties;
277
+ })(
278
+ /** @type {import('json-schema').JSONSchema4Object} */
279
+ (OPTIONS_SCHEMA.properties).require,
280
+ ),
281
+ };
282
+ };
283
+
284
+ /** @type {import('eslint').Rule.RuleModule} */
285
+ export default {
286
+ create (context) {
287
+ /* c8 ignore next -- Fallback to deprecated method */
288
+ const {
289
+ sourceCode = context.getSourceCode(),
290
+ } = context;
291
+ const settings = getSettings(context);
292
+ if (!settings) {
293
+ return {};
294
+ }
295
+
296
+ const opts = getOptions(context, settings);
297
+
298
+ const {
299
+ require: requireOption,
300
+ contexts,
301
+ exemptEmptyFunctions,
302
+ exemptEmptyConstructors,
303
+ enableFixer,
304
+ fixerMessage,
305
+ minLineCount,
306
+ } = opts;
307
+
308
+ const publicOnly =
309
+
310
+ /**
311
+ * @type {{
312
+ * [key: string]: boolean | undefined;
313
+ * }}
314
+ */ (
315
+ opts.publicOnly
316
+ );
317
+
318
+ /**
319
+ * @type {import('../iterateJsdoc.js').CheckJsdoc}
320
+ */
321
+ const checkJsDoc = (info, _handler, node) => {
322
+ if (
323
+ // Optimize
324
+ minLineCount !== undefined || contexts.some((ctxt) => {
325
+ if (typeof ctxt === 'string') {
326
+ return false;
327
+ }
328
+
329
+ const {
330
+ minLineCount: count,
331
+ } = ctxt;
332
+ return count !== undefined;
333
+ })
334
+ ) {
335
+ /**
336
+ * @param {undefined|import('../iterateJsdoc.js').Integer} count
337
+ */
338
+ const underMinLine = (count) => {
339
+ return count !== undefined && count >
340
+ (sourceCode.getText(node).match(/\n/gu)?.length ?? 0) + 1;
341
+ };
342
+
343
+ if (underMinLine(minLineCount)) {
344
+ return;
345
+ }
346
+
347
+ const {
348
+ minLineCount: contextMinLineCount,
349
+ } =
350
+ /**
351
+ * @type {{
352
+ * context: string;
353
+ * inlineCommentBlock: boolean;
354
+ * minLineCount: number;
355
+ * }}
356
+ */ (contexts.find((ctxt) => {
357
+ if (typeof ctxt === 'string') {
358
+ return false;
359
+ }
360
+
361
+ const {
362
+ context: ctx,
363
+ } = ctxt;
364
+ return ctx === (info.selector || node.type);
365
+ })) || {};
366
+ if (underMinLine(contextMinLineCount)) {
367
+ return;
368
+ }
369
+ }
370
+
371
+ const jsDocNode = getJSDocComment(sourceCode, node, settings);
372
+
373
+ if (jsDocNode) {
374
+ return;
375
+ }
376
+
377
+ // For those who have options configured against ANY constructors (or
378
+ // setters or getters) being reported
379
+ if (jsdocUtils.exemptSpeciaMethods(
380
+ {
381
+ description: '',
382
+ inlineTags: [],
383
+ problems: [],
384
+ source: [],
385
+ tags: [],
386
+ },
387
+ node,
388
+ context,
389
+ [
390
+ OPTIONS_SCHEMA,
391
+ ],
392
+ )) {
393
+ return;
394
+ }
395
+
396
+ if (
397
+ // Avoid reporting param-less, return-less functions (when
398
+ // `exemptEmptyFunctions` option is set)
399
+ exemptEmptyFunctions && info.isFunctionContext ||
400
+
401
+ // Avoid reporting param-less, return-less constructor methods (when
402
+ // `exemptEmptyConstructors` option is set)
403
+ exemptEmptyConstructors && jsdocUtils.isConstructor(node)
404
+ ) {
405
+ const functionParameterNames = jsdocUtils.getFunctionParameterNames(node);
406
+ if (!functionParameterNames.length && !jsdocUtils.hasReturnValue(node)) {
407
+ return;
408
+ }
409
+ }
410
+
411
+ const fix = /** @type {import('eslint').Rule.ReportFixer} */ (fixer) => {
412
+ // Default to one line break if the `minLines`/`maxLines` settings allow
413
+ const lines = settings.minLines === 0 && settings.maxLines >= 1 ? 1 : settings.minLines;
414
+ /** @type {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Decorator} */
415
+ let baseNode = getReducedASTNode(node, sourceCode);
416
+
417
+ const decorator = getDecorator(baseNode);
418
+ if (decorator) {
419
+ baseNode = decorator;
420
+ }
421
+
422
+ const indent = jsdocUtils.getIndent({
423
+ text: sourceCode.getText(
424
+ /** @type {import('eslint').Rule.Node} */ (baseNode),
425
+ /** @type {import('eslint').AST.SourceLocation} */
426
+ (
427
+ /** @type {import('eslint').Rule.Node} */ (baseNode).loc
428
+ ).start.column,
429
+ ),
430
+ });
431
+
432
+ const {
433
+ inlineCommentBlock,
434
+ } =
435
+ /**
436
+ * @type {{
437
+ * context: string,
438
+ * inlineCommentBlock: boolean,
439
+ * minLineCount: import('../iterateJsdoc.js').Integer
440
+ * }}
441
+ */ (contexts.find((contxt) => {
442
+ if (typeof contxt === 'string') {
443
+ return false;
444
+ }
445
+
446
+ const {
447
+ context: ctxt,
448
+ } = contxt;
449
+ return ctxt === node.type;
450
+ })) || {};
451
+ const insertion = (inlineCommentBlock ?
452
+ `/** ${fixerMessage}` :
453
+ `/**\n${indent}*${fixerMessage}\n${indent}`) +
454
+ `*/${'\n'.repeat(lines)}${indent.slice(0, -1)}`;
455
+
456
+ return fixer.insertTextBefore(
457
+ /** @type {import('eslint').Rule.Node} */
458
+ (baseNode),
459
+ insertion,
460
+ );
461
+ };
462
+
463
+ const report = () => {
464
+ const {
465
+ start,
466
+ } = /** @type {import('eslint').AST.SourceLocation} */ (node.loc);
467
+ const loc = {
468
+ end: {
469
+ column: 0,
470
+ line: start.line + 1,
471
+ },
472
+ start,
473
+ };
474
+ context.report({
475
+ fix: enableFixer ? fix : null,
476
+ loc,
477
+ messageId: 'missingJsDoc',
478
+ node,
479
+ });
480
+ };
481
+
482
+ if (publicOnly) {
483
+ /** @type {RequireJsdocOpts} */
484
+ const opt = {
485
+ ancestorsOnly: Boolean(publicOnly?.ancestorsOnly ?? false),
486
+ esm: Boolean(publicOnly?.esm ?? true),
487
+ initModuleExports: Boolean(publicOnly?.cjs ?? true),
488
+ initWindow: Boolean(publicOnly?.window ?? false),
489
+ };
490
+ const exported = exportParser.isUncommentedExport(node, sourceCode, opt, settings);
491
+
492
+ if (exported) {
493
+ report();
494
+ }
495
+ } else {
496
+ report();
497
+ }
498
+ };
499
+
500
+ /**
501
+ * @param {string} prop
502
+ * @returns {boolean}
503
+ */
504
+ const hasOption = (prop) => {
505
+ return requireOption[prop] || contexts.some((ctxt) => {
506
+ return typeof ctxt === 'object' ? ctxt.context === prop : ctxt === prop;
507
+ });
508
+ };
509
+
510
+ return {
511
+ ...jsdocUtils.getContextObject(
512
+ jsdocUtils.enforcedContexts(context, [], settings),
513
+ checkJsDoc,
514
+ ),
515
+ ArrowFunctionExpression (node) {
516
+ if (!hasOption('ArrowFunctionExpression')) {
517
+ return;
518
+ }
519
+
520
+ if (
521
+ [
522
+ 'VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration',
523
+ ].includes(node.parent.type) ||
524
+ [
525
+ 'Property', 'ObjectProperty', 'ClassProperty', 'PropertyDefinition',
526
+ ].includes(node.parent.type) &&
527
+ node ===
528
+ /**
529
+ * @type {import('@typescript-eslint/types').TSESTree.Property|
530
+ * import('@typescript-eslint/types').TSESTree.PropertyDefinition
531
+ * }
532
+ */
533
+ (node.parent).value
534
+ ) {
535
+ checkJsDoc({
536
+ isFunctionContext: true,
537
+ }, null, node);
538
+ }
539
+ },
540
+
541
+ ClassDeclaration (node) {
542
+ if (!hasOption('ClassDeclaration')) {
543
+ return;
544
+ }
545
+
546
+ checkJsDoc({
547
+ isFunctionContext: false,
548
+ }, null, node);
549
+ },
550
+
551
+ ClassExpression (node) {
552
+ if (!hasOption('ClassExpression')) {
553
+ return;
554
+ }
555
+
556
+ checkJsDoc({
557
+ isFunctionContext: false,
558
+ }, null, node);
559
+ },
560
+
561
+ FunctionDeclaration (node) {
562
+ if (!hasOption('FunctionDeclaration')) {
563
+ return;
564
+ }
565
+
566
+ checkJsDoc({
567
+ isFunctionContext: true,
568
+ }, null, node);
569
+ },
570
+
571
+ FunctionExpression (node) {
572
+ if (!hasOption('FunctionExpression')) {
573
+ return;
574
+ }
575
+
576
+ if (
577
+ [
578
+ 'VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration',
579
+ ].includes(node.parent.type) ||
580
+ [
581
+ 'Property', 'ObjectProperty', 'ClassProperty', 'PropertyDefinition',
582
+ ].includes(node.parent.type) &&
583
+ node ===
584
+ /**
585
+ * @type {import('@typescript-eslint/types').TSESTree.Property|
586
+ * import('@typescript-eslint/types').TSESTree.PropertyDefinition
587
+ * }
588
+ */
589
+ (node.parent).value
590
+ ) {
591
+ checkJsDoc({
592
+ isFunctionContext: true,
593
+ }, null, node);
594
+ }
595
+ },
596
+
597
+ MethodDefinition (node) {
598
+ if (!hasOption('MethodDefinition')) {
599
+ return;
600
+ }
601
+
602
+ checkJsDoc({
603
+ isFunctionContext: true,
604
+ selector: 'MethodDefinition',
605
+ }, null, /** @type {import('eslint').Rule.Node} */ (node.value));
606
+ },
607
+ };
608
+ },
609
+ meta: {
610
+ docs: {
611
+ category: 'Stylistic Issues',
612
+ description: 'Require JSDoc comments',
613
+ recommended: true,
614
+ url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-jsdoc.md#repos-sticky-header',
615
+ },
616
+
617
+ fixable: 'code',
618
+
619
+ messages: {
620
+ missingJsDoc: 'Missing JSDoc comment.',
621
+ },
622
+
623
+ schema: [
624
+ OPTIONS_SCHEMA,
625
+ ],
626
+
627
+ type: 'suggestion',
628
+ },
629
+ };