Skip to content

Commit 3971ce1

Browse files
dstaleybrettz9
authored andcommitted
add support for checking destructuring props and TS object literal props
1 parent 3c9d20f commit 3971ce1

File tree

4 files changed

+246
-5
lines changed

4 files changed

+246
-5
lines changed

src/jsdocUtils.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,22 @@ import WarnSettings from './WarnSettings';
44

55
type ParserMode = "jsdoc"|"typescript"|"closure";
66

7-
const getFunctionParameterNames = (functionNode : Object) : Array<string> => {
7+
const getFunctionParameterNames = (functionNode : Object) : Array<string | [?string, Array<string>]> => {
88
const getParamName = (param) => {
9+
if (_.has(param, 'typeAnnotation')) {
10+
const typeAnnotation = param.typeAnnotation;
11+
if (typeAnnotation.typeAnnotation.type === 'TSTypeLiteral') {
12+
const propertyNames = typeAnnotation.typeAnnotation.members.map((member) => {
13+
return member.key.name;
14+
});
15+
if (_.has(param, 'name')) {
16+
return [param.name, propertyNames];
17+
}
18+
19+
return [undefined, propertyNames];
20+
}
21+
}
22+
923
if (_.has(param, 'name')) {
1024
return param.name;
1125
}
@@ -15,6 +29,12 @@ const getFunctionParameterNames = (functionNode : Object) : Array<string> => {
1529
}
1630

1731
if (param.type === 'ObjectPattern' || _.get(param, 'left.type') === 'ObjectPattern') {
32+
if (param.properties) {
33+
return [undefined, param.properties.map((prop) => {
34+
return prop.value.name;
35+
})];
36+
}
37+
1838
return '<ObjectPattern>';
1939
}
2040

src/rules/checkParamNames.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,51 @@ const validateParameterNames = (
4949
return true;
5050
}
5151

52+
if (Array.isArray(functionParameterName)) {
53+
const [parameterName, properties] = functionParameterName;
54+
const tagName = parameterName ? parameterName : tag.name.trim();
55+
const expectedNames = properties.map((name) => {
56+
return `${tagName}.${name}`;
57+
});
58+
const actualNames = paramTags.map(([, paramTag]) => {
59+
return paramTag.name.trim();
60+
});
61+
62+
const missingProperties = [];
63+
expectedNames.forEach((name) => {
64+
if (!actualNames.includes(name)) {
65+
missingProperties.push(name);
66+
}
67+
});
68+
69+
const extraProperties = [];
70+
actualNames.filter((name) => {
71+
return name.includes(tag.name.trim());
72+
}).forEach((name) => {
73+
if (!expectedNames.includes(name) && name !== tag.name) {
74+
extraProperties.push(name);
75+
}
76+
});
77+
78+
if (missingProperties.length) {
79+
missingProperties.forEach((missingProperty) => {
80+
report(`Missing @${targetTagName} "${missingProperty}"`, null, tag);
81+
});
82+
83+
return true;
84+
}
85+
86+
if (extraProperties.length) {
87+
extraProperties.forEach((extraProperty) => {
88+
report(`@${targetTagName} "${extraProperty}" does not exist on ${tag.name}`, null, tag);
89+
});
90+
91+
return true;
92+
}
93+
94+
return false;
95+
}
96+
5297
if (functionParameterName === '<ObjectPattern>' || functionParameterName === '<ArrayPattern>') {
5398
return false;
5499
}

src/rules/requireParam.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ export default iterateJsdoc(({
4141
const missingTags = [];
4242

4343
functionParameterNames.forEach((functionParameterName, functionParameterIdx) => {
44-
if (['<ObjectPattern>', '<ArrayPattern>'].includes(functionParameterName)) {
44+
if (
45+
['<ObjectPattern>', '<ArrayPattern>'].includes(functionParameterName) ||
46+
Array.isArray(functionParameterName)
47+
) {
4548
return;
4649
}
4750

test/rules/assertions/checkParamNames.js

Lines changed: 176 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ export default {
231231
* @param cfg.foo
232232
* @param cfg.foo
233233
*/
234-
function quux ({foo, bar}) {
234+
function quux ({foo}) {
235235
236236
}
237237
`,
@@ -242,6 +242,23 @@ export default {
242242
},
243243
],
244244
},
245+
{
246+
code: `
247+
/**
248+
* @param cfg
249+
* @param cfg.foo
250+
*/
251+
function quux ({foo, bar}) {
252+
253+
}
254+
`,
255+
errors: [
256+
{
257+
line: 3,
258+
message: 'Missing @param "cfg.bar"',
259+
},
260+
],
261+
},
245262
{
246263
code: `
247264
/**
@@ -250,7 +267,7 @@ export default {
250267
* @param [cfg.foo]
251268
* @param baz
252269
*/
253-
function quux ({foo, bar}, baz) {
270+
function quux ({foo}, baz) {
254271
255272
}
256273
`,
@@ -266,20 +283,56 @@ export default {
266283
/**
267284
* @param cfg
268285
* @param cfg.foo
269-
* @param [cfg.foo="with a default"]
270286
* @param baz
271287
*/
272288
function quux ({foo, bar}, baz) {
273289
274290
}
275291
`,
292+
errors: [
293+
{
294+
line: 3,
295+
message: 'Missing @param "cfg.bar"',
296+
},
297+
],
298+
},
299+
{
300+
code: `
301+
/**
302+
* @param cfg
303+
* @param cfg.foo
304+
* @param [cfg.foo="with a default"]
305+
* @param baz
306+
*/
307+
function quux ({foo}, baz) {
308+
309+
}
310+
`,
276311
errors: [
277312
{
278313
line: 5,
279314
message: 'Duplicate @param "cfg.foo"',
280315
},
281316
],
282317
},
318+
{
319+
code: `
320+
/**
321+
* @param cfg
322+
* @param [cfg.foo="with a default"]
323+
* @param baz
324+
*/
325+
function quux ({foo, bar}, baz) {
326+
327+
}
328+
`,
329+
errors: [
330+
{
331+
line: 3,
332+
message: 'Missing @param "cfg.bar"',
333+
},
334+
],
335+
},
283336
{
284337
code: `
285338
export class SomeClass {
@@ -300,6 +353,96 @@ export default {
300353
sourceType: 'module',
301354
},
302355
},
356+
{
357+
code: `
358+
export class SomeClass {
359+
/**
360+
* @param prop
361+
* @param prop.foo
362+
*/
363+
constructor(prop: { foo: string, bar: string }) {}
364+
}
365+
`,
366+
errors: [
367+
{
368+
line: 4,
369+
message: 'Missing @param "prop.bar"',
370+
},
371+
],
372+
parser: require.resolve('@typescript-eslint/parser'),
373+
parserOptions: {
374+
sourceType: 'module',
375+
},
376+
},
377+
{
378+
code: `
379+
export class SomeClass {
380+
/**
381+
* @param prop
382+
* @param prop.foo
383+
*/
384+
constructor(prop: { foo: string, bar: string }) {}
385+
}
386+
`,
387+
errors: [
388+
{
389+
line: 4,
390+
message: 'Missing @param "prop.bar"',
391+
},
392+
],
393+
parser: require.resolve('@typescript-eslint/parser'),
394+
parserOptions: {
395+
sourceType: 'module',
396+
},
397+
},
398+
{
399+
code: `
400+
export class SomeClass {
401+
/**
402+
* @param prop
403+
* @param prop.foo
404+
* @param prop.bar
405+
*/
406+
constructor(options: { foo: string, bar: string }) {}
407+
}
408+
`,
409+
errors: [
410+
{
411+
line: 4,
412+
message: 'Missing @param "options.foo"',
413+
},
414+
{
415+
line: 4,
416+
message: 'Missing @param "options.bar"',
417+
},
418+
],
419+
parser: require.resolve('@typescript-eslint/parser'),
420+
parserOptions: {
421+
sourceType: 'module',
422+
},
423+
},
424+
{
425+
code: `
426+
export class SomeClass {
427+
/**
428+
* @param options
429+
* @param options.foo
430+
* @param options.bar
431+
*/
432+
constructor(options: { foo: string }) {}
433+
}
434+
`,
435+
errors: [
436+
{
437+
line: 4,
438+
message: '@param "options.bar" does not exist on options',
439+
},
440+
],
441+
parser: require.resolve('@typescript-eslint/parser'),
442+
parserOptions: {
443+
sourceType: 'module',
444+
},
445+
},
303446
{
304447
code: `
305448
/**
@@ -409,6 +552,8 @@ export default {
409552
code: `
410553
/**
411554
* @param foo
555+
* @param foo.a
556+
* @param foo.b
412557
*/
413558
function quux ({a, b}) {
414559
@@ -475,6 +620,22 @@ export default {
475620
sourceType: 'module',
476621
},
477622
},
623+
{
624+
code: `
625+
export class SomeClass {
626+
/**
627+
* @param options
628+
* @param options.foo
629+
* @param options.bar
630+
*/
631+
constructor(options: { foo: string, bar: string }) {}
632+
}
633+
`,
634+
parser: require.resolve('@typescript-eslint/parser'),
635+
parserOptions: {
636+
sourceType: 'module',
637+
},
638+
},
478639
{
479640
code: `
480641
/**
@@ -501,5 +662,17 @@ export default {
501662
},
502663
],
503664
},
665+
{
666+
code: `
667+
/**
668+
* @param cfg
669+
* @param cfg.foo
670+
* @param baz
671+
*/
672+
function quux ({foo}, baz) {
673+
674+
}
675+
`,
676+
},
504677
],
505678
};

0 commit comments

Comments
 (0)