eslint-plugin-jsdoc 54.4.1 → 54.6.0

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/src/index.js CHANGED
@@ -58,80 +58,140 @@ import sortTags from './rules/sortTags.js';
58
58
  import tagLines from './rules/tagLines.js';
59
59
  import textEscaping from './rules/textEscaping.js';
60
60
  import validTypes from './rules/validTypes.js';
61
+ import {
62
+ merge,
63
+ } from 'object-deep-merge';
61
64
 
62
65
  /* eslint-disable jsdoc/valid-types -- Bug */
63
66
  /**
64
67
  * @typedef {"recommended" | "stylistic" | "contents" | "logical" | "requirements"} ConfigGroups
65
68
  * @typedef {"" | "-typescript" | "-typescript-flavor"} ConfigVariants
66
69
  * @typedef {"" | "-error"} ErrorLevelVariants
67
- * @type {import('eslint').ESLint.Plugin & {
70
+ * @type {((
71
+ * cfg?: {
72
+ * mergeSettings?: boolean,
73
+ * config?: `flat/${ConfigGroups}${ConfigVariants}${ErrorLevelVariants}`,
74
+ * settings?: Partial<import('./iterateJsdoc.js').Settings>
75
+ * }
76
+ * ) => import('eslint').Linter.Config) & import('eslint').ESLint.Plugin & {
68
77
  * configs: Record<`flat/${ConfigGroups}${ConfigVariants}${ErrorLevelVariants}`,
69
78
  * import('eslint').Linter.Config>
70
79
  * }}
71
80
  */
72
- const index = {
73
- /* eslint-enable jsdoc/valid-types -- Bug */
74
- // @ts-expect-error Ok
75
- configs: {},
76
- rules: {
77
- 'check-access': checkAccess,
78
- 'check-alignment': checkAlignment,
79
- 'check-examples': checkExamples,
80
- 'check-indentation': checkIndentation,
81
- 'check-line-alignment': checkLineAlignment,
82
- 'check-param-names': checkParamNames,
83
- 'check-property-names': checkPropertyNames,
84
- 'check-syntax': checkSyntax,
85
- 'check-tag-names': checkTagNames,
86
- 'check-template-names': checkTemplateNames,
87
- 'check-types': checkTypes,
88
- 'check-values': checkValues,
89
- 'convert-to-jsdoc-comments': convertToJsdocComments,
90
- 'empty-tags': emptyTags,
91
- 'implements-on-classes': implementsOnClasses,
92
- 'imports-as-dependencies': importsAsDependencies,
93
- 'informative-docs': informativeDocs,
94
- 'lines-before-block': linesBeforeBlock,
95
- 'match-description': matchDescription,
96
- 'match-name': matchName,
97
- 'multiline-blocks': multilineBlocks,
98
- 'no-bad-blocks': noBadBlocks,
99
- 'no-blank-block-descriptions': noBlankBlockDescriptions,
100
- 'no-blank-blocks': noBlankBlocks,
101
- 'no-defaults': noDefaults,
102
- 'no-missing-syntax': noMissingSyntax,
103
- 'no-multi-asterisks': noMultiAsterisks,
104
- 'no-restricted-syntax': noRestrictedSyntax,
105
- 'no-types': noTypes,
106
- 'no-undefined-types': noUndefinedTypes,
107
- 'require-asterisk-prefix': requireAsteriskPrefix,
108
- 'require-description': requireDescription,
109
- 'require-description-complete-sentence': requireDescriptionCompleteSentence,
110
- 'require-example': requireExample,
111
- 'require-file-overview': requireFileOverview,
112
- 'require-hyphen-before-param-description': requireHyphenBeforeParamDescription,
113
- 'require-jsdoc': requireJsdoc,
114
- 'require-param': requireParam,
115
- 'require-param-description': requireParamDescription,
116
- 'require-param-name': requireParamName,
117
- 'require-param-type': requireParamType,
118
- 'require-property': requireProperty,
119
- 'require-property-description': requirePropertyDescription,
120
- 'require-property-name': requirePropertyName,
121
- 'require-property-type': requirePropertyType,
122
- 'require-returns': requireReturns,
123
- 'require-returns-check': requireReturnsCheck,
124
- 'require-returns-description': requireReturnsDescription,
125
- 'require-returns-type': requireReturnsType,
126
- 'require-template': requireTemplate,
127
- 'require-throws': requireThrows,
128
- 'require-yields': requireYields,
129
- 'require-yields-check': requireYieldsCheck,
130
- 'sort-tags': sortTags,
131
- 'tag-lines': tagLines,
132
- 'text-escaping': textEscaping,
133
- 'valid-types': validTypes,
134
- },
81
+ // @ts-expect-error Ok
82
+ const index = function (cfg) {
83
+ /** @type {import('eslint').Linter.Config} */
84
+ let outputConfig = {
85
+ plugins: {
86
+ jsdoc: index,
87
+ },
88
+ };
89
+ if (
90
+ cfg?.config
91
+ ) {
92
+ // @ts-expect-error Security check
93
+ if (cfg.config === '__proto__') {
94
+ throw new TypeError('Disallowed config value');
95
+ }
96
+
97
+ outputConfig = index.configs[cfg.config];
98
+ }
99
+
100
+ outputConfig.settings = {
101
+ jsdoc: cfg?.mergeSettings === false ?
102
+ cfg.settings :
103
+ merge(
104
+ {},
105
+ cfg?.settings ?? {},
106
+ cfg?.config?.includes('recommended') ?
107
+ {
108
+ // We may need to drop these for "typescript" (non-"flavor") configs,
109
+ // if support is later added: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
110
+ structuredTags: {
111
+ next: {
112
+ required: [
113
+ 'type',
114
+ ],
115
+ },
116
+ throws: {
117
+ required: [
118
+ 'type',
119
+ ],
120
+ },
121
+ yields: {
122
+ required: [
123
+ 'type',
124
+ ],
125
+ },
126
+ },
127
+ } :
128
+ {},
129
+ ),
130
+ };
131
+
132
+ return outputConfig;
133
+ };
134
+ /* eslint-enable jsdoc/valid-types -- Bug */
135
+
136
+ index.configs = {};
137
+ index.rules = {
138
+ 'check-access': checkAccess,
139
+ 'check-alignment': checkAlignment,
140
+ 'check-examples': checkExamples,
141
+ 'check-indentation': checkIndentation,
142
+ 'check-line-alignment': checkLineAlignment,
143
+ 'check-param-names': checkParamNames,
144
+ 'check-property-names': checkPropertyNames,
145
+ 'check-syntax': checkSyntax,
146
+ 'check-tag-names': checkTagNames,
147
+ 'check-template-names': checkTemplateNames,
148
+ 'check-types': checkTypes,
149
+ 'check-values': checkValues,
150
+ 'convert-to-jsdoc-comments': convertToJsdocComments,
151
+ 'empty-tags': emptyTags,
152
+ 'implements-on-classes': implementsOnClasses,
153
+ 'imports-as-dependencies': importsAsDependencies,
154
+ 'informative-docs': informativeDocs,
155
+ 'lines-before-block': linesBeforeBlock,
156
+ 'match-description': matchDescription,
157
+ 'match-name': matchName,
158
+ 'multiline-blocks': multilineBlocks,
159
+ 'no-bad-blocks': noBadBlocks,
160
+ 'no-blank-block-descriptions': noBlankBlockDescriptions,
161
+ 'no-blank-blocks': noBlankBlocks,
162
+ 'no-defaults': noDefaults,
163
+ 'no-missing-syntax': noMissingSyntax,
164
+ 'no-multi-asterisks': noMultiAsterisks,
165
+ 'no-restricted-syntax': noRestrictedSyntax,
166
+ 'no-types': noTypes,
167
+ 'no-undefined-types': noUndefinedTypes,
168
+ 'require-asterisk-prefix': requireAsteriskPrefix,
169
+ 'require-description': requireDescription,
170
+ 'require-description-complete-sentence': requireDescriptionCompleteSentence,
171
+ 'require-example': requireExample,
172
+ 'require-file-overview': requireFileOverview,
173
+ 'require-hyphen-before-param-description': requireHyphenBeforeParamDescription,
174
+ 'require-jsdoc': requireJsdoc,
175
+ 'require-param': requireParam,
176
+ 'require-param-description': requireParamDescription,
177
+ 'require-param-name': requireParamName,
178
+ 'require-param-type': requireParamType,
179
+ 'require-property': requireProperty,
180
+ 'require-property-description': requirePropertyDescription,
181
+ 'require-property-name': requirePropertyName,
182
+ 'require-property-type': requirePropertyType,
183
+ 'require-returns': requireReturns,
184
+ 'require-returns-check': requireReturnsCheck,
185
+ 'require-returns-description': requireReturnsDescription,
186
+ 'require-returns-type': requireReturnsType,
187
+ 'require-template': requireTemplate,
188
+ 'require-throws': requireThrows,
189
+ 'require-yields': requireYields,
190
+ 'require-yields-check': requireYieldsCheck,
191
+ 'sort-tags': sortTags,
192
+ 'tag-lines': tagLines,
193
+ 'text-escaping': textEscaping,
194
+ 'valid-types': validTypes,
135
195
  };
136
196
 
137
197
  /**
@@ -104,6 +104,10 @@ const OPTIONS_SCHEMA = {
104
104
  default: false,
105
105
  type: 'boolean',
106
106
  },
107
+ exemptOverloadedImplementations: {
108
+ default: false,
109
+ type: 'boolean',
110
+ },
107
111
  fixerMessage: {
108
112
  default: '',
109
113
  type: 'string',
@@ -169,6 +173,10 @@ const OPTIONS_SCHEMA = {
169
173
  },
170
174
  type: 'object',
171
175
  },
176
+ skipInterveningOverloadedDeclarations: {
177
+ default: true,
178
+ type: 'boolean',
179
+ },
172
180
  },
173
181
  type: 'object',
174
182
  };
@@ -302,6 +310,8 @@ const getOption = (context, baseObject, option, key) => {
302
310
  * enableFixer: boolean,
303
311
  * exemptEmptyConstructors: boolean,
304
312
  * exemptEmptyFunctions: boolean,
313
+ * skipInterveningOverloadedDeclarations: boolean,
314
+ * exemptOverloadedImplementations: boolean,
305
315
  * fixerMessage: string,
306
316
  * minLineCount: undefined|import('../iterateJsdoc.js').Integer,
307
317
  * publicOnly: boolean|{[key: string]: boolean|undefined}
@@ -314,9 +324,11 @@ const getOptions = (context, settings) => {
314
324
  enableFixer = true,
315
325
  exemptEmptyConstructors = true,
316
326
  exemptEmptyFunctions = false,
327
+ exemptOverloadedImplementations = false,
317
328
  fixerMessage = '',
318
329
  minLineCount = undefined,
319
330
  publicOnly,
331
+ skipInterveningOverloadedDeclarations = true,
320
332
  } = context.options[0] || {};
321
333
 
322
334
  return {
@@ -324,6 +336,7 @@ const getOptions = (context, settings) => {
324
336
  enableFixer,
325
337
  exemptEmptyConstructors,
326
338
  exemptEmptyFunctions,
339
+ exemptOverloadedImplementations,
327
340
  fixerMessage,
328
341
  minLineCount,
329
342
  publicOnly: ((baseObj) => {
@@ -386,9 +399,52 @@ const getOptions = (context, settings) => {
386
399
  /** @type {import('json-schema').JSONSchema4Object} */
387
400
  (OPTIONS_SCHEMA.properties).require,
388
401
  ),
402
+ skipInterveningOverloadedDeclarations,
389
403
  };
390
404
  };
391
405
 
406
+ /**
407
+ * @param {ESLintOrTSNode} node
408
+ */
409
+ const isFunctionWithOverload = (node) => {
410
+ if (node.type !== 'FunctionDeclaration') {
411
+ return false;
412
+ }
413
+
414
+ let parent;
415
+ let child;
416
+
417
+ if (node.parent?.type === 'Program') {
418
+ parent = node.parent;
419
+ child = node;
420
+ } else if (node.parent?.type === 'ExportNamedDeclaration' &&
421
+ node.parent?.parent.type === 'Program') {
422
+ parent = node.parent?.parent;
423
+ child = node.parent;
424
+ }
425
+
426
+ if (!child || !parent) {
427
+ return false;
428
+ }
429
+
430
+ const functionName = node.id.name;
431
+
432
+ const idx = parent.body.indexOf(child);
433
+ const prevSibling = parent.body[idx - 1];
434
+
435
+ return (
436
+ // @ts-expect-error Should be ok
437
+ (prevSibling?.type === 'TSDeclareFunction' &&
438
+ // @ts-expect-error Should be ok
439
+ functionName === prevSibling.id.name) ||
440
+ (prevSibling?.type === 'ExportNamedDeclaration' &&
441
+ // @ts-expect-error Should be ok
442
+ prevSibling.declaration?.type === 'TSDeclareFunction' &&
443
+ // @ts-expect-error Should be ok
444
+ prevSibling.declaration?.id?.name === functionName)
445
+ );
446
+ };
447
+
392
448
  /** @type {import('eslint').Rule.RuleModule} */
393
449
  export default {
394
450
  create (context) {
@@ -408,9 +464,11 @@ export default {
408
464
  enableFixer,
409
465
  exemptEmptyConstructors,
410
466
  exemptEmptyFunctions,
467
+ exemptOverloadedImplementations,
411
468
  fixerMessage,
412
469
  minLineCount,
413
470
  require: requireOption,
471
+ skipInterveningOverloadedDeclarations,
414
472
  } = opts;
415
473
 
416
474
  const publicOnly =
@@ -476,7 +534,15 @@ export default {
476
534
  }
477
535
  }
478
536
 
479
- const jsDocNode = getJSDocComment(sourceCode, node, settings);
537
+ if (exemptOverloadedImplementations && isFunctionWithOverload(node)) {
538
+ return;
539
+ }
540
+
541
+ const jsDocNode = getJSDocComment(
542
+ sourceCode, node, settings, {
543
+ checkOverloads: skipInterveningOverloadedDeclarations,
544
+ },
545
+ );
480
546
 
481
547
  if (jsDocNode) {
482
548
  return;
@@ -42,6 +42,7 @@ export default iterateJsdoc(({
42
42
  /**
43
43
  * @param {import('@typescript-eslint/types').TSESTree.FunctionDeclaration|
44
44
  * import('@typescript-eslint/types').TSESTree.ClassDeclaration|
45
+ * import('@typescript-eslint/types').TSESTree.TSDeclareFunction|
45
46
  * import('@typescript-eslint/types').TSESTree.TSInterfaceDeclaration|
46
47
  * import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration
47
48
  */
@@ -79,6 +80,7 @@ export default iterateJsdoc(({
79
80
  switch (nde.type) {
80
81
  case 'ClassDeclaration':
81
82
  case 'FunctionDeclaration':
83
+ case 'TSDeclareFunction':
82
84
  case 'TSInterfaceDeclaration':
83
85
  case 'TSTypeAliasDeclaration':
84
86
  checkTypeParams(nde);
@@ -97,6 +99,7 @@ export default iterateJsdoc(({
97
99
  switch (nde.declaration?.type) {
98
100
  case 'ClassDeclaration':
99
101
  case 'FunctionDeclaration':
102
+ case 'TSDeclareFunction':
100
103
  case 'TSInterfaceDeclaration':
101
104
  case 'TSTypeAliasDeclaration':
102
105
  checkTypeParams(nde.declaration);