Skip to content

Commit f56b697

Browse files
committed
feat(require-jsdoc): add exemptOverloadedImplementations option
1 parent 20ce7e4 commit f56b697

File tree

4 files changed

+251
-0
lines changed

4 files changed

+251
-0
lines changed

.README/rules/require-jsdoc.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,18 @@ empty string.
117117

118118
If `true`, will skip above uncommented overloaded functions to check
119119
for a comment block (e.g., at the top of a set of overloaded functions).
120+
121+
If `false`, will force each overloaded function to be checked for a
122+
comment block.
123+
120124
Defaults to `true`.
121125

126+
### `exemptOverloadedImplementations`
127+
128+
If set to `true` will avoid checking an overloaded function's implementation.
129+
130+
Defaults to `false`.
131+
122132
## Context and settings
123133

124134
|||

docs/rules/require-jsdoc.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* [`minLineCount`](#user-content-require-jsdoc-options-minlinecount)
1717
* [`fixerMessage`](#user-content-require-jsdoc-options-fixermessage)
1818
* [`skipInterveningOverloadedDeclarations`](#user-content-require-jsdoc-options-skipinterveningoverloadeddeclarations)
19+
* [`exemptOverloadedImplementations`](#user-content-require-jsdoc-options-exemptoverloadedimplementations)
1920
* [Context and settings](#user-content-require-jsdoc-context-and-settings)
2021
* [Failing examples](#user-content-require-jsdoc-failing-examples)
2122
* [Passing examples](#user-content-require-jsdoc-passing-examples)
@@ -164,8 +165,20 @@ empty string.
164165

165166
If `true`, will skip above uncommented overloaded functions to check
166167
for a comment block (e.g., at the top of a set of overloaded functions).
168+
169+
If `false`, will force each overloaded function to be checked for a
170+
comment block.
171+
167172
Defaults to `true`.
168173

174+
<a name="user-content-require-jsdoc-options-exemptoverloadedimplementations"></a>
175+
<a name="require-jsdoc-options-exemptoverloadedimplementations"></a>
176+
### <code>exemptOverloadedImplementations</code>
177+
178+
If set to `true` will avoid checking an overloaded function's implementation.
179+
180+
Defaults to `false`.
181+
169182
<a name="user-content-require-jsdoc-context-and-settings"></a>
170183
<a name="require-jsdoc-context-and-settings"></a>
171184
## Context and settings
@@ -1076,6 +1089,20 @@ function myFunction(foo: string): void;
10761089
function myFunction(foo?: string) {}
10771090
// "jsdoc/require-jsdoc": ["error"|"warn", {"skipInterveningOverloadedDeclarations":false}]
10781091
// Message: Missing JSDoc comment.
1092+
1093+
/**
1094+
* Test function with param.
1095+
* @param foo - Test param.
1096+
*/
1097+
function myFunction(foo: string): void;
1098+
function myFunction(): void;
1099+
/**
1100+
* Function implementation
1101+
* @param foo
1102+
*/
1103+
function myFunction(foo?: string) {}
1104+
// "jsdoc/require-jsdoc": ["error"|"warn", {"contexts":["TSDeclareFunction"],"exemptOverloadedImplementations":false,"skipInterveningOverloadedDeclarations":false}]
1105+
// Message: Missing JSDoc comment.
10791106
````
10801107

10811108

@@ -2007,5 +2034,40 @@ function myFunction(foo: string): void;
20072034
*/
20082035
function myFunction(): void;
20092036
function myFunction(foo?: string) {}
2037+
2038+
/**
2039+
* Test function with param.
2040+
* @param foo - Test param.
2041+
*/
2042+
function myFunction(foo: string): void;
2043+
/**
2044+
* Test function without param.
2045+
*/
2046+
function myFunction(): void;
2047+
function myFunction(foo?: string) {}
2048+
// "jsdoc/require-jsdoc": ["error"|"warn", {"contexts":["TSDeclareFunction"],"exemptOverloadedImplementations":true,"skipInterveningOverloadedDeclarations":false}]
2049+
2050+
/**
2051+
* Test function with param.
2052+
* @param foo - Test param.
2053+
*/
2054+
export function myFunction(foo: string): void;
2055+
/**
2056+
* Test function without param.
2057+
*/
2058+
export function myFunction(): void;
2059+
export function myFunction(foo?: string) {}
2060+
// "jsdoc/require-jsdoc": ["error"|"warn", {"contexts":["TSDeclareFunction"],"exemptOverloadedImplementations":true,"skipInterveningOverloadedDeclarations":false}]
2061+
2062+
/**
2063+
*
2064+
*/
2065+
const quux = () => {
2066+
/**
2067+
*
2068+
*/
2069+
function myFunction(foo?: string) {}
2070+
};
2071+
// "jsdoc/require-jsdoc": ["error"|"warn", {"exemptOverloadedImplementations":true,"require":{"ArrowFunctionExpression":true}}]
20102072
````
20112073

src/rules/requireJsdoc.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ const OPTIONS_SCHEMA = {
104104
default: false,
105105
type: 'boolean',
106106
},
107+
exemptOverloadedImplementations: {
108+
default: false,
109+
type: 'boolean',
110+
},
107111
fixerMessage: {
108112
default: '',
109113
type: 'string',
@@ -307,6 +311,7 @@ const getOption = (context, baseObject, option, key) => {
307311
* exemptEmptyConstructors: boolean,
308312
* exemptEmptyFunctions: boolean,
309313
* skipInterveningOverloadedDeclarations: boolean,
314+
* exemptOverloadedImplementations: boolean,
310315
* fixerMessage: string,
311316
* minLineCount: undefined|import('../iterateJsdoc.js').Integer,
312317
* publicOnly: boolean|{[key: string]: boolean|undefined}
@@ -319,6 +324,7 @@ const getOptions = (context, settings) => {
319324
enableFixer = true,
320325
exemptEmptyConstructors = true,
321326
exemptEmptyFunctions = false,
327+
exemptOverloadedImplementations = false,
322328
fixerMessage = '',
323329
minLineCount = undefined,
324330
publicOnly,
@@ -330,6 +336,7 @@ const getOptions = (context, settings) => {
330336
enableFixer,
331337
exemptEmptyConstructors,
332338
exemptEmptyFunctions,
339+
exemptOverloadedImplementations,
333340
fixerMessage,
334341
minLineCount,
335342
publicOnly: ((baseObj) => {
@@ -396,6 +403,48 @@ const getOptions = (context, settings) => {
396403
};
397404
};
398405

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+
399448
/** @type {import('eslint').Rule.RuleModule} */
400449
export default {
401450
create (context) {
@@ -415,6 +464,7 @@ export default {
415464
enableFixer,
416465
exemptEmptyConstructors,
417466
exemptEmptyFunctions,
467+
exemptOverloadedImplementations,
418468
fixerMessage,
419469
minLineCount,
420470
require: requireOption,
@@ -484,6 +534,10 @@ export default {
484534
}
485535
}
486536

537+
if (exemptOverloadedImplementations && isFunctionWithOverload(node)) {
538+
return;
539+
}
540+
487541
const jsDocNode = getJSDocComment(
488542
sourceCode, node, settings, {
489543
checkOverloads: skipInterveningOverloadedDeclarations,

test/rules/assertions/requireJsdoc.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4382,6 +4382,55 @@ function quux (foo) {
43824382
function myFunction(foo?: string) {}
43834383
`,
43844384
},
4385+
{
4386+
code: `
4387+
/**
4388+
* Test function with param.
4389+
* @param foo - Test param.
4390+
*/
4391+
function myFunction(foo: string): void;
4392+
function myFunction(): void;
4393+
/**
4394+
* Function implementation
4395+
* @param foo
4396+
*/
4397+
function myFunction(foo?: string) {}
4398+
`,
4399+
errors: [
4400+
{
4401+
line: 7,
4402+
message: 'Missing JSDoc comment.',
4403+
},
4404+
],
4405+
languageOptions: {
4406+
parser: typescriptEslintParser,
4407+
},
4408+
options: [
4409+
{
4410+
contexts: [
4411+
'TSDeclareFunction',
4412+
],
4413+
exemptOverloadedImplementations: false,
4414+
skipInterveningOverloadedDeclarations: false,
4415+
},
4416+
],
4417+
output: `
4418+
/**
4419+
* Test function with param.
4420+
* @param foo - Test param.
4421+
*/
4422+
function myFunction(foo: string): void;
4423+
/**
4424+
*
4425+
*/
4426+
function myFunction(): void;
4427+
/**
4428+
* Function implementation
4429+
* @param foo
4430+
*/
4431+
function myFunction(foo?: string) {}
4432+
`,
4433+
},
43854434
],
43864435
valid: [
43874436
{
@@ -6579,5 +6628,81 @@ function quux (foo) {
65796628
parser: typescriptEslintParser,
65806629
},
65816630
},
6631+
{
6632+
code: `
6633+
/**
6634+
* Test function with param.
6635+
* @param foo - Test param.
6636+
*/
6637+
function myFunction(foo: string): void;
6638+
/**
6639+
* Test function without param.
6640+
*/
6641+
function myFunction(): void;
6642+
function myFunction(foo?: string) {}
6643+
`,
6644+
languageOptions: {
6645+
parser: typescriptEslintParser,
6646+
},
6647+
options: [
6648+
{
6649+
contexts: [
6650+
'TSDeclareFunction',
6651+
],
6652+
exemptOverloadedImplementations: true,
6653+
skipInterveningOverloadedDeclarations: false,
6654+
},
6655+
],
6656+
},
6657+
{
6658+
code: `
6659+
/**
6660+
* Test function with param.
6661+
* @param foo - Test param.
6662+
*/
6663+
export function myFunction(foo: string): void;
6664+
/**
6665+
* Test function without param.
6666+
*/
6667+
export function myFunction(): void;
6668+
export function myFunction(foo?: string) {}
6669+
`,
6670+
languageOptions: {
6671+
parser: typescriptEslintParser,
6672+
},
6673+
options: [
6674+
{
6675+
contexts: [
6676+
'TSDeclareFunction',
6677+
],
6678+
exemptOverloadedImplementations: true,
6679+
skipInterveningOverloadedDeclarations: false,
6680+
},
6681+
],
6682+
},
6683+
{
6684+
code: `
6685+
/**
6686+
*
6687+
*/
6688+
const quux = () => {
6689+
/**
6690+
*
6691+
*/
6692+
function myFunction(foo?: string) {}
6693+
};
6694+
`,
6695+
languageOptions: {
6696+
parser: typescriptEslintParser,
6697+
},
6698+
options: [
6699+
{
6700+
exemptOverloadedImplementations: true,
6701+
require: {
6702+
ArrowFunctionExpression: true,
6703+
},
6704+
},
6705+
],
6706+
},
65826707
],
65836708
});

0 commit comments

Comments
 (0)