Skip to content

Commit 116b3e8

Browse files
committedAug 2, 2024·
feat(require-template): check callback/function tag block content
1 parent 058018b commit 116b3e8

File tree

4 files changed

+134
-23
lines changed

4 files changed

+134
-23
lines changed
 

‎.README/rules/require-template.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ or
2020

2121
Note that in the latter TypeScript-flavor JavaScript example, there is no way
2222
for us to firmly distinguish between `D` and `V` as type parameters or as some
23-
other identifiers, so we use an algorithm that any single capital letters
24-
are assumed to be templates.
23+
other identifiers, so we use an algorithm that assumes that any single capital
24+
letters are templates.
2525

2626
## Options
2727

‎docs/rules/require-template.md

+33-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ or
2222

2323
Note that in the latter TypeScript-flavor JavaScript example, there is no way
2424
for us to firmly distinguish between `D` and `V` as type parameters or as some
25-
other identifiers, so we use an algorithm that any single capital letters
26-
are assumed to be templates.
25+
other identifiers, so we use an algorithm that assumes that any single capital
26+
letters are templates.
2727

2828
<a name="user-content-require-template-options"></a>
2929
<a name="require-template-options"></a>
@@ -199,6 +199,18 @@ export default class <NumType> {
199199
add: (x: NumType, y: NumType) => NumType;
200200
}
201201
// Message: Missing @template NumType
202+
203+
/**
204+
* @callback
205+
* @param {[D, V | undefined]} someParam
206+
*/
207+
// Message: Missing @template D
208+
209+
/**
210+
* @callback
211+
* @returns {[D, V | undefined]}
212+
*/
213+
// Message: Missing @template D
202214
````
203215

204216

@@ -323,5 +335,24 @@ export default class <NumType> {
323335
zeroValue: NumType;
324336
add: (x: NumType, y: NumType) => NumType;
325337
}
338+
339+
/**
340+
* @callback
341+
* @template D
342+
* @template V
343+
* @param {[D, V | undefined]} someParam
344+
*/
345+
346+
/**
347+
* @callback
348+
* @template D
349+
* @template V
350+
* @returns {[D, V | undefined]}
351+
*/
352+
353+
/**
354+
* @callback
355+
* @returns {[Something | undefined]}
356+
*/
326357
````
327358

‎src/rules/requireTemplate.js

+35-19
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,6 @@ export default iterateJsdoc(({
8989
}
9090
};
9191

92-
const typedefTags = utils.getTags('typedef');
93-
if (!typedefTags.length || typedefTags.length >= 2) {
94-
handleTypes();
95-
return;
96-
}
97-
9892
const usedNameToTag = new Map();
9993

10094
/**
@@ -124,23 +118,45 @@ export default iterateJsdoc(({
124118
});
125119
};
126120

127-
const potentialTypedef = typedefTags[0];
128-
checkForUsedTypes(potentialTypedef);
121+
/**
122+
* @param {string[]} tagNames
123+
*/
124+
const checkTagsAndTemplates = (tagNames) => {
125+
for (const tagName of tagNames) {
126+
const preferredTagName = /** @type {string} */ (utils.getPreferredTagName({
127+
tagName,
128+
}));
129+
const matchingTags = utils.getTags(preferredTagName);
130+
for (const matchingTag of matchingTags) {
131+
checkForUsedTypes(matchingTag);
132+
}
133+
}
134+
135+
// Could check against whitelist/blacklist
136+
for (const usedName of usedNames) {
137+
if (!templateNames.includes(usedName)) {
138+
report(`Missing @template ${usedName}`, null, usedNameToTag.get(usedName));
139+
}
140+
}
141+
};
129142

130-
const tagName = /** @type {string} */ (utils.getPreferredTagName({
131-
tagName: 'property',
132-
}));
133-
const propertyTags = utils.getTags(tagName);
134-
for (const propertyTag of propertyTags) {
135-
checkForUsedTypes(propertyTag);
143+
const callbackTags = utils.getTags('callback');
144+
const functionTags = utils.getTags('function');
145+
if (callbackTags.length || functionTags.length) {
146+
checkTagsAndTemplates(['param', 'returns']);
147+
return;
136148
}
137149

138-
// Could check against whitelist/blacklist
139-
for (const usedName of usedNames) {
140-
if (!templateNames.includes(usedName)) {
141-
report(`Missing @template ${usedName}`, null, usedNameToTag.get(usedName));
142-
}
150+
const typedefTags = utils.getTags('typedef');
151+
if (!typedefTags.length || typedefTags.length >= 2) {
152+
handleTypes();
153+
return;
143154
}
155+
156+
const potentialTypedef = typedefTags[0];
157+
checkForUsedTypes(potentialTypedef);
158+
159+
checkTagsAndTemplates(['property']);
144160
}, {
145161
iterateAllJsdocs: true,
146162
meta: {

‎test/rules/assertions/requireTemplate.js

+64
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,42 @@ export default {
365365
parser: typescriptEslintParser
366366
},
367367
},
368+
{
369+
code: `
370+
/**
371+
* @callback
372+
* @param {[D, V | undefined]} someParam
373+
*/
374+
`,
375+
errors: [
376+
{
377+
line: 4,
378+
message: 'Missing @template D',
379+
},
380+
{
381+
line: 4,
382+
message: 'Missing @template V',
383+
},
384+
],
385+
},
386+
{
387+
code: `
388+
/**
389+
* @callback
390+
* @returns {[D, V | undefined]}
391+
*/
392+
`,
393+
errors: [
394+
{
395+
line: 4,
396+
message: 'Missing @template D',
397+
},
398+
{
399+
line: 4,
400+
message: 'Missing @template V',
401+
},
402+
],
403+
},
368404
],
369405
valid: [
370406
{
@@ -568,5 +604,33 @@ export default {
568604
parser: typescriptEslintParser
569605
},
570606
},
607+
{
608+
code: `
609+
/**
610+
* @callback
611+
* @template D
612+
* @template V
613+
* @param {[D, V | undefined]} someParam
614+
*/
615+
`,
616+
},
617+
{
618+
code: `
619+
/**
620+
* @callback
621+
* @template D
622+
* @template V
623+
* @returns {[D, V | undefined]}
624+
*/
625+
`,
626+
},
627+
{
628+
code: `
629+
/**
630+
* @callback
631+
* @returns {[Something | undefined]}
632+
*/
633+
`,
634+
},
571635
],
572636
};

0 commit comments

Comments
 (0)
Please sign in to comment.