Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c816b84

Browse files
authored
fix(eslint-plugin): [consistent-type-imports] crash when using both default and namespace in one import (typescript-eslint#2778)
1 parent 05c9bed commit c816b84

File tree

2 files changed

+341
-153
lines changed

2 files changed

+341
-153
lines changed

packages/eslint-plugin/src/rules/consistent-type-imports.ts

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -252,23 +252,25 @@ export default util.createRule<Options, MessageIds>({
252252
? node.specifiers[0]
253253
: null;
254254
const namespaceSpecifier: TSESTree.ImportNamespaceSpecifier | null =
255-
node.specifiers[0].type === AST_NODE_TYPES.ImportNamespaceSpecifier
256-
? node.specifiers[0]
257-
: null;
255+
node.specifiers.find(
256+
(specifier): specifier is TSESTree.ImportNamespaceSpecifier =>
257+
specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier,
258+
) ?? null;
258259
const namedSpecifiers: TSESTree.ImportSpecifier[] = node.specifiers.filter(
259260
(specifier): specifier is TSESTree.ImportSpecifier =>
260261
specifier.type === AST_NODE_TYPES.ImportSpecifier,
261262
);
262263

263-
if (namespaceSpecifier) {
264+
if (namespaceSpecifier && !defaultSpecifier) {
264265
// e.g.
265266
// import * as types from 'foo'
266267
yield* fixToTypeImportByInsertType(fixer, node, false);
267268
return;
268269
} else if (defaultSpecifier) {
269270
if (
270271
report.typeSpecifiers.includes(defaultSpecifier) &&
271-
namedSpecifiers.length === 0
272+
namedSpecifiers.length === 0 &&
273+
!namespaceSpecifier
272274
) {
273275
// e.g.
274276
// import Type from 'foo'
@@ -279,7 +281,8 @@ export default util.createRule<Options, MessageIds>({
279281
if (
280282
namedSpecifiers.every(specifier =>
281283
report.typeSpecifiers.includes(specifier),
282-
)
284+
) &&
285+
!namespaceSpecifier
283286
) {
284287
// e.g.
285288
// import {Type1, Type2} from 'foo'
@@ -336,11 +339,40 @@ export default util.createRule<Options, MessageIds>({
336339
}
337340
}
338341

342+
const fixesRemoveTypeNamespaceSpecifier: TSESLint.RuleFix[] = [];
343+
if (
344+
namespaceSpecifier &&
345+
report.typeSpecifiers.includes(namespaceSpecifier)
346+
) {
347+
// e.g.
348+
// import Foo, * as Type from 'foo'
349+
// import DefType, * as Type from 'foo'
350+
// import DefType, * as Type from 'foo'
351+
const commaToken = util.nullThrows(
352+
sourceCode.getTokenBefore(namespaceSpecifier, util.isCommaToken),
353+
util.NullThrowsReasons.MissingToken(',', node.type),
354+
);
355+
356+
// import Def, * as Ns from 'foo'
357+
// ^^^^^^^^^ remove
358+
fixesRemoveTypeNamespaceSpecifier.push(
359+
fixer.removeRange([commaToken.range[0], namespaceSpecifier.range[1]]),
360+
);
361+
362+
// import type * as Ns from 'foo'
363+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ insert
364+
yield fixer.insertTextBefore(
365+
node,
366+
`import type ${sourceCode.getText(
367+
namespaceSpecifier,
368+
)} from ${sourceCode.getText(node.source)};\n`,
369+
);
370+
}
339371
if (
340372
defaultSpecifier &&
341373
report.typeSpecifiers.includes(defaultSpecifier)
342374
) {
343-
if (typeNamedSpecifiers.length === namedSpecifiers.length) {
375+
if (report.typeSpecifiers.length === node.specifiers.length) {
344376
const importToken = util.nullThrows(
345377
sourceCode.getFirstToken(node, isImportToken),
346378
util.NullThrowsReasons.MissingToken('import', node.type),
@@ -349,20 +381,36 @@ export default util.createRule<Options, MessageIds>({
349381
// ^^^^ insert
350382
yield fixer.insertTextAfter(importToken, ' type');
351383
} else {
384+
const commaToken = util.nullThrows(
385+
sourceCode.getTokenAfter(defaultSpecifier, util.isCommaToken),
386+
util.NullThrowsReasons.MissingToken(',', defaultSpecifier.type),
387+
);
388+
// import Type , {...} from 'foo'
389+
// ^^^^^ pick
390+
const defaultText = sourceCode.text
391+
.slice(defaultSpecifier.range[0], commaToken.range[0])
392+
.trim();
352393
yield fixer.insertTextBefore(
353394
node,
354-
`import type ${sourceCode.getText(
355-
defaultSpecifier,
356-
)} from ${sourceCode.getText(node.source)};\n`,
395+
`import type ${defaultText} from ${sourceCode.getText(
396+
node.source,
397+
)};\n`,
398+
);
399+
const afterToken = util.nullThrows(
400+
sourceCode.getTokenAfter(commaToken, { includeComments: true }),
401+
util.NullThrowsReasons.MissingToken('any token', node.type),
357402
);
358403
// import Type , {...} from 'foo'
359-
// ^^^^^^ remove
360-
yield fixer.remove(defaultSpecifier);
361-
yield fixer.remove(sourceCode.getTokenAfter(defaultSpecifier)!);
404+
// ^^^^^^^ remove
405+
yield fixer.removeRange([
406+
defaultSpecifier.range[0],
407+
afterToken.range[0],
408+
]);
362409
}
363410
}
364411

365412
yield* fixesNamedSpecifiers.removeTypeNamedSpecifiers;
413+
yield* fixesRemoveTypeNamespaceSpecifier;
366414

367415
yield* afterFixes;
368416

@@ -376,6 +424,12 @@ export default util.createRule<Options, MessageIds>({
376424
typeNamedSpecifiersText: string;
377425
removeTypeNamedSpecifiers: TSESLint.RuleFix[];
378426
} {
427+
if (allNamedSpecifiers.length === 0) {
428+
return {
429+
typeNamedSpecifiersText: '',
430+
removeTypeNamedSpecifiers: [],
431+
};
432+
}
379433
const typeNamedSpecifiersTexts: string[] = [];
380434
const removeTypeNamedSpecifiers: TSESLint.RuleFix[] = [];
381435
if (typeNamedSpecifiers.length === allNamedSpecifiers.length) {
@@ -564,7 +618,11 @@ export default util.createRule<Options, MessageIds>({
564618
),
565619
util.NullThrowsReasons.MissingToken('type', node.type),
566620
);
567-
return fixer.remove(typeToken);
621+
const afterToken = util.nullThrows(
622+
sourceCode.getTokenAfter(typeToken, { includeComments: true }),
623+
util.NullThrowsReasons.MissingToken('any token', node.type),
624+
);
625+
return fixer.removeRange([typeToken.range[0], afterToken.range[0]]);
568626
}
569627
},
570628
});

0 commit comments

Comments
 (0)