@@ -21,7 +21,19 @@ const strictNativeTypes = [
21
21
'RegExp' ,
22
22
] ;
23
23
24
- const adjustNames = ( type , preferred , isGenericMatch , nodeName , node , parentNode ) => {
24
+ /**
25
+ * Adjusts the parent type node `meta` for generic matches (or type node
26
+ * `type` for `JsdocTypeAny`) and sets the type node `value`.
27
+ *
28
+ * @param {string } type The actual type
29
+ * @param {string } preferred The preferred type
30
+ * @param {boolean } isGenericMatch
31
+ * @param {string } typeNodeName
32
+ * @param {import('jsdoc-type-pratt-parser/dist/src/index.d.ts').NonTerminalResult } node
33
+ * @param {import('jsdoc-type-pratt-parser/dist/src/index.d.ts').NonTerminalResult } parentNode
34
+ * @returns {void }
35
+ */
36
+ const adjustNames = ( type , preferred , isGenericMatch , typeNodeName , node , parentNode ) => {
25
37
let ret = preferred ;
26
38
if ( isGenericMatch ) {
27
39
if ( preferred === '[]' ) {
@@ -42,7 +54,7 @@ const adjustNames = (type, preferred, isGenericMatch, nodeName, node, parentNode
42
54
ret = preferred . slice ( 0 , - 2 ) ;
43
55
} else if (
44
56
parentNode . meta . brackets === 'square' &&
45
- ( nodeName === '[]' || nodeName === 'Array' )
57
+ ( typeNodeName === '[]' || typeNodeName === 'Array' )
46
58
) {
47
59
parentNode . meta . brackets = 'angle' ;
48
60
parentNode . meta . dot = false ;
@@ -57,7 +69,7 @@ const adjustNames = (type, preferred, isGenericMatch, nodeName, node, parentNode
57
69
58
70
// For bare pseudo-types like `<>`
59
71
if ( ! ret ) {
60
- node . value = nodeName ;
72
+ node . value = typeNodeName ;
61
73
}
62
74
} ;
63
75
@@ -84,10 +96,20 @@ export default iterateJsdoc(({
84
96
exemptTagContexts = [ ] ,
85
97
} = context . options [ 0 ] || { } ;
86
98
87
- const getPreferredTypeInfo = ( _type , nodeName , parentNode , property ) => {
88
- let hasMatchingPreferredType ;
89
- let isGenericMatch ;
90
- let typeName = nodeName ;
99
+ /**
100
+ * Gets information about the preferred type: whether there is a matching
101
+ * preferred type, what the type is, and whether it is a match to a generic.
102
+ *
103
+ * @param {string } _type Not currently in use
104
+ * @param {string } typeNodeName
105
+ * @param {import('jsdoc-type-pratt-parser/dist/src/index.d.ts').NonTerminalResult } parentNode
106
+ * @param {string } property
107
+ * @returns {[hasMatchingPreferredType: boolean, typeName: string, isGenericMatch: boolean] }
108
+ */
109
+ const getPreferredTypeInfo = ( _type , typeNodeName , parentNode , property ) => {
110
+ let hasMatchingPreferredType = false ;
111
+ let isGenericMatch = false ;
112
+ let typeName = typeNodeName ;
91
113
if ( Object . keys ( preferredTypes ) . length ) {
92
114
const isNameOfGeneric = parentNode !== undefined && parentNode . type === 'JsdocTypeGeneric' && property === 'left' ;
93
115
if ( unifyParentAndChildTypeChecks || isNameOfGeneric ) {
@@ -101,7 +123,7 @@ export default iterateJsdoc(({
101
123
'<>' ,
102
124
] ;
103
125
isGenericMatch = checkPostFixes . some ( ( checkPostFix ) => {
104
- if ( preferredTypes ?. [ nodeName + checkPostFix ] !== undefined ) {
126
+ if ( preferredTypes ?. [ typeNodeName + checkPostFix ] !== undefined ) {
105
127
typeName += checkPostFix ;
106
128
107
129
return true ;
@@ -130,8 +152,8 @@ export default iterateJsdoc(({
130
152
}
131
153
}
132
154
133
- const directNameMatch = preferredTypes ?. [ nodeName ] !== undefined &&
134
- ! Object . values ( preferredTypes ) . includes ( nodeName ) ;
155
+ const directNameMatch = preferredTypes ?. [ typeNodeName ] !== undefined &&
156
+ ! Object . values ( preferredTypes ) . includes ( typeNodeName ) ;
135
157
const unifiedSyntaxParentMatch = property && directNameMatch && unifyParentAndChildTypeChecks ;
136
158
isGenericMatch = isGenericMatch || unifiedSyntaxParentMatch ;
137
159
@@ -144,6 +166,128 @@ export default iterateJsdoc(({
144
166
] ;
145
167
} ;
146
168
169
+ /**
170
+ * @param {string } typeNodeName
171
+ * @param {string } preferred
172
+ * @param {import('jsdoc-type-pratt-parser/dist/src/index.d.ts').NonTerminalResult } parentNode
173
+ * @param {string[] } invalidTypes
174
+ * @returns {string } The `preferred` string, optionally changed
175
+ */
176
+ const check = ( typeNodeName , preferred , parentNode , invalidTypes ) => {
177
+ let changedPreferred = preferred ;
178
+ for ( const strictNativeType of strictNativeTypes ) {
179
+ if (
180
+ // Todo: Avoid typescript condition if moving to default typescript
181
+ strictNativeType === 'object' && mode === 'typescript' &&
182
+ (
183
+ // This is not set to remap with exact type match (e.g.,
184
+ // `object: 'Object'`), so can ignore (including if circular)
185
+ ! preferredTypes ?. [ typeNodeName ] ||
186
+ // Although present on `preferredTypes` for remapping, this is a
187
+ // parent object without a parent match (and not
188
+ // `unifyParentAndChildTypeChecks`) and we don't want
189
+ // `object<>` given TypeScript issue https://github.com/microsoft/TypeScript/issues/20555
190
+ parentNode ?. elements . length && (
191
+ parentNode ?. left . type === 'JsdocTypeName' &&
192
+ parentNode ?. left . value === 'Object'
193
+ )
194
+ )
195
+ ) {
196
+ continue ;
197
+ }
198
+
199
+ if ( strictNativeType !== typeNodeName &&
200
+ strictNativeType . toLowerCase ( ) === typeNodeName . toLowerCase ( ) &&
201
+
202
+ // Don't report if user has own map for a strict native type
203
+ ( ! preferredTypes || preferredTypes ?. [ strictNativeType ] === undefined )
204
+ ) {
205
+ changedPreferred = strictNativeType ;
206
+ invalidTypes . push ( [
207
+ typeNodeName , changedPreferred ,
208
+ ] ) ;
209
+ break ;
210
+ }
211
+ }
212
+
213
+ return changedPreferred ;
214
+ } ;
215
+
216
+ /**
217
+ * Collect invalid type info.
218
+ *
219
+ * @param {string } type
220
+ * @param {string } value
221
+ * @param {string } tagName
222
+ * @param {string } property
223
+ * @param {import('jsdoc-type-pratt-parser/dist/src/index.d.ts').NonTerminalResult } node
224
+ * @param {import('jsdoc-type-pratt-parser/dist/src/index.d.ts').NonTerminalResult } parentNode
225
+ * @param {string[] } invalidTypes
226
+ * @returns {void }
227
+ */
228
+ const getInvalidTypes = ( type , value , tagName , property , node , parentNode , invalidTypes ) => {
229
+ let typeNodeName = type === 'JsdocTypeAny' ? '*' : value ;
230
+
231
+ const [
232
+ hasMatchingPreferredType ,
233
+ typeName ,
234
+ isGenericMatch ,
235
+ ] = getPreferredTypeInfo ( type , typeNodeName , parentNode , property ) ;
236
+
237
+ let preferred ;
238
+ let types ;
239
+ if ( hasMatchingPreferredType ) {
240
+ const preferredSetting = preferredTypes [ typeName ] ;
241
+ typeNodeName = typeName === '[]' ? typeName : typeNodeName ;
242
+
243
+ if ( ! preferredSetting ) {
244
+ invalidTypes . push ( [
245
+ typeNodeName ,
246
+ ] ) ;
247
+ } else if ( typeof preferredSetting === 'string' ) {
248
+ preferred = preferredSetting ;
249
+ invalidTypes . push ( [
250
+ typeNodeName , preferred ,
251
+ ] ) ;
252
+ } else if ( typeof preferredSetting === 'object' ) {
253
+ preferred = preferredSetting ?. replacement ;
254
+ invalidTypes . push ( [
255
+ typeNodeName ,
256
+ preferred ,
257
+ preferredSetting ?. message ,
258
+ ] ) ;
259
+ } else {
260
+ utils . reportSettings (
261
+ 'Invalid `settings.jsdoc.preferredTypes`. Values must be falsy, a string, or an object.' ,
262
+ ) ;
263
+
264
+ return ;
265
+ }
266
+ } else if ( Object . entries ( structuredTags ) . some ( ( [
267
+ tag ,
268
+ {
269
+ type : typs ,
270
+ } ,
271
+ ] ) => {
272
+ types = typs ;
273
+
274
+ return tag === tagName &&
275
+ Array . isArray ( types ) &&
276
+ ! types . includes ( typeNodeName ) ;
277
+ } ) ) {
278
+ invalidTypes . push ( [
279
+ typeNodeName , types ,
280
+ ] ) ;
281
+ } else if ( ! noDefaults && type === 'JsdocTypeName' ) {
282
+ preferred = check ( typeNodeName , preferred , parentNode , invalidTypes ) ;
283
+ }
284
+
285
+ // For fixer
286
+ if ( preferred ) {
287
+ adjustNames ( type , preferred , isGenericMatch , typeNodeName , node , parentNode ) ;
288
+ }
289
+ } ;
290
+
147
291
for ( const jsdocTag of jsdocTagsWithPossibleType ) {
148
292
const invalidTypes = [ ] ;
149
293
let typeAst ;
@@ -156,7 +300,6 @@ export default iterateJsdoc(({
156
300
157
301
const tagName = jsdocTag . tag ;
158
302
159
- // eslint-disable-next-line complexity -- To refactor
160
303
traverse ( typeAst , ( node , parentNode , property ) => {
161
304
const {
162
305
type,
@@ -168,104 +311,16 @@ export default iterateJsdoc(({
168
311
return ;
169
312
}
170
313
171
- let nodeName = type === 'JsdocTypeAny' ? '*' : value ;
172
-
173
- const [
174
- hasMatchingPreferredType ,
175
- typeName ,
176
- isGenericMatch ,
177
- ] = getPreferredTypeInfo ( type , nodeName , parentNode , property ) ;
178
-
179
- let preferred ;
180
- let types ;
181
- if ( hasMatchingPreferredType ) {
182
- const preferredSetting = preferredTypes [ typeName ] ;
183
- nodeName = typeName === '[]' ? typeName : nodeName ;
184
-
185
- if ( ! preferredSetting ) {
186
- invalidTypes . push ( [
187
- nodeName ,
188
- ] ) ;
189
- } else if ( typeof preferredSetting === 'string' ) {
190
- preferred = preferredSetting ;
191
- invalidTypes . push ( [
192
- nodeName , preferred ,
193
- ] ) ;
194
- } else if ( typeof preferredSetting === 'object' ) {
195
- preferred = preferredSetting ?. replacement ;
196
- invalidTypes . push ( [
197
- nodeName ,
198
- preferred ,
199
- preferredSetting ?. message ,
200
- ] ) ;
201
- } else {
202
- utils . reportSettings (
203
- 'Invalid `settings.jsdoc.preferredTypes`. Values must be falsy, a string, or an object.' ,
204
- ) ;
205
-
206
- return ;
207
- }
208
- } else if ( Object . entries ( structuredTags ) . some ( ( [
209
- tag ,
210
- {
211
- type : typs ,
212
- } ,
213
- ] ) => {
214
- types = typs ;
215
-
216
- return tag === tagName &&
217
- Array . isArray ( types ) &&
218
- ! types . includes ( nodeName ) ;
219
- } ) ) {
220
- invalidTypes . push ( [
221
- nodeName , types ,
222
- ] ) ;
223
- } else if ( ! noDefaults && type === 'JsdocTypeName' ) {
224
- for ( const strictNativeType of strictNativeTypes ) {
225
- if (
226
- // Todo: Avoid typescript condition if moving to default typescript
227
- strictNativeType === 'object' && mode === 'typescript' &&
228
- (
229
- // This is not set to remap with exact type match (e.g.,
230
- // `object: 'Object'`), so can ignore (including if circular)
231
- ! preferredTypes ?. [ nodeName ] ||
232
- // Although present on `preferredTypes` for remapping, this is a
233
- // parent object without a parent match (and not
234
- // `unifyParentAndChildTypeChecks`) and we don't want
235
- // `object<>` given TypeScript issue https://github.com/microsoft/TypeScript/issues/20555
236
- parentNode ?. elements . length && (
237
- parentNode ?. left . type === 'JsdocTypeName' &&
238
- parentNode ?. left . value === 'Object'
239
- )
240
- )
241
- ) {
242
- continue ;
243
- }
244
-
245
- if ( strictNativeType !== nodeName &&
246
- strictNativeType . toLowerCase ( ) === nodeName . toLowerCase ( ) &&
247
-
248
- // Don't report if user has own map for a strict native type
249
- ( ! preferredTypes || preferredTypes ?. [ strictNativeType ] === undefined )
250
- ) {
251
- preferred = strictNativeType ;
252
- invalidTypes . push ( [
253
- nodeName , preferred ,
254
- ] ) ;
255
- break ;
256
- }
257
- }
258
- }
259
-
260
- // For fixer
261
- if ( preferred ) {
262
- adjustNames ( type , preferred , isGenericMatch , nodeName , node , parentNode ) ;
263
- }
314
+ getInvalidTypes ( type , value , tagName , property , node , parentNode , invalidTypes ) ;
264
315
} ) ;
265
316
266
317
if ( invalidTypes . length ) {
267
318
const fixedType = stringify ( typeAst ) ;
268
319
320
+ /**
321
+ * @param {any } fixer The ESLint fixer
322
+ * @returns {string }
323
+ */
269
324
const fix = ( fixer ) => {
270
325
return fixer . replaceText (
271
326
jsdocNode ,
0 commit comments