eslint-plugin-jsdoc 57.2.0 → 58.0.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/buildRejectOrPreferRuleDefinition.cjs +349 -0
- package/dist/buildRejectOrPreferRuleDefinition.cjs.map +1 -0
- package/dist/buildRejectOrPreferRuleDefinition.d.ts +9 -0
- package/dist/cjs/buildRejectOrPreferRuleDefinition.d.ts +8 -0
- package/dist/cjs/rules/checkTypes.d.ts +6 -1
- package/dist/generateRuleTypes.cjs +9 -11
- package/dist/generateRuleTypes.cjs.map +1 -1
- package/dist/index-cjs.cjs +33 -3
- package/dist/index-cjs.cjs.map +1 -1
- package/dist/index-esm.cjs +29 -1
- package/dist/index-esm.cjs.map +1 -1
- package/dist/index-esm.d.ts +28 -2
- package/dist/index.cjs +61 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +28 -2
- package/dist/rules/checkTypes.cjs +73 -379
- package/dist/rules/checkTypes.cjs.map +1 -1
- package/dist/rules/checkTypes.d.ts +6 -1
- package/dist/rules.d.ts +42 -0
- package/package.json +1 -1
- package/src/buildRejectOrPreferRuleDefinition.js +472 -0
- package/src/index-cjs.js +35 -3
- package/src/index-esm.js +36 -1
- package/src/index.js +68 -4
- package/src/rules/checkTypes.js +82 -511
- package/src/rules.d.ts +42 -0
package/dist/rules.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export interface Rules {
|
|
2
|
+
/** Checks that `@access` tags have a valid value. */
|
|
3
|
+
"jsdoc/check-access": [];
|
|
4
|
+
|
|
2
5
|
/** Reports invalid alignment of JSDoc block asterisks. */
|
|
3
6
|
"jsdoc/check-alignment":
|
|
4
7
|
| []
|
|
@@ -90,6 +93,9 @@ export interface Rules {
|
|
|
90
93
|
}
|
|
91
94
|
];
|
|
92
95
|
|
|
96
|
+
/** Reports against syntax not valid for the mode (e.g., Google Closure Compiler in non-Closure mode). */
|
|
97
|
+
"jsdoc/check-syntax": [];
|
|
98
|
+
|
|
93
99
|
/** Reports invalid block tag names. */
|
|
94
100
|
"jsdoc/check-tag-names":
|
|
95
101
|
| []
|
|
@@ -102,6 +108,9 @@ export interface Rules {
|
|
|
102
108
|
}
|
|
103
109
|
];
|
|
104
110
|
|
|
111
|
+
/** Checks that any `@template` names are actually used in the connected `@typedef` or type alias. */
|
|
112
|
+
"jsdoc/check-template-names": [];
|
|
113
|
+
|
|
105
114
|
/** Reports invalid types. */
|
|
106
115
|
"jsdoc/check-types":
|
|
107
116
|
| []
|
|
@@ -188,6 +197,9 @@ export interface Rules {
|
|
|
188
197
|
}
|
|
189
198
|
];
|
|
190
199
|
|
|
200
|
+
/** Reports if JSDoc `import()` statements point to a package which is not listed in `dependencies` or `devDependencies` */
|
|
201
|
+
"jsdoc/imports-as-dependencies": [];
|
|
202
|
+
|
|
191
203
|
/** This rule reports doc comments that only restate their attached name. */
|
|
192
204
|
"jsdoc/informative-docs":
|
|
193
205
|
| []
|
|
@@ -300,6 +312,9 @@ export interface Rules {
|
|
|
300
312
|
}
|
|
301
313
|
];
|
|
302
314
|
|
|
315
|
+
/** Detects and removes extra lines of a blank block description */
|
|
316
|
+
"jsdoc/no-blank-block-descriptions": [];
|
|
317
|
+
|
|
303
318
|
/** Removes empty blocks with nothing but possibly line breaks */
|
|
304
319
|
"jsdoc/no-blank-blocks":
|
|
305
320
|
| []
|
|
@@ -395,6 +410,12 @@ export interface Rules {
|
|
|
395
410
|
}
|
|
396
411
|
];
|
|
397
412
|
|
|
413
|
+
/** Reports use of `any` or `*` type */
|
|
414
|
+
"jsdoc/reject-any-type": [];
|
|
415
|
+
|
|
416
|
+
/** Reports use of `Function` type */
|
|
417
|
+
"jsdoc/reject-function-type": [];
|
|
418
|
+
|
|
398
419
|
/** Requires that each JSDoc line starts with an `*`. */
|
|
399
420
|
"jsdoc/require-asterisk-prefix":
|
|
400
421
|
| []
|
|
@@ -542,6 +563,9 @@ export interface Rules {
|
|
|
542
563
|
}
|
|
543
564
|
];
|
|
544
565
|
|
|
566
|
+
/** Requires a type for `@next` tags */
|
|
567
|
+
"jsdoc/require-next-type": [];
|
|
568
|
+
|
|
545
569
|
/** Requires that all function parameters are documented. */
|
|
546
570
|
"jsdoc/require-param":
|
|
547
571
|
| []
|
|
@@ -621,6 +645,18 @@ export interface Rules {
|
|
|
621
645
|
}
|
|
622
646
|
];
|
|
623
647
|
|
|
648
|
+
/** Requires that all `@typedef` and `@namespace` tags have `@property` when their type is a plain `object`, `Object`, or `PlainObject`. */
|
|
649
|
+
"jsdoc/require-property": [];
|
|
650
|
+
|
|
651
|
+
/** Requires that each `@property` tag has a `description` value. */
|
|
652
|
+
"jsdoc/require-property-description": [];
|
|
653
|
+
|
|
654
|
+
/** Requires that all function `@property` tags have names. */
|
|
655
|
+
"jsdoc/require-property-name": [];
|
|
656
|
+
|
|
657
|
+
/** Requires that each `@property` tag has a `type` value. */
|
|
658
|
+
"jsdoc/require-property-type": [];
|
|
659
|
+
|
|
624
660
|
/** Requires that returns are documented. */
|
|
625
661
|
"jsdoc/require-returns":
|
|
626
662
|
| []
|
|
@@ -718,6 +754,9 @@ export interface Rules {
|
|
|
718
754
|
}
|
|
719
755
|
];
|
|
720
756
|
|
|
757
|
+
/** Requires a type for `@throws` tags */
|
|
758
|
+
"jsdoc/require-throws-type": [];
|
|
759
|
+
|
|
721
760
|
/** Requires yields are documented. */
|
|
722
761
|
"jsdoc/require-yields":
|
|
723
762
|
| []
|
|
@@ -757,6 +796,9 @@ export interface Rules {
|
|
|
757
796
|
}
|
|
758
797
|
];
|
|
759
798
|
|
|
799
|
+
/** Requires a type for `@yields` tags */
|
|
800
|
+
"jsdoc/require-yields-type": [];
|
|
801
|
+
|
|
760
802
|
/** Sorts tags by a specified sequence according to tag name. */
|
|
761
803
|
"jsdoc/sort-tags":
|
|
762
804
|
| []
|
package/package.json
CHANGED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import iterateJsdoc from './iterateJsdoc.js';
|
|
2
|
+
import {
|
|
3
|
+
parse,
|
|
4
|
+
stringify,
|
|
5
|
+
traverse,
|
|
6
|
+
tryParse,
|
|
7
|
+
} from '@es-joy/jsdoccomment';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Adjusts the parent type node `meta` for generic matches (or type node
|
|
11
|
+
* `type` for `JsdocTypeAny`) and sets the type node `value`.
|
|
12
|
+
* @param {string} type The actual type
|
|
13
|
+
* @param {string} preferred The preferred type
|
|
14
|
+
* @param {boolean} isGenericMatch
|
|
15
|
+
* @param {string} typeNodeName
|
|
16
|
+
* @param {import('jsdoc-type-pratt-parser').NonRootResult} node
|
|
17
|
+
* @param {import('jsdoc-type-pratt-parser').NonRootResult|undefined} parentNode
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
20
|
+
const adjustNames = (type, preferred, isGenericMatch, typeNodeName, node, parentNode) => {
|
|
21
|
+
let ret = preferred;
|
|
22
|
+
if (isGenericMatch) {
|
|
23
|
+
const parentMeta = /** @type {import('jsdoc-type-pratt-parser').GenericResult} */ (
|
|
24
|
+
parentNode
|
|
25
|
+
).meta;
|
|
26
|
+
if (preferred === '[]') {
|
|
27
|
+
parentMeta.brackets = 'square';
|
|
28
|
+
parentMeta.dot = false;
|
|
29
|
+
ret = 'Array';
|
|
30
|
+
} else {
|
|
31
|
+
const dotBracketEnd = preferred.match(/\.(?:<>)?$/v);
|
|
32
|
+
if (dotBracketEnd) {
|
|
33
|
+
parentMeta.brackets = 'angle';
|
|
34
|
+
parentMeta.dot = true;
|
|
35
|
+
ret = preferred.slice(0, -dotBracketEnd[0].length);
|
|
36
|
+
} else {
|
|
37
|
+
const bracketEnd = preferred.endsWith('<>');
|
|
38
|
+
if (bracketEnd) {
|
|
39
|
+
parentMeta.brackets = 'angle';
|
|
40
|
+
parentMeta.dot = false;
|
|
41
|
+
ret = preferred.slice(0, -2);
|
|
42
|
+
} else if (
|
|
43
|
+
parentMeta?.brackets === 'square' &&
|
|
44
|
+
(typeNodeName === '[]' || typeNodeName === 'Array')
|
|
45
|
+
) {
|
|
46
|
+
parentMeta.brackets = 'angle';
|
|
47
|
+
parentMeta.dot = false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} else if (type === 'JsdocTypeAny') {
|
|
52
|
+
node.type = 'JsdocTypeName';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** @type {import('jsdoc-type-pratt-parser').NameResult} */ (
|
|
56
|
+
node
|
|
57
|
+
).value = ret.replace(/(?:\.|<>|\.<>|\[\])$/v, '');
|
|
58
|
+
|
|
59
|
+
// For bare pseudo-types like `<>`
|
|
60
|
+
if (!ret) {
|
|
61
|
+
/** @type {import('jsdoc-type-pratt-parser').NameResult} */ (
|
|
62
|
+
node
|
|
63
|
+
).value = typeNodeName;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {boolean} [upperCase]
|
|
69
|
+
* @returns {string}
|
|
70
|
+
*/
|
|
71
|
+
const getMessage = (upperCase) => {
|
|
72
|
+
return 'Use object shorthand or index signatures instead of ' +
|
|
73
|
+
'`' + (upperCase ? 'O' : 'o') + 'bject`, e.g., `{[key: string]: string}`';
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @type {{
|
|
78
|
+
* message: string,
|
|
79
|
+
* replacement: false
|
|
80
|
+
* }}
|
|
81
|
+
*/
|
|
82
|
+
const info = {
|
|
83
|
+
message: getMessage(),
|
|
84
|
+
replacement: false,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @type {{
|
|
89
|
+
* message: string,
|
|
90
|
+
* replacement: false
|
|
91
|
+
* }}
|
|
92
|
+
*/
|
|
93
|
+
const infoUC = {
|
|
94
|
+
message: getMessage(true),
|
|
95
|
+
replacement: false,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {{
|
|
100
|
+
* checkNativeTypes?: import('./rules/checkTypes.js').CheckNativeTypes|null
|
|
101
|
+
* overrideSettings?: import('./iterateJsdoc.js').Settings['preferredTypes']|null,
|
|
102
|
+
* description?: string,
|
|
103
|
+
* schema?: import('eslint').Rule.RuleMetaData['schema'],
|
|
104
|
+
* typeName?: string,
|
|
105
|
+
* url?: string,
|
|
106
|
+
* }} cfg
|
|
107
|
+
* @returns {import('@eslint/core').RuleDefinition<
|
|
108
|
+
* import('@eslint/core').RuleDefinitionTypeOptions
|
|
109
|
+
* >}
|
|
110
|
+
*/
|
|
111
|
+
export const buildRejectOrPreferRuleDefinition = ({
|
|
112
|
+
checkNativeTypes = null,
|
|
113
|
+
typeName,
|
|
114
|
+
description = typeName ?? 'Reports invalid types.',
|
|
115
|
+
overrideSettings = null,
|
|
116
|
+
schema = [],
|
|
117
|
+
url = 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-types.md#repos-sticky-header',
|
|
118
|
+
}) => {
|
|
119
|
+
return iterateJsdoc(
|
|
120
|
+
({
|
|
121
|
+
context,
|
|
122
|
+
jsdocNode,
|
|
123
|
+
report,
|
|
124
|
+
settings,
|
|
125
|
+
sourceCode,
|
|
126
|
+
utils,
|
|
127
|
+
}) => {
|
|
128
|
+
const jsdocTagsWithPossibleType = utils.filterTags((tag) => {
|
|
129
|
+
return Boolean(utils.tagMightHaveTypePosition(tag.tag));
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const
|
|
133
|
+
/**
|
|
134
|
+
* @type {{
|
|
135
|
+
* preferredTypes: import('./iterateJsdoc.js').PreferredTypes,
|
|
136
|
+
* structuredTags: import('./iterateJsdoc.js').StructuredTags,
|
|
137
|
+
* mode: import('./jsdocUtils.js').ParserMode
|
|
138
|
+
* }}
|
|
139
|
+
*/
|
|
140
|
+
{
|
|
141
|
+
mode,
|
|
142
|
+
preferredTypes: preferredTypesOriginal,
|
|
143
|
+
structuredTags,
|
|
144
|
+
} = overrideSettings ? {
|
|
145
|
+
mode: settings.mode,
|
|
146
|
+
preferredTypes: overrideSettings,
|
|
147
|
+
structuredTags: {},
|
|
148
|
+
} : settings;
|
|
149
|
+
|
|
150
|
+
const injectObjectPreferredTypes = !('Object' in preferredTypesOriginal ||
|
|
151
|
+
'object' in preferredTypesOriginal ||
|
|
152
|
+
'object.<>' in preferredTypesOriginal ||
|
|
153
|
+
'Object.<>' in preferredTypesOriginal ||
|
|
154
|
+
'object<>' in preferredTypesOriginal);
|
|
155
|
+
|
|
156
|
+
/** @type {import('./iterateJsdoc.js').PreferredTypes} */
|
|
157
|
+
const typeToInject = mode === 'typescript' ?
|
|
158
|
+
{
|
|
159
|
+
Object: 'object',
|
|
160
|
+
'object.<>': info,
|
|
161
|
+
'Object.<>': infoUC,
|
|
162
|
+
'object<>': info,
|
|
163
|
+
'Object<>': infoUC,
|
|
164
|
+
} :
|
|
165
|
+
{
|
|
166
|
+
Object: 'object',
|
|
167
|
+
'object.<>': 'Object<>',
|
|
168
|
+
'Object.<>': 'Object<>',
|
|
169
|
+
'object<>': 'Object<>',
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/** @type {import('./iterateJsdoc.js').PreferredTypes} */
|
|
173
|
+
const preferredTypes = {
|
|
174
|
+
...injectObjectPreferredTypes ?
|
|
175
|
+
typeToInject :
|
|
176
|
+
{},
|
|
177
|
+
...preferredTypesOriginal,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const
|
|
181
|
+
/**
|
|
182
|
+
* @type {{
|
|
183
|
+
* noDefaults: boolean,
|
|
184
|
+
* unifyParentAndChildTypeChecks: boolean,
|
|
185
|
+
* exemptTagContexts: ({
|
|
186
|
+
* tag: string,
|
|
187
|
+
* types: true|string[]
|
|
188
|
+
* })[]
|
|
189
|
+
* }}
|
|
190
|
+
*/ {
|
|
191
|
+
exemptTagContexts = [],
|
|
192
|
+
noDefaults,
|
|
193
|
+
unifyParentAndChildTypeChecks,
|
|
194
|
+
} = context.options[0] || {};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Gets information about the preferred type: whether there is a matching
|
|
198
|
+
* preferred type, what the type is, and whether it is a match to a generic.
|
|
199
|
+
* @param {string} _type Not currently in use
|
|
200
|
+
* @param {string} typeNodeName
|
|
201
|
+
* @param {import('jsdoc-type-pratt-parser').NonRootResult|undefined} parentNode
|
|
202
|
+
* @param {string|undefined} property
|
|
203
|
+
* @returns {[hasMatchingPreferredType: boolean, typeName: string, isGenericMatch: boolean]}
|
|
204
|
+
*/
|
|
205
|
+
const getPreferredTypeInfo = (_type, typeNodeName, parentNode, property) => {
|
|
206
|
+
let hasMatchingPreferredType = false;
|
|
207
|
+
let isGenericMatch = false;
|
|
208
|
+
let typName = typeNodeName;
|
|
209
|
+
|
|
210
|
+
const isNameOfGeneric = parentNode !== undefined && parentNode.type === 'JsdocTypeGeneric' && property === 'left';
|
|
211
|
+
|
|
212
|
+
const brackets = /** @type {import('jsdoc-type-pratt-parser').GenericResult} */ (
|
|
213
|
+
parentNode
|
|
214
|
+
)?.meta?.brackets;
|
|
215
|
+
const dot = /** @type {import('jsdoc-type-pratt-parser').GenericResult} */ (
|
|
216
|
+
parentNode
|
|
217
|
+
)?.meta?.dot;
|
|
218
|
+
|
|
219
|
+
if (brackets === 'angle') {
|
|
220
|
+
const checkPostFixes = dot ? [
|
|
221
|
+
'.', '.<>',
|
|
222
|
+
] : [
|
|
223
|
+
'<>',
|
|
224
|
+
];
|
|
225
|
+
isGenericMatch = checkPostFixes.some((checkPostFix) => {
|
|
226
|
+
const preferredType = preferredTypes?.[typeNodeName + checkPostFix];
|
|
227
|
+
|
|
228
|
+
// Does `unifyParentAndChildTypeChecks` need to be checked here?
|
|
229
|
+
if (
|
|
230
|
+
(unifyParentAndChildTypeChecks || isNameOfGeneric ||
|
|
231
|
+
/* c8 ignore next 2 -- If checking `unifyParentAndChildTypeChecks` */
|
|
232
|
+
(typeof preferredType === 'object' &&
|
|
233
|
+
preferredType?.unifyParentAndChildTypeChecks)
|
|
234
|
+
) &&
|
|
235
|
+
preferredType !== undefined
|
|
236
|
+
) {
|
|
237
|
+
typName += checkPostFix;
|
|
238
|
+
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return false;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (
|
|
247
|
+
!isGenericMatch && property &&
|
|
248
|
+
/** @type {import('jsdoc-type-pratt-parser').NonRootResult} */ (
|
|
249
|
+
parentNode
|
|
250
|
+
).type === 'JsdocTypeGeneric'
|
|
251
|
+
) {
|
|
252
|
+
const checkPostFixes = dot ? [
|
|
253
|
+
'.', '.<>',
|
|
254
|
+
] : [
|
|
255
|
+
brackets === 'angle' ? '<>' : '[]',
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
isGenericMatch = checkPostFixes.some((checkPostFix) => {
|
|
259
|
+
const preferredType = preferredTypes?.[checkPostFix];
|
|
260
|
+
if (
|
|
261
|
+
// Does `unifyParentAndChildTypeChecks` need to be checked here?
|
|
262
|
+
(unifyParentAndChildTypeChecks || isNameOfGeneric ||
|
|
263
|
+
/* c8 ignore next 2 -- If checking `unifyParentAndChildTypeChecks` */
|
|
264
|
+
(typeof preferredType === 'object' &&
|
|
265
|
+
preferredType?.unifyParentAndChildTypeChecks)) &&
|
|
266
|
+
preferredType !== undefined
|
|
267
|
+
) {
|
|
268
|
+
typName = checkPostFix;
|
|
269
|
+
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return false;
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const prefType = preferredTypes?.[typeNodeName];
|
|
278
|
+
const directNameMatch = prefType !== undefined &&
|
|
279
|
+
!Object.values(preferredTypes).includes(typeNodeName);
|
|
280
|
+
const specificUnify = typeof prefType === 'object' &&
|
|
281
|
+
prefType?.unifyParentAndChildTypeChecks;
|
|
282
|
+
const unifiedSyntaxParentMatch = property && directNameMatch && (unifyParentAndChildTypeChecks || specificUnify);
|
|
283
|
+
isGenericMatch = isGenericMatch || Boolean(unifiedSyntaxParentMatch);
|
|
284
|
+
|
|
285
|
+
hasMatchingPreferredType = isGenericMatch ||
|
|
286
|
+
directNameMatch && !property;
|
|
287
|
+
|
|
288
|
+
return [
|
|
289
|
+
hasMatchingPreferredType, typName, isGenericMatch,
|
|
290
|
+
];
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Collect invalid type info.
|
|
295
|
+
* @param {string} type
|
|
296
|
+
* @param {string} value
|
|
297
|
+
* @param {string} tagName
|
|
298
|
+
* @param {string} nameInTag
|
|
299
|
+
* @param {number} idx
|
|
300
|
+
* @param {string|undefined} property
|
|
301
|
+
* @param {import('jsdoc-type-pratt-parser').NonRootResult} node
|
|
302
|
+
* @param {import('jsdoc-type-pratt-parser').NonRootResult|undefined} parentNode
|
|
303
|
+
* @param {(string|false|undefined)[][]} invalidTypes
|
|
304
|
+
* @returns {void}
|
|
305
|
+
*/
|
|
306
|
+
const getInvalidTypes = (type, value, tagName, nameInTag, idx, property, node, parentNode, invalidTypes) => {
|
|
307
|
+
let typeNodeName = type === 'JsdocTypeAny' ? '*' : value;
|
|
308
|
+
|
|
309
|
+
const [
|
|
310
|
+
hasMatchingPreferredType,
|
|
311
|
+
typName,
|
|
312
|
+
isGenericMatch,
|
|
313
|
+
] = getPreferredTypeInfo(type, typeNodeName, parentNode, property);
|
|
314
|
+
|
|
315
|
+
let preferred;
|
|
316
|
+
let types;
|
|
317
|
+
if (hasMatchingPreferredType) {
|
|
318
|
+
const preferredSetting = preferredTypes[typName];
|
|
319
|
+
typeNodeName = typName === '[]' ? typName : typeNodeName;
|
|
320
|
+
|
|
321
|
+
if (!preferredSetting) {
|
|
322
|
+
invalidTypes.push([
|
|
323
|
+
typeNodeName,
|
|
324
|
+
]);
|
|
325
|
+
} else if (typeof preferredSetting === 'string') {
|
|
326
|
+
preferred = preferredSetting;
|
|
327
|
+
invalidTypes.push([
|
|
328
|
+
typeNodeName, preferred,
|
|
329
|
+
]);
|
|
330
|
+
} else if (preferredSetting && typeof preferredSetting === 'object') {
|
|
331
|
+
const nextItem = preferredSetting.skipRootChecking && jsdocTagsWithPossibleType[idx + 1];
|
|
332
|
+
|
|
333
|
+
if (!nextItem || !nextItem.name.startsWith(`${nameInTag}.`)) {
|
|
334
|
+
preferred = preferredSetting.replacement;
|
|
335
|
+
invalidTypes.push([
|
|
336
|
+
typeNodeName,
|
|
337
|
+
preferred,
|
|
338
|
+
preferredSetting.message,
|
|
339
|
+
]);
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
utils.reportSettings(
|
|
343
|
+
'Invalid `settings.jsdoc.preferredTypes`. Values must be falsy, a string, or an object.',
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
} else if (Object.entries(structuredTags).some(([
|
|
349
|
+
tag,
|
|
350
|
+
{
|
|
351
|
+
type: typs,
|
|
352
|
+
},
|
|
353
|
+
]) => {
|
|
354
|
+
types = typs;
|
|
355
|
+
|
|
356
|
+
return tag === tagName &&
|
|
357
|
+
Array.isArray(types) &&
|
|
358
|
+
!types.includes(typeNodeName);
|
|
359
|
+
})) {
|
|
360
|
+
invalidTypes.push([
|
|
361
|
+
typeNodeName, types,
|
|
362
|
+
]);
|
|
363
|
+
} else if (checkNativeTypes && !noDefaults && type === 'JsdocTypeName') {
|
|
364
|
+
preferred = checkNativeTypes(
|
|
365
|
+
preferredTypes, typeNodeName, preferred, parentNode, invalidTypes,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// For fixer
|
|
370
|
+
if (preferred) {
|
|
371
|
+
adjustNames(type, preferred, isGenericMatch, typeNodeName, node, parentNode);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
for (const [
|
|
376
|
+
idx,
|
|
377
|
+
jsdocTag,
|
|
378
|
+
] of jsdocTagsWithPossibleType.entries()) {
|
|
379
|
+
/** @type {(string|false|undefined)[][]} */
|
|
380
|
+
const invalidTypes = [];
|
|
381
|
+
let typeAst;
|
|
382
|
+
|
|
383
|
+
try {
|
|
384
|
+
typeAst = mode === 'permissive' ? tryParse(jsdocTag.type) : parse(jsdocTag.type, mode);
|
|
385
|
+
} catch {
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const {
|
|
390
|
+
name: nameInTag,
|
|
391
|
+
tag: tagName,
|
|
392
|
+
} = jsdocTag;
|
|
393
|
+
|
|
394
|
+
traverse(typeAst, (node, parentNode, property) => {
|
|
395
|
+
const {
|
|
396
|
+
type,
|
|
397
|
+
value,
|
|
398
|
+
} =
|
|
399
|
+
/**
|
|
400
|
+
* @type {import('jsdoc-type-pratt-parser').NameResult}
|
|
401
|
+
*/ (node);
|
|
402
|
+
if (![
|
|
403
|
+
'JsdocTypeAny', 'JsdocTypeName',
|
|
404
|
+
].includes(type)) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
getInvalidTypes(type, value, tagName, nameInTag, idx, property, node, parentNode, invalidTypes);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
if (invalidTypes.length) {
|
|
412
|
+
const fixedType = stringify(typeAst);
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* @type {import('eslint').Rule.ReportFixer}
|
|
416
|
+
*/
|
|
417
|
+
const fix = (fixer) => {
|
|
418
|
+
return fixer.replaceText(
|
|
419
|
+
jsdocNode,
|
|
420
|
+
sourceCode.getText(jsdocNode).replace(
|
|
421
|
+
`{${jsdocTag.type}}`,
|
|
422
|
+
`{${fixedType}}`,
|
|
423
|
+
),
|
|
424
|
+
);
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
for (const [
|
|
428
|
+
badType,
|
|
429
|
+
preferredType = '',
|
|
430
|
+
msg,
|
|
431
|
+
] of invalidTypes) {
|
|
432
|
+
const tagValue = jsdocTag.name ? ` "${jsdocTag.name}"` : '';
|
|
433
|
+
if (exemptTagContexts.some(({
|
|
434
|
+
tag,
|
|
435
|
+
types,
|
|
436
|
+
}) => {
|
|
437
|
+
return tag === tagName &&
|
|
438
|
+
(types === true || types.includes(jsdocTag.type));
|
|
439
|
+
})) {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
report(
|
|
444
|
+
msg ||
|
|
445
|
+
`Invalid JSDoc @${tagName}${tagValue} type "${badType}"` +
|
|
446
|
+
(preferredType ? '; ' : '.') +
|
|
447
|
+
(preferredType ? `prefer: ${JSON.stringify(preferredType)}.` : ''),
|
|
448
|
+
preferredType ? fix : null,
|
|
449
|
+
jsdocTag,
|
|
450
|
+
msg ? {
|
|
451
|
+
tagName,
|
|
452
|
+
tagValue,
|
|
453
|
+
} : undefined,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
iterateAllJsdocs: true,
|
|
461
|
+
meta: {
|
|
462
|
+
docs: {
|
|
463
|
+
description,
|
|
464
|
+
url,
|
|
465
|
+
},
|
|
466
|
+
fixable: 'code',
|
|
467
|
+
schema,
|
|
468
|
+
type: 'suggestion',
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
);
|
|
472
|
+
};
|
package/src/index-cjs.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildForbidRuleDefinition,
|
|
3
3
|
} from './buildForbidRuleDefinition.js';
|
|
4
|
+
import {
|
|
5
|
+
buildRejectOrPreferRuleDefinition,
|
|
6
|
+
} from './buildRejectOrPreferRuleDefinition.js';
|
|
4
7
|
import {
|
|
5
8
|
getJsdocProcessorPlugin,
|
|
6
9
|
} from './getJsdocProcessorPlugin.js';
|
|
@@ -107,6 +110,33 @@ index.rules = {
|
|
|
107
110
|
'no-restricted-syntax': noRestrictedSyntax,
|
|
108
111
|
'no-types': noTypes,
|
|
109
112
|
'no-undefined-types': noUndefinedTypes,
|
|
113
|
+
'reject-any-type': buildRejectOrPreferRuleDefinition({
|
|
114
|
+
description: 'Reports use of `any` or `*` type',
|
|
115
|
+
overrideSettings: {
|
|
116
|
+
'*': {
|
|
117
|
+
message: 'Prefer a more specific type to `*`',
|
|
118
|
+
replacement: false,
|
|
119
|
+
unifyParentAndChildTypeChecks: true,
|
|
120
|
+
},
|
|
121
|
+
any: {
|
|
122
|
+
message: 'Prefer a more specific type to `any`',
|
|
123
|
+
replacement: false,
|
|
124
|
+
unifyParentAndChildTypeChecks: true,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/reject-any-type.md#repos-sticky-header',
|
|
128
|
+
}),
|
|
129
|
+
'reject-function-type': buildRejectOrPreferRuleDefinition({
|
|
130
|
+
description: 'Reports use of `Function` type',
|
|
131
|
+
overrideSettings: {
|
|
132
|
+
Function: {
|
|
133
|
+
message: 'Prefer a more specific type to `Function`',
|
|
134
|
+
replacement: false,
|
|
135
|
+
unifyParentAndChildTypeChecks: true,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/reject-function-type.md#repos-sticky-header',
|
|
139
|
+
}),
|
|
110
140
|
'require-asterisk-prefix': requireAsteriskPrefix,
|
|
111
141
|
'require-description': requireDescription,
|
|
112
142
|
'require-description-complete-sentence': requireDescriptionCompleteSentence,
|
|
@@ -122,7 +152,7 @@ index.rules = {
|
|
|
122
152
|
message: '@next should have a type',
|
|
123
153
|
},
|
|
124
154
|
],
|
|
125
|
-
description: 'Requires a type for
|
|
155
|
+
description: 'Requires a type for `@next` tags',
|
|
126
156
|
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-next-type.md#repos-sticky-header',
|
|
127
157
|
}),
|
|
128
158
|
'require-param': requireParam,
|
|
@@ -147,7 +177,7 @@ index.rules = {
|
|
|
147
177
|
message: '@throws should have a type',
|
|
148
178
|
},
|
|
149
179
|
],
|
|
150
|
-
description: 'Requires a type for
|
|
180
|
+
description: 'Requires a type for `@throws` tags',
|
|
151
181
|
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-throws-type.md#repos-sticky-header',
|
|
152
182
|
}),
|
|
153
183
|
'require-yields': requireYields,
|
|
@@ -160,7 +190,7 @@ index.rules = {
|
|
|
160
190
|
message: '@yields should have a type',
|
|
161
191
|
},
|
|
162
192
|
],
|
|
163
|
-
description: 'Requires a type for
|
|
193
|
+
description: 'Requires a type for `@yields` tags',
|
|
164
194
|
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-yields-type.md#repos-sticky-header',
|
|
165
195
|
}),
|
|
166
196
|
'sort-tags': sortTags,
|
|
@@ -218,6 +248,8 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
|
|
|
218
248
|
'jsdoc/no-restricted-syntax': 'off',
|
|
219
249
|
'jsdoc/no-types': 'off',
|
|
220
250
|
'jsdoc/no-undefined-types': warnOrError,
|
|
251
|
+
'jsdoc/reject-any-type': warnOrError,
|
|
252
|
+
'jsdoc/reject-function-type': warnOrError,
|
|
221
253
|
'jsdoc/require-asterisk-prefix': 'off',
|
|
222
254
|
'jsdoc/require-description': 'off',
|
|
223
255
|
'jsdoc/require-description-complete-sentence': 'off',
|