Skip to content

Commit ec254e8

Browse files
committed
fix: reuse existing type import declarations if they exist
1 parent 45382c5 commit ec254e8

File tree

1 file changed

+75
-21
lines changed

1 file changed

+75
-21
lines changed

src/rules/no-wildcard-imports.js

Lines changed: 75 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -355,11 +355,29 @@ module.exports = {
355355
*fix(fixer) {
356356
for (const [entrypoint, importSpecifiers] of changes) {
357357
const importDeclaration = node.parent.body.find(node => {
358-
return node.type === 'ImportDeclaration' && node.source.value === entrypoint
358+
return (
359+
node.type === 'ImportDeclaration' && node.source.value === entrypoint && node.importKind !== 'type'
360+
)
361+
})
362+
const typeImportDeclaration = node.parent.body.find(node => {
363+
return (
364+
node.type === 'ImportDeclaration' && node.source.value === entrypoint && node.importKind === 'type'
365+
)
359366
})
360367
const namedSpecifiers = importSpecifiers
361-
.filter(([imported]) => {
362-
return imported !== 'default'
368+
.filter(([imported, _local, type]) => {
369+
return imported !== 'default' && type !== 'type'
370+
})
371+
.map(([imported, local, type]) => {
372+
const prefix = type === 'type' ? 'type ' : ''
373+
if (imported !== local) {
374+
return `${prefix}${imported} as ${local}`
375+
}
376+
return `${prefix}${imported}`
377+
})
378+
const namedTypeSpecifiers = importSpecifiers
379+
.filter(([imported, _local, type]) => {
380+
return imported !== 'default' && type === 'type'
363381
})
364382
.map(([imported, local, type]) => {
365383
const prefix = type === 'type' ? 'type ' : ''
@@ -368,46 +386,82 @@ module.exports = {
368386
}
369387
return `${prefix}${imported}`
370388
})
371-
let defaultSpecifier = importSpecifiers.find(([imported]) => {
372-
return imported === 'default'
389+
let defaultSpecifier = importSpecifiers.find(([imported, _local, type]) => {
390+
return imported === 'default' && type !== 'type'
373391
})
374392
if (defaultSpecifier) {
375-
const prefix = defaultSpecifier[2] === 'type' ? 'type ' : ''
376-
defaultSpecifier = `${prefix}${defaultSpecifier[1]}`
393+
defaultSpecifier = defaultSpecifier[1]
394+
}
395+
let defaultTypeSpecifier = importSpecifiers.find(([imported, _local, type]) => {
396+
return imported === 'default' && type === 'type'
397+
})
398+
if (defaultTypeSpecifier) {
399+
defaultTypeSpecifier = `type ${defaultTypeSpecifier[1]}`
377400
}
378401

379-
const hasNamedSpecifiers = namedSpecifiers.length > 0
380-
const hasDefaultSpecifier = !!defaultSpecifier
381-
382-
if (importDeclaration) {
402+
if (typeImportDeclaration || importDeclaration) {
383403
yield fixer.remove(node)
404+
}
405+
406+
// Reuse a type import if it exists
407+
if (typeImportDeclaration) {
408+
const firstSpecifier = typeImportDeclaration.specifiers[0]
409+
const lastSpecifier = typeImportDeclaration.specifiers[importDeclaration.specifiers.length - 1]
384410

385-
if (importDeclaration.specifiers.length === 0) {
386-
throw new Error('Import Declaration has no specifiers')
411+
if (defaultTypeSpecifier) {
412+
const postfix =
413+
namedTypeSpecifiers.length > 0 || typeImportDeclaration.specifiers.length > 0 ? ', ' : ' '
414+
yield fix.insertTextBeforeRange(
415+
[firstSpecifier.range[0] - 1, firstSpecifier.range[1]],
416+
`${defaultTypeSpecifier}${postfix}`,
417+
)
387418
}
388419

420+
if (namedTypeSpecifiers.length > 0) {
421+
yield fixer.insertTextAfter(lastSpecifier, `, ${namedTypeSpecifiers.join(', ')}`)
422+
}
423+
}
424+
425+
// Reuse an import declaration if one exists
426+
if (importDeclaration) {
389427
const firstSpecifier = importDeclaration.specifiers[0]
390428
const lastSpecifier = importDeclaration.specifiers[importDeclaration.specifiers.length - 1]
391429

392-
if (hasDefaultSpecifier) {
393-
yield fixer.insertTextBefore(firstSpecifier, `${defaultSpecifier}, `)
430+
if (defaultSpecifier) {
431+
const postfix = namedSpecifiers.length > 0 || importDeclaration.specifiers.length > 0 ? ', ' : ' '
432+
yield fix.insertTextBeforeRange(
433+
[firstSpecifier.range[0] - 1, firstSpecifier.range[1]],
434+
`${defaultSpecifier}${postfix}`,
435+
)
394436
}
395437

396-
if (hasNamedSpecifiers) {
397-
yield fixer.insertTextAfter(lastSpecifier, `, ${namedSpecifiers.join(', ')}`)
438+
if (namedSpecifiers.length > 0 || (!typeImportDeclaration && namedTypeSpecifiers.length > 0)) {
439+
const specifiers = [...namedSpecifiers]
440+
if (!typeImportDeclaration) {
441+
specifiers.push(...namedTypeSpecifiers)
442+
}
443+
yield fixer.insertTextAfter(lastSpecifier, `, ${specifiers.join(', ')}`)
398444
}
399445
} else {
446+
const specifiers = [...namedSpecifiers]
447+
if (!typeImportDeclaration) {
448+
specifiers.push(...namedTypeSpecifiers)
449+
}
400450
let declaration = 'import '
401451

402-
if (hasDefaultSpecifier) {
452+
if (defaultSpecifier) {
403453
declaration += defaultSpecifier
404454
}
405455

406-
if (hasNamedSpecifiers) {
407-
if (hasDefaultSpecifier) {
456+
if (defaultTypeSpecifier && !typeImportDeclaration) {
457+
declaration += defaultTypeSpecifier
458+
}
459+
460+
if (specifiers.length > 0) {
461+
if (defaultSpecifier || defaultTypeSpecifier) {
408462
declaration += ', '
409463
}
410-
declaration += `{${namedSpecifiers.join(', ')}}`
464+
declaration += `{${specifiers.join(', ')}}`
411465
}
412466

413467
declaration += ` from '${entrypoint}'`

0 commit comments

Comments
 (0)