Skip to content

Commit a36faf2

Browse files
committed
fix(require-param, check-param-names): add option useDefaultObjectProperties for expecting documentation or avoiding reporting of documented; addresses part of #676
1 parent 5f586fd commit a36faf2

9 files changed

Lines changed: 221 additions & 25 deletions

File tree

.README/rules/check-param-names.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,17 @@ their presence within the function signature. Other inconsistencies between
5757

5858
Whether to check destructured properties. Defaults to `true`.
5959

60+
##### `useDefaultObjectProperties`
61+
62+
Set to `true` if you wish to avoid reporting of child property documentation
63+
where instead of destructuring, a whole plain object is supplied as default
64+
value but you wish its keys to be considered as signalling that the properties
65+
are present and can therefore be documented. Defaults to `false`.
66+
6067
|||
6168
|---|---|
6269
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
63-
|Options|`allowExtraTrailingParamDocs`, `checkDestructured`, `checkRestProperty`, `checkTypesPattern`|
70+
|Options|`allowExtraTrailingParamDocs`, `checkDestructured`, `checkRestProperty`, `checkTypesPattern`, `useDefaultObjectProperties`|
6471
|Tags|`param`|
6572
|Aliases|`arg`, `argument`|
6673
|Recommended|true|

.README/rules/require-param.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -359,13 +359,18 @@ implied to be `false` (i.e., the inside of the roots will not be checked
359359
either, e.g., it will also not complain if `a` or `b` do not have their own
360360
documentation). Defaults to `true`.
361361

362-
| | |
363-
| -------- | ------------------------------------------------------------------------------------------------------------- |
362+
##### `useDefaultObjectProperties`
363+
364+
Set to `true` if you wish to expect documentation of properties on objects
365+
supplied as default values. Defaults to `false`.
366+
367+
| | |
368+
| -------- | ----------------------------------------------------------------------------- |
364369
| Context | `ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`; others when `contexts` option enabled |
365-
| Tags | `param` |
366-
| Aliases | `arg`, `argument` |
367-
|Recommended|true|
368-
| Options | `autoIncrementBase`, `checkDestructured`, `checkDestructuredRoots`, `contexts`, `enableFixer`, `enableRootFixer`, `enableRestElementFixer`, `checkRestProperty`, `exemptedBy`, `checkConstructors`, `checkGetters`, `checkSetters`, `checkTypesPattern`, `unnamedRootBase` |
369-
| Settings | `overrideReplacesDocs`, `augmentsExtendsReplacesDocs`, `implementsReplacesDocs` |
370+
| Tags | `param` |
371+
| Aliases | `arg`, `argument` |
372+
|Recommended | true|
373+
| Options | `autoIncrementBase`, `checkDestructured`, `checkDestructuredRoots`, `contexts`, `enableFixer`, `enableRootFixer`, `enableRestElementFixer`, `checkRestProperty`, `exemptedBy`, `checkConstructors`, `checkGetters`, `checkSetters`, `checkTypesPattern`, `unnamedRootBase`, `useDefaultObjectProperties`|
374+
| Settings | `overrideReplacesDocs`, `augmentsExtendsReplacesDocs`, `implementsReplacesDocs`|
370375

371376
<!-- assertions requireParam -->

README.md

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2266,10 +2266,18 @@ their presence within the function signature. Other inconsistencies between
22662266

22672267
Whether to check destructured properties. Defaults to `true`.
22682268

2269+
<a name="eslint-plugin-jsdoc-rules-check-param-names-options-4-usedefaultobjectproperties"></a>
2270+
##### <code>useDefaultObjectProperties</code>
2271+
2272+
Set to `true` if you wish to avoid reporting of child property documentation
2273+
where instead of destructuring, a whole plain object is supplied as default
2274+
value but you wish its keys to be considered as signalling that the properties
2275+
are present and can therefore be documented. Defaults to `false`.
2276+
22692277
|||
22702278
|---|---|
22712279
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
2272-
|Options|`allowExtraTrailingParamDocs`, `checkDestructured`, `checkRestProperty`, `checkTypesPattern`|
2280+
|Options|`allowExtraTrailingParamDocs`, `checkDestructured`, `checkRestProperty`, `checkTypesPattern`, `useDefaultObjectProperties`|
22732281
|Tags|`param`|
22742282
|Aliases|`arg`, `argument`|
22752283
|Recommended|true|
@@ -2664,6 +2672,19 @@ function quux ({ foo: { bar } }) {}
26642672
*/
26652673
function foo({ foo: { bar: { baz } }}) {}
26662674
// Message: Missing @param "options.foo.bar.baz"
2675+
2676+
/**
2677+
* Returns a number.
2678+
* @param {Object} props Props.
2679+
* @param {Object} props.prop Prop.
2680+
* @param {string} props.prop.a String.
2681+
* @param {string} props.prop.b String.
2682+
* @return {number} A number.
2683+
*/
2684+
export function testFn1 ({ prop = { a: 1, b: 2 } }) {
2685+
}
2686+
// Options: [{"useDefaultObjectProperties":false}]
2687+
// Message: @param "props.prop.a" does not exist on props
26672688
````
26682689

26692690
The following patterns are not considered problems:
@@ -2995,6 +3016,18 @@ function Item({
29953016
defaulting: [quux, xyz] = []
29963017
}) {
29973018
}
3019+
3020+
/**
3021+
* Returns a number.
3022+
* @param {Object} props Props.
3023+
* @param {Object} props.prop Prop.
3024+
* @param {string} props.prop.a String.
3025+
* @param {string} props.prop.b String.
3026+
* @return {number} A number.
3027+
*/
3028+
export function testFn1 ({ prop = { a: 1, b: 2 } }) {
3029+
}
3030+
// Options: [{"useDefaultObjectProperties":true}]
29983031
````
29993032

30003033

@@ -11930,14 +11963,20 @@ implied to be `false` (i.e., the inside of the roots will not be checked
1193011963
either, e.g., it will also not complain if `a` or `b` do not have their own
1193111964
documentation). Defaults to `true`.
1193211965

11933-
| | |
11934-
| -------- | ------------------------------------------------------------------------------------------------------------- |
11966+
<a name="eslint-plugin-jsdoc-rules-require-param-options-26-usedefaultobjectproperties-1"></a>
11967+
##### <code>useDefaultObjectProperties</code>
11968+
11969+
Set to `true` if you wish to expect documentation of properties on objects
11970+
supplied as default values. Defaults to `false`.
11971+
11972+
| | |
11973+
| -------- | ----------------------------------------------------------------------------- |
1193511974
| Context | `ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`; others when `contexts` option enabled |
11936-
| Tags | `param` |
11937-
| Aliases | `arg`, `argument` |
11938-
|Recommended|true|
11939-
| Options | `autoIncrementBase`, `checkDestructured`, `checkDestructuredRoots`, `contexts`, `enableFixer`, `enableRootFixer`, `enableRestElementFixer`, `checkRestProperty`, `exemptedBy`, `checkConstructors`, `checkGetters`, `checkSetters`, `checkTypesPattern`, `unnamedRootBase` |
11940-
| Settings | `overrideReplacesDocs`, `augmentsExtendsReplacesDocs`, `implementsReplacesDocs` |
11975+
| Tags | `param` |
11976+
| Aliases | `arg`, `argument` |
11977+
|Recommended | true|
11978+
| Options | `autoIncrementBase`, `checkDestructured`, `checkDestructuredRoots`, `contexts`, `enableFixer`, `enableRootFixer`, `enableRestElementFixer`, `checkRestProperty`, `exemptedBy`, `checkConstructors`, `checkGetters`, `checkSetters`, `checkTypesPattern`, `unnamedRootBase`, `useDefaultObjectProperties`|
11979+
| Settings | `overrideReplacesDocs`, `augmentsExtendsReplacesDocs`, `implementsReplacesDocs`|
1194111980

1194211981
The following patterns are considered problems:
1194311982

@@ -12582,6 +12621,17 @@ function quux ({ foo: { bar } }) {}
1258212621
*/
1258312622
function foo({ foo: { bar: { baz } }}) {}
1258412623
// Message: Missing JSDoc @param "options.foo.bar.baz" declaration.
12624+
12625+
/**
12626+
* Returns a number.
12627+
* @param {Object} props Props.
12628+
* @param {Object} props.prop Prop.
12629+
* @return {number} A number.
12630+
*/
12631+
export function testFn1 ({ prop = { a: 1, b: 2 } }) {
12632+
}
12633+
// Options: [{"useDefaultObjectProperties":true}]
12634+
// Message: Missing JSDoc @param "props.prop.a" declaration.
1258512635
````
1258612636

1258712637
The following patterns are not considered problems:
@@ -13195,6 +13245,16 @@ function Item({
1319513245
defaulting: [quux, xyz] = []
1319613246
}) {
1319713247
}
13248+
13249+
/**
13250+
* Returns a number.
13251+
* @param {Object} props Props.
13252+
* @param {Object} props.prop Prop.
13253+
* @return {number} A number.
13254+
*/
13255+
export function testFn1 ({ prop = { a: 1, b: 2 } }) {
13256+
}
13257+
// Options: [{"useDefaultObjectProperties":false}]
1319813258
````
1319913259

1320013260

src/iterateJsdoc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,8 @@ const getUtils = (
299299
return jsdocUtils.flattenRoots(params);
300300
};
301301

302-
utils.getFunctionParameterNames = () => {
303-
return jsdocUtils.getFunctionParameterNames(node);
302+
utils.getFunctionParameterNames = (useDefaultObjectProperties) => {
303+
return jsdocUtils.getFunctionParameterNames(node, useDefaultObjectProperties);
304304
};
305305

306306
utils.hasParams = () => {

src/jsdocUtils.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ const getPropertiesFromPropertySignature = (propSignature): T => {
9696
return propSignature.key.name;
9797
};
9898

99-
const getFunctionParameterNames = (functionNode : Object) : Array<T> => {
99+
const getFunctionParameterNames = (
100+
functionNode : Object, checkDefaultObjects: Boolean,
101+
) : Array<T> => {
100102
// eslint-disable-next-line complexity
101103
const getParamName = (param, isProperty) => {
102104
if (_.has(param, 'typeAnnotation') || _.has(param, 'left.typeAnnotation')) {
@@ -149,12 +151,20 @@ const getFunctionParameterNames = (functionNode : Object) : Array<T> => {
149151
})];
150152
}
151153
if (param.value.type === 'AssignmentPattern') {
152-
if (param.value.left.type === 'ObjectPattern') {
154+
switch (param.value.left.type) {
155+
case 'Identifier':
156+
// Default parameter
157+
if (checkDefaultObjects && param.value.right.type === 'ObjectExpression') {
158+
return [param.key.name, param.value.right.properties.map((prop) => {
159+
return getParamName(prop, isProperty);
160+
})];
161+
}
162+
break;
163+
case 'ObjectPattern':
153164
return [param.key.name, param.value.left.properties.map((prop) => {
154165
return getParamName(prop, isProperty);
155166
})];
156-
}
157-
if (param.value.left.type === 'ArrayPattern') {
167+
case 'ArrayPattern':
158168
return [param.key.name, param.value.left.elements.map((prop, idx) => {
159169
return {
160170
name: idx,

src/rules/checkParamNames.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ const validateParameterNames = (
108108
if (!hasPropertyRest || checkRestProperty) {
109109
actualNames.forEach((name, idx) => {
110110
const match = name.startsWith(tag.name.trim() + '.');
111-
if (match && !expectedNames.some(utils.comparePaths(name)) && !utils.comparePaths(name)(tag.name)) {
111+
if (match && !expectedNames.some(
112+
utils.comparePaths(name),
113+
) && !utils.comparePaths(name)(tag.name)) {
112114
extraProperties.push([name, paramTags[idx][1]]);
113115
}
114116
});
@@ -219,6 +221,7 @@ export default iterateJsdoc(({
219221
checkRestProperty = false,
220222
checkTypesPattern = '/^(?:[oO]bject|[aA]rray|PlainObject|Generic(?:Object|Array))$/',
221223
enableFixer = false,
224+
useDefaultObjectProperties = false,
222225
} = context.options[0] || {};
223226

224227
const lastSlashPos = checkTypesPattern.lastIndexOf('/');
@@ -230,7 +233,7 @@ export default iterateJsdoc(({
230233
if (!jsdocParameterNamesDeep.length) {
231234
return;
232235
}
233-
const functionParameterNames = utils.getFunctionParameterNames();
236+
const functionParameterNames = utils.getFunctionParameterNames(useDefaultObjectProperties);
234237
const targetTagName = utils.getPreferredTagName({tagName: 'param'});
235238
const isError = validateParameterNames(
236239
targetTagName,
@@ -278,6 +281,9 @@ export default iterateJsdoc(({
278281
enableFixer: {
279282
type: 'boolean',
280283
},
284+
useDefaultObjectProperties: {
285+
type: 'boolean',
286+
},
281287
},
282288
type: 'object',
283289
},

src/rules/requireParam.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export default iterateJsdoc(({
2424
utils,
2525
context,
2626
}) => {
27-
const functionParameterNames = utils.getFunctionParameterNames();
2827
const preferredTagName = utils.getPreferredTagName({tagName: 'param'});
2928
if (!preferredTagName) {
3029
return;
@@ -57,6 +56,7 @@ export default iterateJsdoc(({
5756
enableRootFixer = true,
5857
enableRestElementFixer = true,
5958
unnamedRootBase = ['root'],
59+
useDefaultObjectProperties = false,
6060
} = context.options[0] || {};
6161

6262
const lastSlashPos = checkTypesPattern.lastIndexOf('/');
@@ -65,6 +65,7 @@ export default iterateJsdoc(({
6565
new RegExp(checkTypesPattern.slice(1, lastSlashPos), checkTypesPattern.slice(lastSlashPos + 1));
6666

6767
const missingTags = [];
68+
const functionParameterNames = utils.getFunctionParameterNames(useDefaultObjectProperties);
6869
const flattenedRoots = utils.flattenRoots(functionParameterNames).names;
6970

7071
const paramIndex = {};
@@ -372,6 +373,9 @@ export default iterateJsdoc(({
372373
},
373374
type: 'array',
374375
},
376+
useDefaultObjectProperties: {
377+
type: 'boolean',
378+
},
375379
},
376380
type: 'object',
377381
},

test/rules/assertions/checkParamNames.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,34 @@ export default {
943943
},
944944
],
945945
},
946+
{
947+
code: `
948+
/**
949+
* Returns a number.
950+
* @param {Object} props Props.
951+
* @param {Object} props.prop Prop.
952+
* @param {string} props.prop.a String.
953+
* @param {string} props.prop.b String.
954+
* @return {number} A number.
955+
*/
956+
export function testFn1 ({ prop = { a: 1, b: 2 } }) {
957+
}
958+
`,
959+
errors: [
960+
{
961+
message: '@param "props.prop.a" does not exist on props',
962+
},
963+
{
964+
message: '@param "props.prop.b" does not exist on props',
965+
},
966+
],
967+
options: [{
968+
useDefaultObjectProperties: false,
969+
}],
970+
parserOptions: {
971+
sourceType: 'module',
972+
},
973+
},
946974
],
947975
valid: [
948976
{
@@ -1423,5 +1451,25 @@ export default {
14231451
}
14241452
`,
14251453
},
1454+
{
1455+
code: `
1456+
/**
1457+
* Returns a number.
1458+
* @param {Object} props Props.
1459+
* @param {Object} props.prop Prop.
1460+
* @param {string} props.prop.a String.
1461+
* @param {string} props.prop.b String.
1462+
* @return {number} A number.
1463+
*/
1464+
export function testFn1 ({ prop = { a: 1, b: 2 } }) {
1465+
}
1466+
`,
1467+
options: [{
1468+
useDefaultObjectProperties: true,
1469+
}],
1470+
parserOptions: {
1471+
sourceType: 'module',
1472+
},
1473+
},
14261474
],
14271475
};

0 commit comments

Comments
 (0)