eslint-plugin-jsdoc 57.2.1 → 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/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 +9 -3
- 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 +9 -3
|
@@ -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',
|
package/src/index-esm.js
CHANGED
|
@@ -8,6 +8,9 @@ import index from './index-cjs.js';
|
|
|
8
8
|
import {
|
|
9
9
|
buildForbidRuleDefinition,
|
|
10
10
|
} from './buildForbidRuleDefinition.js';
|
|
11
|
+
import {
|
|
12
|
+
buildRejectOrPreferRuleDefinition,
|
|
13
|
+
} from './buildRejectOrPreferRuleDefinition.js';
|
|
11
14
|
|
|
12
15
|
// eslint-disable-next-line unicorn/prefer-export-from --- Reusing `index`
|
|
13
16
|
export default index;
|
|
@@ -22,7 +25,7 @@ export default index;
|
|
|
22
25
|
* settings?: Partial<import('./iterateJsdoc.js').Settings>,
|
|
23
26
|
* rules?: {[key in keyof import('./rules.d.ts').Rules]?: import('eslint').Linter.RuleEntry<import('./rules.d.ts').Rules[key]>},
|
|
24
27
|
* extraRuleDefinitions?: {
|
|
25
|
-
* forbid
|
|
28
|
+
* forbid?: {
|
|
26
29
|
* [contextName: string]: {
|
|
27
30
|
* description?: string,
|
|
28
31
|
* url?: string,
|
|
@@ -32,6 +35,19 @@ export default index;
|
|
|
32
35
|
* comment: string
|
|
33
36
|
* })[]
|
|
34
37
|
* }
|
|
38
|
+
* },
|
|
39
|
+
* preferTypes?: {
|
|
40
|
+
* [typeName: string]: {
|
|
41
|
+
* description: string,
|
|
42
|
+
* overrideSettings: {
|
|
43
|
+
* [typeNodeName: string]: {
|
|
44
|
+
* message: string,
|
|
45
|
+
* replacement?: false|string,
|
|
46
|
+
* unifyParentAndChildTypeChecks?: boolean,
|
|
47
|
+
* }
|
|
48
|
+
* },
|
|
49
|
+
* url: string,
|
|
50
|
+
* }
|
|
35
51
|
* }
|
|
36
52
|
* }
|
|
37
53
|
* }
|
|
@@ -125,6 +141,25 @@ export const jsdoc = function (cfg) {
|
|
|
125
141
|
});
|
|
126
142
|
}
|
|
127
143
|
}
|
|
144
|
+
|
|
145
|
+
if (cfg.extraRuleDefinitions.preferTypes) {
|
|
146
|
+
for (const [
|
|
147
|
+
typeName,
|
|
148
|
+
{
|
|
149
|
+
description,
|
|
150
|
+
overrideSettings,
|
|
151
|
+
url,
|
|
152
|
+
},
|
|
153
|
+
] of Object.entries(cfg.extraRuleDefinitions.preferTypes)) {
|
|
154
|
+
outputConfig.plugins.jsdoc.rules[`prefer-type-${typeName}`] =
|
|
155
|
+
buildRejectOrPreferRuleDefinition({
|
|
156
|
+
description,
|
|
157
|
+
overrideSettings,
|
|
158
|
+
typeName,
|
|
159
|
+
url,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
128
163
|
}
|
|
129
164
|
}
|
|
130
165
|
|