Skip to content

Commit

Permalink
feat(check-param-names): add options `disableExtraPropertyReporting…
Browse files Browse the repository at this point in the history
…` to allow extra properties to be documented without error (as long as there are no siblings destructured as for such cases, they will still be expected since the function is not using them)
  • Loading branch information
brettz9 committed Jan 24, 2021
1 parent 33118ef commit 8b2d143
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 18 deletions.
12 changes: 11 additions & 1 deletion .README/rules/check-param-names.md
Expand Up @@ -64,10 +64,20 @@ where instead of destructuring, a whole plain object is supplied as default
value but you wish its keys to be considered as signalling that the properties
are present and can therefore be documented. Defaults to `false`.

##### `disableExtraPropertyReporting`

Whether to check for extra destructured properties. Defaults to `false`. Change
to `true` if you want to be able to document properties which are not actually
destructured. Keep as `false` if you expect properties to be documented in
their own types. Note that extra properties will always be reported if another
item at the same level is destructured as destructuring will prevent other
access and this option is only intended to permit documenting extra properties
that are available and actually used in the function.

|||
|---|---|
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
|Options|`allowExtraTrailingParamDocs`, `checkDestructured`, `checkRestProperty`, `checkTypesPattern`, `useDefaultObjectProperties`|
|Options|`allowExtraTrailingParamDocs`, `checkDestructured`, `checkRestProperty`, `checkTypesPattern`, `useDefaultObjectProperties`, `disableExtraPropertyReporting`|
|Tags|`param`|
|Aliases|`arg`, `argument`|
|Recommended|true|
Expand Down
87 changes: 86 additions & 1 deletion README.md
Expand Up @@ -2274,10 +2274,21 @@ where instead of destructuring, a whole plain object is supplied as default
value but you wish its keys to be considered as signalling that the properties
are present and can therefore be documented. Defaults to `false`.

<a name="eslint-plugin-jsdoc-rules-check-param-names-options-4-disableextrapropertyreporting"></a>
##### <code>disableExtraPropertyReporting</code>

Whether to check for extra destructured properties. Defaults to `false`. Change
to `true` if you want to be able to document properties which are not actually
destructured. Keep as `false` if you expect properties to be documented in
their own types. Note that extra properties will always be reported if another
item at the same level is destructured as destructuring will prevent other
access and this option is only intended to permit documenting extra properties
that are available and actually used in the function.

|||
|---|---|
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
|Options|`allowExtraTrailingParamDocs`, `checkDestructured`, `checkRestProperty`, `checkTypesPattern`, `useDefaultObjectProperties`|
|Options|`allowExtraTrailingParamDocs`, `checkDestructured`, `checkRestProperty`, `checkTypesPattern`, `useDefaultObjectProperties`, `disableExtraPropertyReporting`|
|Tags|`param`|
|Aliases|`arg`, `argument`|
|Recommended|true|
Expand Down Expand Up @@ -2685,6 +2696,68 @@ export function testFn1 ({ prop = { a: 1, b: 2 } }) {
}
// Options: [{"useDefaultObjectProperties":false}]
// Message: @param "props.prop.a" does not exist on props

/**
* @param {object} cfg
* @param {string} cfg.foo
* @param {string} cfg.bar
* @param {object} cfg.extra
*/
function quux ({foo}) {

}
// Message: @param "cfg.bar" does not exist on cfg

/**
* @param {object} cfg
* @param {string} cfg.foo
* @param {string} cfg.bar
* @param {object} cfg.extra
*/
function quux ({foo}) {

}
// Options: [{"disableExtraPropertyReporting":true}]
// Message: @param "cfg.bar" does not exist on cfg

/**
* @param {object} root
* @param {object} root.cfg
* @param {object} root.cfg.a
* @param {string} root.cfg.a.foo
* @param {string} root.cfg.a.bar
* @param {object} root.cfg.a.extra
*/
function quux ({cfg: {a: {foo}}}) {

}
// Message: @param "root.cfg.a.bar" does not exist on root

/**
* @param {object} root
* @param {object} root.cfg
* @param {object} root.cfg.a
* @param {string} root.cfg.a.foo
* @param {string} root.cfg.a.bar
* @param {object} root.cfg.a.extra
*/
function quux ({cfg: {a: {foo}}}) {

}
// Options: [{"disableExtraPropertyReporting":true}]
// Message: @param "root.cfg.a.bar" does not exist on root

/**
* @param {object} root
* @param {object} root.cfg
* @param {string} root.cfg.foo
* @param {string} root.cfg.bar
* @param {object} root.cfg.extra
*/
function quux ({cfg}) {

}
// Message: @param "root.cfg.foo" does not exist on root
````

The following patterns are not considered problems:
Expand Down Expand Up @@ -3028,6 +3101,18 @@ function Item({
export function testFn1 ({ prop = { a: 1, b: 2 } }) {
}
// Options: [{"useDefaultObjectProperties":true}]

/**
* @param {object} root
* @param {object} root.cfg
* @param {string} root.cfg.foo
* @param {string} root.cfg.bar
* @param {object} root.cfg.extra
*/
function quux ({cfg}) {

}
// Options: [{"disableExtraPropertyReporting":true}]
````


Expand Down
42 changes: 26 additions & 16 deletions src/rules/checkParamNames.js
Expand Up @@ -6,6 +6,7 @@ const validateParameterNames = (
checkDestructured : boolean,
checkRestProperty : boolean,
checkTypesRegex : RegExp,
disableExtraPropertyReporting,
enableFixer: boolean,
functionParameterNames : Array<string>, jsdoc, _jsdocNode, utils, report,
) => {
Expand Down Expand Up @@ -104,31 +105,35 @@ const validateParameterNames = (
}
});

const extraProperties = [];
if (!hasPropertyRest || checkRestProperty) {
actualNames.forEach((name, idx) => {
const match = name.startsWith(tag.name.trim() + '.');
if (match && !expectedNames.some(
utils.comparePaths(name),
) && !utils.comparePaths(name)(tag.name)) {
extraProperties.push([name, paramTags[idx][1]]);
}
});
}

const hasMissing = missingProperties.length;
if (hasMissing) {
missingProperties.forEach((missingProperty) => {
report(`Missing @${targetTagName} "${missingProperty}"`, null, tag);
});
}

if (extraProperties.length) {
extraProperties.forEach(([extraProperty, tg]) => {
report(`@${targetTagName} "${extraProperty}" does not exist on ${tag.name}`, null, tg);
if (!hasPropertyRest || checkRestProperty) {
const extraProperties = [];
actualNames.forEach((name, idx) => {
const match = name.startsWith(tag.name.trim() + '.');
if (
match && !expectedNames.some(
utils.comparePaths(name),
) && !utils.comparePaths(name)(tag.name) &&
(!disableExtraPropertyReporting || properties.some((prop) => {
return prop.split('.').length >= name.split('.').length - 1;
}))
) {
extraProperties.push([name, paramTags[idx][1]]);
}
});
if (extraProperties.length) {
extraProperties.forEach(([extraProperty, tg]) => {
report(`@${targetTagName} "${extraProperty}" does not exist on ${tag.name}`, null, tg);
});

return true;
return true;
}
}

return hasMissing;
Expand Down Expand Up @@ -222,6 +227,7 @@ export default iterateJsdoc(({
checkTypesPattern = '/^(?:[oO]bject|[aA]rray|PlainObject|Generic(?:Object|Array))$/',
enableFixer = false,
useDefaultObjectProperties = false,
disableExtraPropertyReporting = false,
} = context.options[0] || {};

const lastSlashPos = checkTypesPattern.lastIndexOf('/');
Expand All @@ -241,6 +247,7 @@ export default iterateJsdoc(({
checkDestructured,
checkRestProperty,
checkTypesRegex,
disableExtraPropertyReporting,
enableFixer,
functionParameterNames,
jsdoc, jsdocNode, utils, report,
Expand Down Expand Up @@ -278,6 +285,9 @@ export default iterateJsdoc(({
checkTypesPattern: {
type: 'string',
},
disableExtraPropertyReporting: {
type: 'boolean',
},
enableFixer: {
type: 'boolean',
},
Expand Down
153 changes: 153 additions & 0 deletions test/rules/assertions/checkParamNames.js
Expand Up @@ -971,6 +971,140 @@ export default {
sourceType: 'module',
},
},
{
code: `
/**
* @param {object} cfg
* @param {string} cfg.foo
* @param {string} cfg.bar
* @param {object} cfg.extra
*/
function quux ({foo}) {
}
`,
errors: [
{
line: 5,
message: '@param "cfg.bar" does not exist on cfg',
},
{
line: 6,
message: '@param "cfg.extra" does not exist on cfg',
},
],
},
{
code: `
/**
* @param {object} cfg
* @param {string} cfg.foo
* @param {string} cfg.bar
* @param {object} cfg.extra
*/
function quux ({foo}) {
}
`,
errors: [
{
line: 5,
message: '@param "cfg.bar" does not exist on cfg',
},
{
line: 6,
message: '@param "cfg.extra" does not exist on cfg',
},
],
options: [
{
disableExtraPropertyReporting: true,
},
],
},
{
code: `
/**
* @param {object} root
* @param {object} root.cfg
* @param {object} root.cfg.a
* @param {string} root.cfg.a.foo
* @param {string} root.cfg.a.bar
* @param {object} root.cfg.a.extra
*/
function quux ({cfg: {a: {foo}}}) {
}
`,
errors: [
{
line: 7,
message: '@param "root.cfg.a.bar" does not exist on root',
},
{
line: 8,
message: '@param "root.cfg.a.extra" does not exist on root',
},
],
},
{
code: `
/**
* @param {object} root
* @param {object} root.cfg
* @param {object} root.cfg.a
* @param {string} root.cfg.a.foo
* @param {string} root.cfg.a.bar
* @param {object} root.cfg.a.extra
*/
function quux ({cfg: {a: {foo}}}) {
}
`,
errors: [
{
line: 7,
message: '@param "root.cfg.a.bar" does not exist on root',
},
{
line: 8,
message: '@param "root.cfg.a.extra" does not exist on root',
},
],
options: [
{
disableExtraPropertyReporting: true,
},
],
},
{
code: `
/**
* @param {object} root
* @param {object} root.cfg
* @param {string} root.cfg.foo
* @param {string} root.cfg.bar
* @param {object} root.cfg.extra
*/
function quux ({cfg}) {
}
`,
errors: [
{
line: 5,
message: '@param "root.cfg.foo" does not exist on root',
},
{
line: 6,
message: '@param "root.cfg.bar" does not exist on root',
},
{
line: 7,
message: '@param "root.cfg.extra" does not exist on root',
},
],
},
],
valid: [
{
Expand Down Expand Up @@ -1471,5 +1605,24 @@ export default {
sourceType: 'module',
},
},
{
code: `
/**
* @param {object} root
* @param {object} root.cfg
* @param {string} root.cfg.foo
* @param {string} root.cfg.bar
* @param {object} root.cfg.extra
*/
function quux ({cfg}) {
}
`,
options: [
{
disableExtraPropertyReporting: true,
},
],
},
],
};

0 comments on commit 8b2d143

Please sign in to comment.