@@ -74,6 +74,7 @@ function hasProblematicComments(node, sourceCode) {
74
74
) ;
75
75
}
76
76
77
+ /** @type {(first: import('estree').ImportDeclaration, rest: import('estree').ImportDeclaration[], sourceCode: import('eslint').SourceCode.SourceCode, context: import('eslint').Rule.RuleContext) => import('eslint').Rule.ReportFixer | undefined } */
77
78
function getFix ( first , rest , sourceCode , context ) {
78
79
// Sorry ESLint <= 3 users, no autofix for you. Autofixing duplicate imports
79
80
// requires multiple `fixer.whatever()` calls in the `fix`: We both need to
@@ -123,7 +124,7 @@ function getFix(first, rest, sourceCode, context) {
123
124
isEmpty : ! hasSpecifiers ( node ) ,
124
125
} ;
125
126
} )
126
- . filter ( Boolean ) ;
127
+ . filter ( ( x ) => ! ! x ) ;
127
128
128
129
const unnecessaryImports = restWithoutComments . filter ( ( node ) => ! hasSpecifiers ( node )
129
130
&& ! hasNamespace ( node )
@@ -139,6 +140,7 @@ function getFix(first, rest, sourceCode, context) {
139
140
return undefined ;
140
141
}
141
142
143
+ /** @type {import('eslint').Rule.ReportFixer } */
142
144
return ( fixer ) => {
143
145
const tokens = sourceCode . getTokens ( first ) ;
144
146
const openBrace = tokens . find ( ( token ) => isPunctuator ( token , '{' ) ) ;
@@ -185,6 +187,7 @@ function getFix(first, rest, sourceCode, context) {
185
187
[ '' , ! firstHasTrailingComma && ! firstIsEmpty , firstExistingIdentifiers ] ,
186
188
) ;
187
189
190
+ /** @type {import('eslint').Rule.Fix[] } */
188
191
const fixes = [ ] ;
189
192
190
193
if ( shouldAddSpecifiers && preferInline && first . importKind === 'type' ) {
@@ -228,7 +231,7 @@ function getFix(first, rest, sourceCode, context) {
228
231
}
229
232
230
233
// Remove imports whose specifiers have been moved into the first import.
231
- for ( const specifier of specifiers ) {
234
+ specifiers . forEach ( ( specifier ) => {
232
235
const importNode = specifier . importNode ;
233
236
fixes . push ( fixer . remove ( importNode ) ) ;
234
237
@@ -237,25 +240,26 @@ function getFix(first, rest, sourceCode, context) {
237
240
if ( charAfterImport === '\n' ) {
238
241
fixes . push ( fixer . removeRange ( charAfterImportRange ) ) ;
239
242
}
240
- }
243
+ } ) ;
241
244
242
245
// Remove imports whose default import has been moved to the first import,
243
246
// and side-effect-only imports that are unnecessary due to the first
244
247
// import.
245
- for ( const node of unnecessaryImports ) {
248
+ unnecessaryImports . forEach ( ( node ) => {
246
249
fixes . push ( fixer . remove ( node ) ) ;
247
250
248
251
const charAfterImportRange = [ node . range [ 1 ] , node . range [ 1 ] + 1 ] ;
249
252
const charAfterImport = sourceCode . text . substring ( charAfterImportRange [ 0 ] , charAfterImportRange [ 1 ] ) ;
250
253
if ( charAfterImport === '\n' ) {
251
254
fixes . push ( fixer . removeRange ( charAfterImportRange ) ) ;
252
255
}
253
- }
256
+ } ) ;
254
257
255
258
return fixes ;
256
259
} ;
257
260
}
258
261
262
+ /** @type {(imported: Map<string, import('estree').ImportDeclaration[]>, context: import('eslint').Rule.RuleContext) => void } */
259
263
function checkImports ( imported , context ) {
260
264
for ( const [ module , nodes ] of imported . entries ( ) ) {
261
265
if ( nodes . length > 1 ) {
@@ -270,16 +274,17 @@ function checkImports(imported, context) {
270
274
fix, // Attach the autofix (if any) to the first import.
271
275
} ) ;
272
276
273
- for ( const node of rest ) {
277
+ rest . forEach ( ( node ) => {
274
278
context . report ( {
275
279
node : node . source ,
276
280
message,
277
281
} ) ;
278
- }
282
+ } ) ;
279
283
}
280
284
}
281
285
}
282
286
287
+ /** @type {import('eslint').Rule.RuleModule } */
283
288
module . exports = {
284
289
meta : {
285
290
type : 'problem' ,
@@ -305,10 +310,13 @@ module.exports = {
305
310
] ,
306
311
} ,
307
312
313
+ /** @param {import('eslint').Rule.RuleContext } context */
308
314
create ( context ) {
315
+ /** @type {boolean } */
309
316
// Prepare the resolver from options.
310
- const considerQueryStringOption = context . options [ 0 ]
311
- && context . options [ 0 ] . considerQueryString ;
317
+ const considerQueryStringOption = context . options [ 0 ] && context . options [ 0 ] . considerQueryString ;
318
+ /** @type {boolean } */
319
+ const preferInline = context . options [ 0 ] && context . options [ 0 ] [ 'prefer-inline' ] ;
312
320
const defaultResolver = ( sourcePath ) => resolve ( sourcePath , context ) || sourcePath ;
313
321
const resolver = considerQueryStringOption ? ( sourcePath ) => {
314
322
const parts = sourcePath . match ( / ^ ( [ ^ ? ] * ) \? ( .* ) $ / ) ;
@@ -318,19 +326,21 @@ module.exports = {
318
326
return `${ defaultResolver ( parts [ 1 ] ) } ?${ parts [ 2 ] } ` ;
319
327
} : defaultResolver ;
320
328
329
+ /** @type {Map<unknown, { imported: Map<string, import('estree').ImportDeclaration[]>, nsImported: Map<string, import('estree').ImportDeclaration[]>, defaultTypesImported: Map<string, import('estree').ImportDeclaration[]>, namedTypesImported: Map<string, import('estree').ImportDeclaration[]>}> } */
321
330
const moduleMaps = new Map ( ) ;
322
331
332
+ /** @param {import('estree').ImportDeclaration } n */
333
+ /** @returns {typeof moduleMaps[keyof typeof moduleMaps] } */
323
334
function getImportMap ( n ) {
324
335
if ( ! moduleMaps . has ( n . parent ) ) {
325
- moduleMaps . set ( n . parent , {
336
+ moduleMaps . set ( n . parent , /** @type { typeof moduleMaps } */ {
326
337
imported : new Map ( ) ,
327
338
nsImported : new Map ( ) ,
328
339
defaultTypesImported : new Map ( ) ,
329
340
namedTypesImported : new Map ( ) ,
330
341
} ) ;
331
342
}
332
343
const map = moduleMaps . get ( n . parent ) ;
333
- const preferInline = context . options [ 0 ] && context . options [ 0 ] [ 'prefer-inline' ] ;
334
344
if ( ! preferInline && n . importKind === 'type' ) {
335
345
return n . specifiers . length > 0 && n . specifiers [ 0 ] . type === 'ImportDefaultSpecifier' ? map . defaultTypesImported : map . namedTypesImported ;
336
346
}
@@ -342,7 +352,9 @@ module.exports = {
342
352
}
343
353
344
354
return {
355
+ /** @param {import('estree').ImportDeclaration } n */
345
356
ImportDeclaration ( n ) {
357
+ /** @type {string } */
346
358
// resolved path will cover aliased duplicates
347
359
const resolvedPath = resolver ( n . source . value ) ;
348
360
const importMap = getImportMap ( n ) ;
0 commit comments