eslint-plugin-jsdoc 61.2.0 → 61.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/iterateJsdoc.d.ts +2 -2
- package/dist/cjs/jsdocUtils.d.ts +2 -1
- package/dist/iterateJsdoc.cjs +9 -4
- package/dist/iterateJsdoc.cjs.map +1 -1
- package/dist/iterateJsdoc.d.ts +2 -2
- package/dist/jsdocUtils.cjs +11 -1
- package/dist/jsdocUtils.cjs.map +1 -1
- package/dist/jsdocUtils.d.ts +2 -1
- package/dist/rules/checkIndentation.cjs +84 -7
- package/dist/rules/checkIndentation.cjs.map +1 -1
- package/dist/rules/requireParam.cjs +3 -8
- package/dist/rules/requireParam.cjs.map +1 -1
- package/dist/rules/tagLines.cjs +10 -9
- package/dist/rules/tagLines.cjs.map +1 -1
- package/dist/rules.d.ts +4 -0
- package/dist/to-valid-identifier.cjs +3 -1
- package/package.json +1 -1
- package/src/iterateJsdoc.js +9 -3
- package/src/jsdocUtils.js +15 -1
- package/src/rules/checkIndentation.js +89 -7
- package/src/rules/requireParam.js +5 -14
- package/src/rules/tagLines.js +13 -9
- package/src/rules.d.ts +4 -0
package/src/iterateJsdoc.js
CHANGED
|
@@ -170,7 +170,8 @@ import esquery from 'esquery';
|
|
|
170
170
|
* seedTokens: (
|
|
171
171
|
* tokens?: Partial<import('comment-parser').Tokens>
|
|
172
172
|
* ) => import('comment-parser').Tokens,
|
|
173
|
-
* descLines: string[]
|
|
173
|
+
* descLines: string[],
|
|
174
|
+
* postDelims: string[]
|
|
174
175
|
* ) => import('comment-parser').Line[]} setter
|
|
175
176
|
* @returns {void}
|
|
176
177
|
*/
|
|
@@ -257,6 +258,7 @@ import esquery from 'esquery';
|
|
|
257
258
|
/**
|
|
258
259
|
* @callback GetFunctionParameterNames
|
|
259
260
|
* @param {boolean} [useDefaultObjectProperties]
|
|
261
|
+
* @param {boolean} [ignoreInterfacedParameters]
|
|
260
262
|
* @returns {import('./jsdocUtils.js').ParamNameInfo[]}
|
|
261
263
|
*/
|
|
262
264
|
|
|
@@ -927,6 +929,8 @@ const getUtils = (
|
|
|
927
929
|
utils.setBlockDescription = (setter) => {
|
|
928
930
|
/** @type {string[]} */
|
|
929
931
|
const descLines = [];
|
|
932
|
+
/** @type {string[]} */
|
|
933
|
+
const postDelims = [];
|
|
930
934
|
/**
|
|
931
935
|
* @type {undefined|Integer}
|
|
932
936
|
*/
|
|
@@ -973,6 +977,7 @@ const getUtils = (
|
|
|
973
977
|
return true;
|
|
974
978
|
}
|
|
975
979
|
|
|
980
|
+
postDelims.push(postDelimiter);
|
|
976
981
|
descLines.push(description);
|
|
977
982
|
return false;
|
|
978
983
|
});
|
|
@@ -993,6 +998,7 @@ const getUtils = (
|
|
|
993
998
|
(info),
|
|
994
999
|
seedTokens,
|
|
995
1000
|
descLines,
|
|
1001
|
+
postDelims,
|
|
996
1002
|
),
|
|
997
1003
|
);
|
|
998
1004
|
}
|
|
@@ -1369,8 +1375,8 @@ const getUtils = (
|
|
|
1369
1375
|
utils.flattenRoots = jsdocUtils.flattenRoots;
|
|
1370
1376
|
|
|
1371
1377
|
/** @type {GetFunctionParameterNames} */
|
|
1372
|
-
utils.getFunctionParameterNames = (useDefaultObjectProperties) => {
|
|
1373
|
-
return jsdocUtils.getFunctionParameterNames(node, useDefaultObjectProperties);
|
|
1378
|
+
utils.getFunctionParameterNames = (useDefaultObjectProperties, ignoreInterfacedParameters) => {
|
|
1379
|
+
return jsdocUtils.getFunctionParameterNames(node, useDefaultObjectProperties, ignoreInterfacedParameters);
|
|
1374
1380
|
};
|
|
1375
1381
|
|
|
1376
1382
|
/** @type {HasParams} */
|
package/src/jsdocUtils.js
CHANGED
|
@@ -203,11 +203,12 @@ const getPropertiesFromPropertySignature = (propSignature) => {
|
|
|
203
203
|
/**
|
|
204
204
|
* @param {ESTreeOrTypeScriptNode|null} functionNode
|
|
205
205
|
* @param {boolean} [checkDefaultObjects]
|
|
206
|
+
* @param {boolean} [ignoreInterfacedParameters]
|
|
206
207
|
* @throws {Error}
|
|
207
208
|
* @returns {ParamNameInfo[]}
|
|
208
209
|
*/
|
|
209
210
|
const getFunctionParameterNames = (
|
|
210
|
-
functionNode, checkDefaultObjects,
|
|
211
|
+
functionNode, checkDefaultObjects, ignoreInterfacedParameters,
|
|
211
212
|
) => {
|
|
212
213
|
/* eslint-disable complexity -- Temporary */
|
|
213
214
|
/**
|
|
@@ -230,6 +231,19 @@ const getFunctionParameterNames = (
|
|
|
230
231
|
const hasLeftTypeAnnotation = 'left' in param && 'typeAnnotation' in param.left;
|
|
231
232
|
|
|
232
233
|
if ('typeAnnotation' in param || hasLeftTypeAnnotation) {
|
|
234
|
+
if (ignoreInterfacedParameters && 'typeAnnotation' in param &&
|
|
235
|
+
param.typeAnnotation) {
|
|
236
|
+
// No-op
|
|
237
|
+
return [
|
|
238
|
+
undefined, {
|
|
239
|
+
hasPropertyRest: false,
|
|
240
|
+
hasRestElement: false,
|
|
241
|
+
names: [],
|
|
242
|
+
rests: [],
|
|
243
|
+
},
|
|
244
|
+
];
|
|
245
|
+
}
|
|
246
|
+
|
|
233
247
|
const typeAnnotation = hasLeftTypeAnnotation ?
|
|
234
248
|
/** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
|
235
249
|
param.left
|
|
@@ -25,6 +25,17 @@ const maskCodeBlocks = (str) => {
|
|
|
25
25
|
});
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @param {string[]} lines
|
|
30
|
+
* @param {number} lineIndex
|
|
31
|
+
* @returns {number}
|
|
32
|
+
*/
|
|
33
|
+
const getLineNumber = (lines, lineIndex) => {
|
|
34
|
+
const precedingText = lines.slice(0, lineIndex).join('\n');
|
|
35
|
+
const lineBreaks = precedingText.match(/\n/gv) || [];
|
|
36
|
+
return lineBreaks.length + 1;
|
|
37
|
+
};
|
|
38
|
+
|
|
28
39
|
export default iterateJsdoc(({
|
|
29
40
|
context,
|
|
30
41
|
jsdocNode,
|
|
@@ -32,21 +43,88 @@ export default iterateJsdoc(({
|
|
|
32
43
|
sourceCode,
|
|
33
44
|
}) => {
|
|
34
45
|
const options = context.options[0] || {};
|
|
35
|
-
const /** @type {{excludeTags: string[]}} */ {
|
|
46
|
+
const /** @type {{excludeTags: string[], allowIndentedSections: boolean}} */ {
|
|
47
|
+
allowIndentedSections = false,
|
|
36
48
|
excludeTags = [
|
|
37
49
|
'example',
|
|
38
50
|
],
|
|
39
51
|
} = options;
|
|
40
52
|
|
|
41
|
-
const reg = /^(?:\/?\**|[ \t]*)\*[ \t]{2}/gmv;
|
|
42
53
|
const textWithoutCodeBlocks = maskCodeBlocks(sourceCode.getText(jsdocNode));
|
|
43
54
|
const text = excludeTags.length ? maskExcludedContent(textWithoutCodeBlocks, excludeTags) : textWithoutCodeBlocks;
|
|
44
55
|
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
if (allowIndentedSections) {
|
|
57
|
+
// When allowIndentedSections is enabled, only check for indentation on tag lines
|
|
58
|
+
// and the very first line of the main description
|
|
59
|
+
const lines = text.split('\n');
|
|
60
|
+
let hasSeenContent = false;
|
|
61
|
+
let currentSectionIndent = null;
|
|
62
|
+
|
|
63
|
+
for (const [
|
|
64
|
+
lineIndex,
|
|
65
|
+
line,
|
|
66
|
+
] of lines.entries()) {
|
|
67
|
+
// Check for indentation (two or more spaces after *)
|
|
68
|
+
const indentMatch = line.match(/^(?:\/?\**|[\t ]*)\*([\t ]{2,})/v);
|
|
69
|
+
|
|
70
|
+
if (indentMatch) {
|
|
71
|
+
// Check what comes after the indentation
|
|
72
|
+
const afterIndent = line.slice(indentMatch[0].length);
|
|
73
|
+
const indentAmount = indentMatch[1].length;
|
|
74
|
+
|
|
75
|
+
// If this is a tag line with indentation, always report
|
|
76
|
+
if (/^@\w+/v.test(afterIndent)) {
|
|
77
|
+
report('There must be no indentation.', null, {
|
|
78
|
+
line: getLineNumber(lines, lineIndex),
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// If we haven't seen any content yet (main description first line) and there's content, report
|
|
84
|
+
if (!hasSeenContent && afterIndent.trim().length > 0) {
|
|
85
|
+
report('There must be no indentation.', null, {
|
|
86
|
+
line: getLineNumber(lines, lineIndex),
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// For continuation lines, check consistency
|
|
92
|
+
if (hasSeenContent && afterIndent.trim().length > 0) {
|
|
93
|
+
if (currentSectionIndent === null) {
|
|
94
|
+
// First indented line in this section, set the indent level
|
|
95
|
+
currentSectionIndent = indentAmount;
|
|
96
|
+
} else if (indentAmount < currentSectionIndent) {
|
|
97
|
+
// Indentation is less than the established level (inconsistent)
|
|
98
|
+
report('There must be no indentation.', null, {
|
|
99
|
+
line: getLineNumber(lines, lineIndex),
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} else if (/^\s*\*\s+\S/v.test(line)) {
|
|
105
|
+
// No indentation on this line, reset section indent tracking
|
|
106
|
+
// (unless it's just whitespace or a closing comment)
|
|
107
|
+
currentSectionIndent = null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Track if we've seen any content (non-whitespace after the *)
|
|
111
|
+
if (/^\s*\*\s+\S/v.test(line)) {
|
|
112
|
+
hasSeenContent = true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Reset section indent when we encounter a tag
|
|
116
|
+
if (/@\w+/v.test(line)) {
|
|
117
|
+
currentSectionIndent = null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
const reg = /^(?:\/?\**|[ \t]*)\*[ \t]{2}/gmv;
|
|
122
|
+
if (reg.test(text)) {
|
|
123
|
+
const lineBreaks = text.slice(0, reg.lastIndex).match(/\n/gv) || [];
|
|
124
|
+
report('There must be no indentation.', null, {
|
|
125
|
+
line: lineBreaks.length,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
50
128
|
}
|
|
51
129
|
}, {
|
|
52
130
|
iterateAllJsdocs: true,
|
|
@@ -59,6 +137,10 @@ export default iterateJsdoc(({
|
|
|
59
137
|
{
|
|
60
138
|
additionalProperties: false,
|
|
61
139
|
properties: {
|
|
140
|
+
allowIndentedSections: {
|
|
141
|
+
description: 'Allows indentation of nested sections on subsequent lines (like bullet lists)',
|
|
142
|
+
type: 'boolean',
|
|
143
|
+
},
|
|
62
144
|
excludeTags: {
|
|
63
145
|
description: `Array of tags (e.g., \`['example', 'description']\`) whose content will be
|
|
64
146
|
"hidden" from the \`check-indentation\` rule. Defaults to \`['example']\`.
|
|
@@ -65,19 +65,10 @@ export default iterateJsdoc(({
|
|
|
65
65
|
useDefaultObjectProperties = false,
|
|
66
66
|
} = context.options[0] || {};
|
|
67
67
|
|
|
68
|
-
if (interfaceExemptsParamsCheck
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
'typeAnnotation' in node.params[0] && node.params[0].typeAnnotation
|
|
73
|
-
) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (node && node.parent?.type === 'VariableDeclarator' &&
|
|
78
|
-
'typeAnnotation' in node.parent.id && node.parent.id.typeAnnotation) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
68
|
+
if (interfaceExemptsParamsCheck && node &&
|
|
69
|
+
node.parent?.type === 'VariableDeclarator' &&
|
|
70
|
+
'typeAnnotation' in node.parent.id && node.parent.id.typeAnnotation) {
|
|
71
|
+
return;
|
|
81
72
|
}
|
|
82
73
|
|
|
83
74
|
const preferredTagName = /** @type {string} */ (utils.getPreferredTagName({
|
|
@@ -87,7 +78,7 @@ export default iterateJsdoc(({
|
|
|
87
78
|
return;
|
|
88
79
|
}
|
|
89
80
|
|
|
90
|
-
const functionParameterNames = utils.getFunctionParameterNames(useDefaultObjectProperties);
|
|
81
|
+
const functionParameterNames = utils.getFunctionParameterNames(useDefaultObjectProperties, interfaceExemptsParamsCheck);
|
|
91
82
|
if (!functionParameterNames.length) {
|
|
92
83
|
return;
|
|
93
84
|
}
|
package/src/rules/tagLines.js
CHANGED
|
@@ -37,18 +37,22 @@ const checkMaxBlockLines = ({
|
|
|
37
37
|
line: excessIndexLine,
|
|
38
38
|
},
|
|
39
39
|
() => {
|
|
40
|
-
utils.setBlockDescription((info, seedTokens, descLines) => {
|
|
40
|
+
utils.setBlockDescription((info, seedTokens, descLines, postDelims) => {
|
|
41
|
+
const newPostDelims = [
|
|
42
|
+
...postDelims.slice(0, excessIndexLine),
|
|
43
|
+
...postDelims.slice(excessIndexLine + excessBlockLines - 1 - maxBlockLines),
|
|
44
|
+
];
|
|
41
45
|
return [
|
|
42
46
|
...descLines.slice(0, excessIndexLine),
|
|
43
47
|
...descLines.slice(excessIndexLine + excessBlockLines - 1 - maxBlockLines),
|
|
44
|
-
].map((desc) => {
|
|
48
|
+
].map((desc, idx) => {
|
|
45
49
|
return {
|
|
46
50
|
number: 0,
|
|
47
51
|
source: '',
|
|
48
52
|
tokens: seedTokens({
|
|
49
53
|
...info,
|
|
50
54
|
description: desc,
|
|
51
|
-
postDelimiter:
|
|
55
|
+
postDelimiter: newPostDelims[idx],
|
|
52
56
|
}),
|
|
53
57
|
};
|
|
54
58
|
});
|
|
@@ -310,15 +314,15 @@ export default iterateJsdoc(({
|
|
|
310
314
|
line: lastDescriptionLine - trailingDiff,
|
|
311
315
|
},
|
|
312
316
|
() => {
|
|
313
|
-
utils.setBlockDescription((info, seedTokens, descLines) => {
|
|
314
|
-
return descLines.slice(0, -trailingDiff).map((desc) => {
|
|
317
|
+
utils.setBlockDescription((info, seedTokens, descLines, postDelims) => {
|
|
318
|
+
return descLines.slice(0, -trailingDiff).map((desc, idx) => {
|
|
315
319
|
return {
|
|
316
320
|
number: 0,
|
|
317
321
|
source: '',
|
|
318
322
|
tokens: seedTokens({
|
|
319
323
|
...info,
|
|
320
324
|
description: desc,
|
|
321
|
-
postDelimiter:
|
|
325
|
+
postDelimiter: postDelims[idx],
|
|
322
326
|
}),
|
|
323
327
|
};
|
|
324
328
|
});
|
|
@@ -332,7 +336,7 @@ export default iterateJsdoc(({
|
|
|
332
336
|
line: lastDescriptionLine,
|
|
333
337
|
},
|
|
334
338
|
() => {
|
|
335
|
-
utils.setBlockDescription((info, seedTokens, descLines) => {
|
|
339
|
+
utils.setBlockDescription((info, seedTokens, descLines, postDelims) => {
|
|
336
340
|
return [
|
|
337
341
|
...descLines,
|
|
338
342
|
...Array.from({
|
|
@@ -340,14 +344,14 @@ export default iterateJsdoc(({
|
|
|
340
344
|
}, () => {
|
|
341
345
|
return '';
|
|
342
346
|
}),
|
|
343
|
-
].map((desc) => {
|
|
347
|
+
].map((desc, idx) => {
|
|
344
348
|
return {
|
|
345
349
|
number: 0,
|
|
346
350
|
source: '',
|
|
347
351
|
tokens: seedTokens({
|
|
348
352
|
...info,
|
|
349
353
|
description: desc,
|
|
350
|
-
postDelimiter: desc.trim() ?
|
|
354
|
+
postDelimiter: desc.trim() ? postDelims[idx] : '',
|
|
351
355
|
}),
|
|
352
356
|
};
|
|
353
357
|
});
|
package/src/rules.d.ts
CHANGED
|
@@ -47,6 +47,10 @@ export interface Rules {
|
|
|
47
47
|
| []
|
|
48
48
|
| [
|
|
49
49
|
{
|
|
50
|
+
/**
|
|
51
|
+
* Allows indentation of nested sections on subsequent lines (like bullet lists)
|
|
52
|
+
*/
|
|
53
|
+
allowIndentedSections?: boolean;
|
|
50
54
|
/**
|
|
51
55
|
* Array of tags (e.g., `['example', 'description']`) whose content will be
|
|
52
56
|
* "hidden" from the `check-indentation` rule. Defaults to `['example']`.
|